fix: song timeline offset, lyrics scroll overlay offset

This commit is contained in:
Zhe Fang
2025-12-04 12:34:46 -05:00
parent 3e6ba725d2
commit 66c42f81f2
16 changed files with 371 additions and 267 deletions

View File

@@ -25,6 +25,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Vanara.PInvoke;
using WinUIEx;
namespace BetterLyrics.WinUI3
{
@@ -68,6 +69,10 @@ namespace BetterLyrics.WinUI3
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// 设置托盘
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
WindowHook.HideWindow<SystemTrayWindow>();
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
{
@@ -109,11 +114,13 @@ namespace BetterLyrics.WinUI3
.AddSingleton<LyricsWindowSettingsControlViewModel>()
.AddSingleton<LyricsWindowSwitchControlViewModel>()
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
.AddSingleton<NowPlayingWindowViewModel>()
.AddSingleton<NowPlayingPageViewModel>()
.AddSingleton<SettingsWindowViewModel>()
.AddSingleton<SystemTrayViewModel>()
.AddSingleton<SettingsPageViewModel>()
.AddSingleton<NowPlayingPageViewModel>()
.AddSingleton<MusicGalleryViewModel>()
.AddSingleton<AboutControlViewModel>()
.BuildServiceProvider()

View File

@@ -44,6 +44,7 @@
<None Remove="Views\MusicGalleryPage.xaml" />
<None Remove="Views\MusicGalleryWindow.xaml" />
<None Remove="Views\SettingsWindow.xaml" />
<None Remove="Views\SystemTrayWindow.xaml" />
</ItemGroup>
<ItemGroup>
<Content Include="Logo.ico" />
@@ -67,10 +68,10 @@
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
<PackageReference Include="csharp-kana" Version="1.0.2" />
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
<PackageReference Include="DevWinUI.Controls" Version="9.6.0" />
<PackageReference Include="DevWinUI.Controls" Version="9.7.0" />
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
<PackageReference Include="F23.StringSimilarity" Version="7.0.0" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
@@ -336,6 +337,11 @@
<ItemGroup>
<Folder Include="TemplateSelector\" />
</ItemGroup>
<ItemGroup>
<Page Update="Views\SystemTrayWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\WindowSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -19,6 +19,7 @@ using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Windows.Foundation;
@@ -96,6 +97,7 @@ namespace BetterLyrics.WinUI3.Controls
easingType: EasingType.EaseInOutSine
);
private TimeSpan _songPositionWithOffset;
private TimeSpan _songPosition; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>
private TimeSpan _totalPlayedTime; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ػ<EFBFBD><D8BB>ظ<EFBFBD><D8B8><EFBFBD><EFBFBD>ŵ<EFBFBD>ʱ<EFBFBD>
private bool _isLastFMTracked = false;
@@ -112,7 +114,7 @@ namespace BetterLyrics.WinUI3.Controls
private bool _isMousePressing = false;
private bool _isMouseScrolling = false;
private LyricsData? _lyricsData;
private List<RenderLyricsLine>? _renderLyricsLines = null;
private bool _isLayoutChanged = true;
private bool _isMouseScrollingChanged = false;
@@ -123,7 +125,7 @@ namespace BetterLyrics.WinUI3.Controls
public TimeSpan SongPosition => _songPosition;
public double CurrentCanvasYScroll => _canvasYScrollTransition.Value;
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines);
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines);
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> X <20><><EFBFBD><EFBFBD>
@@ -316,8 +318,6 @@ namespace BetterLyrics.WinUI3.Controls
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
double fixedSongPositionMs = _songPosition.TotalMilliseconds + (_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
var lyricsThemeColors = _mediaSessionsService.AlbumArtThemeColors;
Color overlayColor;
@@ -355,7 +355,7 @@ namespace BetterLyrics.WinUI3.Controls
_lyricsRenderer.Draw(
control: sender,
ds: args.DrawingSession,
lyricsData: _lyricsData,
lines: _renderLyricsLines,
playingLineIndex: _playingLineIndex,
mouseHoverLineIndex: _mouseHoverLineIndex,
isMousePressing: _isMousePressing,
@@ -374,15 +374,15 @@ namespace BetterLyrics.WinUI3.Controls
fgColor: lyricsThemeColors.FgFontColor,
getPlaybackState: (lineIndex) =>
{
if (_lyricsData == null) return new LinePlaybackState();
if (_renderLyricsLines == null) return new LinePlaybackState();
var line = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex);
var line = _renderLyricsLines.ElementAtOrDefault(lineIndex);
if (line == null) return new LinePlaybackState();
var nextLine = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex + 1);
var nextLine = _renderLyricsLines.ElementAtOrDefault(lineIndex + 1);
return _synchronizer.GetLinePlayingProgress(
fixedSongPositionMs,
_songPositionWithOffset.TotalMilliseconds,
line,
nextLine,
songDuration,
@@ -411,12 +411,12 @@ namespace BetterLyrics.WinUI3.Controls
args.DrawingSession.DrawText(
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines)}\n" +
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
$"Playing line (idx): {_playingLineIndex}\n" +
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines).End + 1}\n" +
$"Played: {TimeSpan.FromMilliseconds(fixedSongPositionMs)} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
$"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
$"Y offset: {_canvasYScrollTransition.Value}\n" +
$"User scroll offset: {_mouseYScrollTransition.Value}",
new Vector2(0, 0), Colors.Red);
@@ -430,6 +430,7 @@ namespace BetterLyrics.WinUI3.Controls
var lyricsStyle = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
var lyricsData = _mediaSessionsService.CurrentLyricsData;
TimeSpan elapsedTime = args.Timing.ElapsedTime;
@@ -447,7 +448,7 @@ namespace BetterLyrics.WinUI3.Controls
#region UpdatePlayingLineIndex
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPosition.TotalMilliseconds, _lyricsData);
int newPlayingIndex = _synchronizer.GetCurrentLineIndex(_songPositionWithOffset.TotalMilliseconds, lyricsData);
bool isPlayingLineChanged = newPlayingIndex != _playingLineIndex;
_playingLineIndex = newPlayingIndex;
@@ -457,7 +458,7 @@ namespace BetterLyrics.WinUI3.Controls
if (isPlayingLineChanged || _isLayoutChanged)
{
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_renderLyricsLines, _playingLineIndex);
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
@@ -471,7 +472,7 @@ namespace BetterLyrics.WinUI3.Controls
_mouseYScrollTransition.Update(elapsedTime);
_mouseHoverLineIndex = LyricsLayoutManager.FindMouseHoverLineIndex(
_lyricsData?.LyricsLines,
_renderLyricsLines,
_isMouseInLyricsArea,
_mousePosition,
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
@@ -481,7 +482,7 @@ namespace BetterLyrics.WinUI3.Controls
);
_visibleRange = LyricsLayoutManager.CalculateVisibleRange(
_lyricsData?.LyricsLines,
_renderLyricsLines,
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value, // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>
_renderLyricsStartY,
_renderLyricsHeight,
@@ -489,10 +490,10 @@ namespace BetterLyrics.WinUI3.Controls
lyricsStyle.PlayingLineTopOffset / 100.0
);
var maxRange = LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines);
var maxRange = LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines);
_animator.UpdateLines(
_lyricsData,
_renderLyricsLines,
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
_isMouseScrolling ? maxRange.End : _visibleRange.End,
_playingLineIndex,
@@ -591,11 +592,11 @@ namespace BetterLyrics.WinUI3.Controls
private void TriggerRelayout()
{
if (_lyricsData == null || !_isLayoutChanged) return;
if (_renderLyricsLines == null || !_isLayoutChanged) return;
LyricsLayoutManager.MeasureAndArrange(
resourceCreator: Canvas,
lyricsData: _lyricsData,
lines: _renderLyricsLines,
status: _liveStatesService.LiveStates.LyricsWindowStatus,
appSettings: _settingsService.AppSettings,
canvasWidth: Canvas.Size.Width,
@@ -611,6 +612,7 @@ namespace BetterLyrics.WinUI3.Controls
{
_songPosition += elapsedTime;
_totalPlayedTime += elapsedTime;
_songPositionWithOffset = _songPosition + TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
CheckAndScrobbleLastFM();
}
}
@@ -632,7 +634,7 @@ namespace BetterLyrics.WinUI3.Controls
private void ResetPlaybackState()
{
_totalPlayedTime = TimeSpan.Zero;
_songPosition = TimeSpan.Zero;
_totalPlayedTime = TimeSpan.Zero;
_isLastFMTracked = false;
}
@@ -694,7 +696,19 @@ namespace BetterLyrics.WinUI3.Controls
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentLyricsData))
{
_lyricsData = message.NewValue;
_renderLyricsLines = null;
if (_mediaSessionsService.CurrentLyricsData is LyricsData lyricsData)
{
_renderLyricsLines = lyricsData.LyricsLines.Select(x => new RenderLyricsLine()
{
LyricsSyllables = x.LyricsSyllables,
StartMs = x.StartMs,
EndMs = x.EndMs,
PhoneticText = x.PhoneticText,
OriginalText = x.OriginalText,
TranslatedText = x.TranslatedText
}).ToList();
}
_isLayoutChanged = true;
}
}

View File

@@ -11,6 +11,9 @@ namespace BetterLyrics.WinUI3.Extensions
public Point AddX(double deltaX) => new(point.X + deltaX, point.Y);
public Point AddY(double deltaY) => new(point.X, point.Y + deltaY);
public Point WithX(double x) => new(x, point.Y);
public Point WithY(double y) => new(point.X, y);
}
}
}

View File

@@ -100,6 +100,7 @@ namespace BetterLyrics.WinUI3.Hooks
public static void OpenOrShowWindow<T>()
{
var window = _activeWindows.Find(w => w is T);
//window = null;
if (window == null)
{
if (typeof(T) == typeof(NowPlayingWindow))
@@ -123,6 +124,10 @@ namespace BetterLyrics.WinUI3.Hooks
{
window = new LyricsWindowSwitchWindow();
}
else if (typeof(T) == typeof(SystemTrayWindow))
{
window = new SystemTrayWindow();
}
else
{
throw new ArgumentException("Unsupported window type", nameof(T));

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI;
@@ -13,7 +14,7 @@ namespace BetterLyrics.WinUI3.Logic
private readonly double _highlightedScale = 1.0f;
public void UpdateLines(
LyricsData? lyricsData,
IList<RenderLyricsLine>? lines,
int startIndex,
int endIndex,
int playingLineIndex,
@@ -31,14 +32,14 @@ namespace BetterLyrics.WinUI3.Logic
bool isMouseScrollingChanged
)
{
if (lyricsData == null) return;
if (lines == null) return;
var currentPlayingLine = lyricsData.LyricsLines.ElementAtOrDefault(playingLineIndex);
var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex);
if (currentPlayingLine == null) return;
for (int i = startIndex; i <= endIndex + 1; i++)
{
var line = lyricsData.LyricsLines.ElementAtOrDefault(i);
var line = lines.ElementAtOrDefault(i);
if (line == null) continue;
if (isLayoutChanged || isPlayingLineChanged || isMouseScrollingChanged)

View File

@@ -25,7 +25,7 @@ namespace BetterLyrics.WinUI3.Logic
/// <param name="lyricsHeight"></param>
public static void MeasureAndArrange(
ICanvasAnimatedControl resourceCreator,
LyricsData? lyricsData,
IList<RenderLyricsLine>? lines,
LyricsWindowStatus status,
AppSettings appSettings,
double canvasWidth,
@@ -33,7 +33,7 @@ namespace BetterLyrics.WinUI3.Logic
double lyricsWidth,
double lyricsHeight)
{
if (lyricsData == null || resourceCreator == null) return;
if (lines == null || resourceCreator == null) return;
// 计算字体大小
int originalFontSize, phoneticFontSize, translatedFontSize;
@@ -60,7 +60,7 @@ namespace BetterLyrics.WinUI3.Logic
double currentY = 0;
double actualWidth = 0;
foreach (var line in lyricsData.LyricsLines)
foreach (var line in lines)
{
if (line == null) continue;
@@ -130,10 +130,9 @@ namespace BetterLyrics.WinUI3.Logic
/// 计算为了让当前歌词行的竖直几何中心点对齐到 0原点画布应该移动的距离从画布最初始状态计算的值
/// </summary>
public static double? CalculateTargetScrollOffset(
LyricsData? lyricsData,
IList<RenderLyricsLine>? lines,
int playingLineIndex)
{
var lines = lyricsData?.LyricsLines;
if (lines == null || lines.Count == 0) return null;
var currentLine = lines.ElementAtOrDefault(playingLineIndex);
@@ -150,7 +149,7 @@ namespace BetterLyrics.WinUI3.Logic
/// 返回值: (StartVisibleIndex, EndVisibleIndex)
/// </summary>
public static (int Start, int End) CalculateVisibleRange(
IList<LyricsLine>? lines,
IList<RenderLyricsLine>? lines,
double currentScrollOffset,
double lyricsY,
double lyricsHeight,
@@ -174,14 +173,14 @@ namespace BetterLyrics.WinUI3.Logic
return (start, end);
}
public static (int Start, int End) CalculateMaxRange(IList<LyricsLine>? lines)
public static (int Start, int End) CalculateMaxRange(IList<RenderLyricsLine>? lines)
{
if (lines == null || lines.Count == 0) return (-1, -1);
return (0, lines.Count - 1);
}
public static double CalculateActualHeight(IList<LyricsLine>? lines)
public static double CalculateActualHeight(IList<RenderLyricsLine>? lines)
{
if (lines == null || lines.Count == 0) return 0;
@@ -189,7 +188,7 @@ namespace BetterLyrics.WinUI3.Logic
}
public static int FindMouseHoverLineIndex(
IList<LyricsLine>? lines,
IList<RenderLyricsLine>? lines,
bool isMouseInLyricsArea,
Point mousePosition,
double currentScrollOffset,
@@ -217,7 +216,7 @@ namespace BetterLyrics.WinUI3.Logic
return result;
}
private static int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
private static int FindFirstVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY)
{
int left = 0, right = lines.Count - 1, result = -1;
while (left <= right)
@@ -234,7 +233,7 @@ namespace BetterLyrics.WinUI3.Logic
return result;
}
private static int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
private static int FindLastVisibleLine(IList<RenderLyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
{
int left = 0, right = lines.Count - 1, result = -1;
while (left <= right)

View File

@@ -1,6 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using DevWinUI;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -175,5 +176,6 @@ namespace BetterLyrics.WinUI3.Models
}
return null;
}
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Numerics;
using Windows.UI;
@@ -15,47 +16,6 @@ namespace BetterLyrics.WinUI3.Models
{
public class LyricsLine
{
public double AnimationDuration { get; set; } = 0.3;
public ValueTransition<double> AngleTransition { get; set; }
public ValueTransition<double> BlurAmountTransition { get; set; }
public ValueTransition<double> PhoneticOpacityTransition { get; set; }
public ValueTransition<double> PlayedOriginalOpacityTransition { get; set; }
public ValueTransition<double> UnplayedOriginalOpacityTransition { get; set; }
public ValueTransition<double> TranslatedOpacityTransition { get; set; }
public ValueTransition<double> ScaleTransition { get; set; }
public ValueTransition<double> YOffsetTransition { get; set; }
public ValueTransition<Color> ColorTransition { get; set; }
public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; }
public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; }
public CanvasTextLayout? PhoneticCanvasTextLayout { get; private set; }
/// <summary>
/// 原文坐标(相对于坐标原点)
/// </summary>
public Vector2 OriginalPosition { get; set; }
/// <summary>
/// 译文坐标(相对于坐标原点)
/// </summary>
public Vector2 TranslatedPosition { get; set; }
/// <summary>
/// 注音坐标(相对于坐标原点)
/// </summary>
public Vector2 PhoneticPosition { get; set; }
/// <summary>
/// 顶部坐标(相对于坐标原点)
/// </summary>
public Vector2 TopLeftPosition { get; set; }
/// <summary>
/// 中心坐标(相对于坐标原点)
/// </summary>
public Vector2 CenterPosition { get; private set; }
/// <summary>
/// 底部坐标(相对于坐标原点)
/// </summary>
public Vector2 BottomRightPosition { get; set; }
public List<LyricsSyllable> LyricsSyllables { get; set; } = [];
public int? DurationMs => EndMs - StartMs;
@@ -75,172 +35,5 @@ namespace BetterLyrics.WinUI3.Models
/// </summary>
public string PhoneticText { get; set; } = "";
public CanvasGeometry? OriginalCanvasGeometry { get; private set; }
public CanvasGeometry? TranslatedCanvasGeometry { get; private set; }
public CanvasGeometry? PhoneticCanvasGeometry { get; private set; }
public LyricsLine()
{
AngleTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
BlurAmountTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
PhoneticOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
PlayedOriginalOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
UnplayedOriginalOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
TranslatedOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
ScaleTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
YOffsetTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
ColorTransition = new(
initialValue: Colors.Transparent,
durationSeconds: 0.3f,
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
);
}
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
{
if (OriginalCanvasTextLayout == null)
{
return;
}
double centerY = (TopLeftPosition.Y + BottomRightPosition.Y) / 2;
CenterPosition = type switch
{
TextAlignmentType.Left => new Vector2(0, (float)centerY),
TextAlignmentType.Center => new Vector2((float)(0 + maxWidth / 2.0), (float)centerY),
TextAlignmentType.Right => new Vector2((float)(0 + maxWidth), (float)centerY),
_ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null),
};
}
public void DisposeTextLayout()
{
PhoneticCanvasTextLayout?.Dispose();
PhoneticCanvasTextLayout = null;
OriginalCanvasTextLayout?.Dispose();
OriginalCanvasTextLayout = null;
TranslatedCanvasTextLayout?.Dispose();
TranslatedCanvasTextLayout = null;
}
public void RecreateTextLayout(
ICanvasAnimatedControl control,
bool createPhonetic, bool createTranslated,
int phoneticTextFontSize, int originalTextFontSize, int translatedTextFontSize,
LyricsFontWeight fontWeight,
string fontFamilyCJK, string fontFamilyWestern,
double maxWidth, double maxHeight, TextAlignmentType type)
{
DisposeTextLayout();
if (createPhonetic && PhoneticText != "")
{
PhoneticCanvasTextLayout = new CanvasTextLayout(control, PhoneticText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = phoneticTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment(),
};
PhoneticCanvasTextLayout.SetFontFamily(PhoneticText, fontFamilyCJK, fontFamilyWestern);
}
OriginalCanvasTextLayout = new CanvasTextLayout(control, OriginalText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = originalTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
};
OriginalCanvasTextLayout.SetFontFamily(OriginalText, fontFamilyCJK, fontFamilyWestern);
if (createTranslated && TranslatedText != "")
{
TranslatedCanvasTextLayout = new CanvasTextLayout(control, TranslatedText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = translatedTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
};
TranslatedCanvasTextLayout.SetFontFamily(TranslatedText, fontFamilyCJK, fontFamilyWestern);
}
}
public void DisposeTextGeometry()
{
PhoneticCanvasGeometry?.Dispose();
PhoneticCanvasGeometry = null;
OriginalCanvasGeometry?.Dispose();
OriginalCanvasGeometry = null;
TranslatedCanvasGeometry?.Dispose();
TranslatedCanvasGeometry = null;
}
public void RecreateTextGeometry()
{
DisposeTextGeometry();
if (PhoneticCanvasTextLayout != null)
{
PhoneticCanvasGeometry = CanvasGeometry.CreateText(PhoneticCanvasTextLayout);
}
if (OriginalCanvasTextLayout != null)
{
OriginalCanvasGeometry = CanvasGeometry.CreateText(OriginalCanvasTextLayout);
}
if (TranslatedCanvasTextLayout != null)
{
TranslatedCanvasGeometry = CanvasGeometry.CreateText(TranslatedCanvasTextLayout);
}
}
}
}

View File

@@ -0,0 +1,228 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using Windows.UI;
namespace BetterLyrics.WinUI3.Models
{
public class RenderLyricsLine : LyricsLine
{
public double AnimationDuration { get; set; } = 0.3;
public ValueTransition<double> AngleTransition { get; set; }
public ValueTransition<double> BlurAmountTransition { get; set; }
public ValueTransition<double> PhoneticOpacityTransition { get; set; }
public ValueTransition<double> PlayedOriginalOpacityTransition { get; set; }
public ValueTransition<double> UnplayedOriginalOpacityTransition { get; set; }
public ValueTransition<double> TranslatedOpacityTransition { get; set; }
public ValueTransition<double> ScaleTransition { get; set; }
public ValueTransition<double> YOffsetTransition { get; set; }
public ValueTransition<Color> ColorTransition { get; set; }
public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; }
public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; }
public CanvasTextLayout? PhoneticCanvasTextLayout { get; private set; }
/// <summary>
/// 原文坐标(相对于坐标原点)
/// </summary>
public Vector2 OriginalPosition { get; set; }
/// <summary>
/// 译文坐标(相对于坐标原点)
/// </summary>
public Vector2 TranslatedPosition { get; set; }
/// <summary>
/// 注音坐标(相对于坐标原点)
/// </summary>
public Vector2 PhoneticPosition { get; set; }
/// <summary>
/// 顶部坐标(相对于坐标原点)
/// </summary>
public Vector2 TopLeftPosition { get; set; }
/// <summary>
/// 中心坐标(相对于坐标原点)
/// </summary>
public Vector2 CenterPosition { get; private set; }
/// <summary>
/// 底部坐标(相对于坐标原点)
/// </summary>
public Vector2 BottomRightPosition { get; set; }
public CanvasGeometry? OriginalCanvasGeometry { get; private set; }
public CanvasGeometry? TranslatedCanvasGeometry { get; private set; }
public CanvasGeometry? PhoneticCanvasGeometry { get; private set; }
public RenderLyricsLine()
{
AngleTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
BlurAmountTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
PhoneticOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
PlayedOriginalOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
UnplayedOriginalOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
TranslatedOpacityTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
ScaleTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
YOffsetTransition = new(
initialValue: 0,
durationSeconds: AnimationDuration,
easingType: EasingType.EaseInOutSine
);
ColorTransition = new(
initialValue: Colors.Transparent,
durationSeconds: 0.3f,
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
);
}
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
{
if (OriginalCanvasTextLayout == null)
{
return;
}
double centerY = (TopLeftPosition.Y + BottomRightPosition.Y) / 2;
CenterPosition = type switch
{
TextAlignmentType.Left => new Vector2(0, (float)centerY),
TextAlignmentType.Center => new Vector2((float)(0 + maxWidth / 2.0), (float)centerY),
TextAlignmentType.Right => new Vector2((float)(0 + maxWidth), (float)centerY),
_ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null),
};
}
public void DisposeTextLayout()
{
PhoneticCanvasTextLayout?.Dispose();
PhoneticCanvasTextLayout = null;
OriginalCanvasTextLayout?.Dispose();
OriginalCanvasTextLayout = null;
TranslatedCanvasTextLayout?.Dispose();
TranslatedCanvasTextLayout = null;
}
public void RecreateTextLayout(
ICanvasAnimatedControl control,
bool createPhonetic, bool createTranslated,
int phoneticTextFontSize, int originalTextFontSize, int translatedTextFontSize,
LyricsFontWeight fontWeight,
string fontFamilyCJK, string fontFamilyWestern,
double maxWidth, double maxHeight, TextAlignmentType type)
{
DisposeTextLayout();
if (createPhonetic && PhoneticText != "")
{
PhoneticCanvasTextLayout = new CanvasTextLayout(control, PhoneticText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = phoneticTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment(),
};
PhoneticCanvasTextLayout.SetFontFamily(PhoneticText, fontFamilyCJK, fontFamilyWestern);
}
OriginalCanvasTextLayout = new CanvasTextLayout(control, OriginalText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = originalTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
};
OriginalCanvasTextLayout.SetFontFamily(OriginalText, fontFamilyCJK, fontFamilyWestern);
if (createTranslated && TranslatedText != "")
{
TranslatedCanvasTextLayout = new CanvasTextLayout(control, TranslatedText, new CanvasTextFormat
{
HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = translatedTextFontSize,
FontWeight = fontWeight.ToFontWeight(),
}, (float)maxWidth, (float)maxHeight)
{
HorizontalAlignment = type.ToCanvasHorizontalAlignment()
};
TranslatedCanvasTextLayout.SetFontFamily(TranslatedText, fontFamilyCJK, fontFamilyWestern);
}
}
public void DisposeTextGeometry()
{
PhoneticCanvasGeometry?.Dispose();
PhoneticCanvasGeometry = null;
OriginalCanvasGeometry?.Dispose();
OriginalCanvasGeometry = null;
TranslatedCanvasGeometry?.Dispose();
TranslatedCanvasGeometry = null;
}
public void RecreateTextGeometry()
{
DisposeTextGeometry();
if (PhoneticCanvasTextLayout != null)
{
PhoneticCanvasGeometry = CanvasGeometry.CreateText(PhoneticCanvasTextLayout);
}
if (OriginalCanvasTextLayout != null)
{
OriginalCanvasGeometry = CanvasGeometry.CreateText(OriginalCanvasTextLayout);
}
if (TranslatedCanvasTextLayout != null)
{
TranslatedCanvasGeometry = CanvasGeometry.CreateText(TranslatedCanvasTextLayout);
}
}
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Windows.UI;
@@ -23,7 +24,7 @@ namespace BetterLyrics.WinUI3.Renderer
public void Draw(
ICanvasAnimatedControl control,
CanvasDrawingSession ds,
LyricsData? lyricsData,
IList<RenderLyricsLine>? lines,
int playingLineIndex,
int mouseHoverLineIndex,
bool isMousePressing,
@@ -53,7 +54,7 @@ namespace BetterLyrics.WinUI3.Renderer
DrawLyrics(
control,
layerDs,
lyricsData,
lines,
playingLineIndex,
mouseHoverLineIndex,
isMousePressing,
@@ -84,7 +85,7 @@ namespace BetterLyrics.WinUI3.Renderer
DrawLyrics(
control,
ds,
lyricsData,
lines,
playingLineIndex,
mouseHoverLineIndex,
isMousePressing,
@@ -108,7 +109,7 @@ namespace BetterLyrics.WinUI3.Renderer
private void DrawLyrics(
ICanvasAnimatedControl control,
CanvasDrawingSession ds,
LyricsData? lyricsData,
IList<RenderLyricsLine>? lines,
int playingLineIndex,
int mouseHoverLineIndex,
bool isMousePressing,
@@ -126,9 +127,9 @@ namespace BetterLyrics.WinUI3.Renderer
Color fgColor,
Func<int, LinePlaybackState> getPlaybackState)
{
if (lyricsData == null) return;
if (lines == null) return;
var currentPlayingLine = lyricsData.LyricsLines.ElementAtOrDefault(playingLineIndex);
var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex);
if (currentPlayingLine == null) return;
var effectSettings = windowStatus.LyricsEffectSettings;
@@ -138,7 +139,7 @@ namespace BetterLyrics.WinUI3.Renderer
for (int i = startVisibleIndex; i <= endVisibleIndex; i++)
{
var line = lyricsData.LyricsLines.ElementAtOrDefault(i);
var line = lines.ElementAtOrDefault(i);
if (line == null) continue;
if (line.OriginalCanvasTextLayout == null) continue;
@@ -171,7 +172,7 @@ namespace BetterLyrics.WinUI3.Renderer
byte opacity = isMousePressing ? (byte)32 : (byte)16;
double scale = isMousePressing ? 1.09 : 1.10;
ds.FillRoundedRectangle(
new Windows.Foundation.Rect(line.TopLeftPosition.ToPoint(), line.BottomRightPosition.ToPoint()).Scale(scale),
new Windows.Foundation.Rect(line.TopLeftPosition.ToPoint().WithX(0), line.BottomRightPosition.ToPoint().WithX(lyricsWidth)).Scale(scale),
8, 8, Color.FromArgb(opacity, 255, 255, 255));
}
}
@@ -182,7 +183,7 @@ namespace BetterLyrics.WinUI3.Renderer
private CanvasCommandList RenderBaseTextLayer(
ICanvasResourceCreator resourceCreator,
LyricsLine line,
RenderLyricsLine line,
double strokeWidth,
Color strokeColor,
Color fillColor)

View File

@@ -20,7 +20,7 @@ namespace BetterLyrics.WinUI3.Renderer
ICanvasAnimatedControl control,
CanvasDrawingSession ds,
ICanvasImage textOnlyLayer,
LyricsLine line,
RenderLyricsLine line,
LinePlaybackState playbackState,
Color bgColor,
Color fgColor,
@@ -31,7 +31,7 @@ namespace BetterLyrics.WinUI3.Renderer
DrawTranslated(ds, textOnlyLayer, line);
}
private void DrawPhonetic(CanvasDrawingSession ds, ICanvasImage source, LyricsLine line)
private void DrawPhonetic(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
{
if (line.PhoneticCanvasTextLayout == null) return;
@@ -63,7 +63,7 @@ namespace BetterLyrics.WinUI3.Renderer
});
}
private void DrawTranslated(CanvasDrawingSession ds, ICanvasImage source, LyricsLine line)
private void DrawTranslated(CanvasDrawingSession ds, ICanvasImage source, RenderLyricsLine line)
{
if (line.TranslatedCanvasTextLayout == null) return;
@@ -99,7 +99,7 @@ namespace BetterLyrics.WinUI3.Renderer
ICanvasResourceCreator resourceCreator,
CanvasDrawingSession ds,
ICanvasImage source,
LyricsLine line,
RenderLyricsLine line,
LinePlaybackState state,
Color bgColor,
Color fgColor,
@@ -122,7 +122,7 @@ namespace BetterLyrics.WinUI3.Renderer
ICanvasResourceCreator resourceCreator,
CanvasDrawingSession ds,
ICanvasImage source,
LyricsLine line,
RenderLyricsLine line,
CanvasTextLayoutRegion subLineRegion,
double curCharIndex,
float fadeWidth,
@@ -193,7 +193,7 @@ namespace BetterLyrics.WinUI3.Renderer
private void DrawSingleCharacter(
CanvasDrawingSession ds,
LyricsLine line,
RenderLyricsLine line,
int charIndex,
double exactProgressIndex,
ICanvasImage source,

View File

@@ -12,7 +12,7 @@ namespace BetterLyrics.WinUI3.Renderer
public void Draw(
CanvasDrawingSession ds,
ICanvasImage textOnlyLayer,
LyricsLine line)
RenderLyricsLine line)
{
var blurAmount = (float)line.BlurAmountTransition.Value;

View File

@@ -18,6 +18,7 @@
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
xmlns:ui="using:CommunityToolkit.WinUI"
Loaded="Page_Loaded"
NavigationCacheMode="Disabled"
Unloaded="Page_Unloaded"
mc:Ignorable="d">
@@ -535,7 +536,5 @@
</Grid.ContextFlyout>
</Grid>
<uc:SystemTray />
</Grid>
</Page>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="BetterLyrics.WinUI3.Views.SystemTrayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
Title="SystemTrayWindow"
mc:Ignorable="d">
<Grid>
<uc:SystemTray />
</Grid>
</Window>

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Views;
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SystemTrayWindow : Window
{
public SystemTrayWindow()
{
InitializeComponent();
}
}