feat: add settings for changing playing line top offset; fix: scroll bar visibility for lyrics placeholder

This commit is contained in:
Zhe Fang
2025-12-03 11:44:50 -05:00
parent 5faace562d
commit 4d3b982904
12 changed files with 104 additions and 47 deletions

View File

@@ -52,7 +52,6 @@ namespace BetterLyrics.WinUI3.Controls
private readonly SpectrumRenderer _spectrumRenderer = new();
private readonly LyricsSynchronizer _synchronizer = new();
private readonly LyricsLayoutManager _layoutManager = new();
private readonly LyricsAnimator _animator = new();
private readonly SpectrumAnalyzer _spectrumAnalyzer = new();
@@ -124,7 +123,7 @@ namespace BetterLyrics.WinUI3.Controls
public TimeSpan SongPosition => _songPosition;
public double CurrentCanvasYScroll => _canvasYScrollTransition.Value;
public double ActualLyricsHeight => _layoutManager.CalculateActualHeight(_lyricsData?.LyricsLines);
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines);
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> X <20><><EFBFBD><EFBFBD>
@@ -368,6 +367,7 @@ namespace BetterLyrics.WinUI3.Controls
lyricsHeight: _renderLyricsHeight,
userScrollOffset: _mouseYScrollTransition.Value,
lyricsOpacity: _renderLyricsOpacity,
playingLineTopOffsetFactor: lyricsStyle.PlayingLineTopOffset / 100.0,
windowStatus: status,
strokeColor: lyricsThemeColors.StrokeFontColor,
bgColor: lyricsThemeColors.BgFontColor,
@@ -411,11 +411,11 @@ 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: {_layoutManager.CalculateActualHeight(_lyricsData?.LyricsLines)}\n" +
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines)}\n" +
$"Playing line (idx): {_playingLineIndex}\n" +
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
$"Total line count: {_layoutManager.CalculateMaxRange(_lyricsData?.LyricsLines).End + 1}\n" +
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines).End + 1}\n" +
$"Played: {TimeSpan.FromMilliseconds(fixedSongPositionMs)} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
$"Y offset: {_canvasYScrollTransition.Value}\n" +
$"User scroll offset: {_mouseYScrollTransition.Value}",
@@ -427,6 +427,7 @@ namespace BetterLyrics.WinUI3.Controls
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args)
{
var lyricsBg = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings;
var lyricsStyle = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
@@ -456,7 +457,7 @@ namespace BetterLyrics.WinUI3.Controls
if (isPlayingLineChanged || _isLayoutChanged)
{
var targetScroll = _layoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
@@ -469,24 +470,26 @@ namespace BetterLyrics.WinUI3.Controls
_mouseYScrollTransition.Update(elapsedTime);
_mouseHoverLineIndex = _layoutManager.FindMouseHoverLineIndex(
_lyricsData?.LyricsLines,
_mouseHoverLineIndex = LyricsLayoutManager.FindMouseHoverLineIndex(
_lyricsData?.LyricsLines,
_isMouseInLyricsArea,
_mousePosition,
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
_renderLyricsStartY,
_renderLyricsHeight
_mousePosition,
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
_renderLyricsStartY,
_renderLyricsHeight,
lyricsStyle.PlayingLineTopOffset / 100.0
);
_visibleRange = _layoutManager.CalculateVisibleRange(
_visibleRange = LyricsLayoutManager.CalculateVisibleRange(
_lyricsData?.LyricsLines,
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value, // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>λ<EFBFBD><CEBB>
_renderLyricsStartY,
_renderLyricsHeight,
sender.Size.Height
sender.Size.Height,
lyricsStyle.PlayingLineTopOffset / 100.0
);
var maxRange = _layoutManager.CalculateMaxRange(_lyricsData?.LyricsLines);
var maxRange = LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines);
_animator.UpdateLines(
_lyricsData,
@@ -495,6 +498,7 @@ namespace BetterLyrics.WinUI3.Controls
_playingLineIndex,
sender.Size.Height,
_canvasTargetScrollOffset,
lyricsStyle.PlayingLineTopOffset / 100.0,
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings,
_canvasYScrollTransition,
albumArtThemeColors.BgFontColor,
@@ -587,9 +591,9 @@ namespace BetterLyrics.WinUI3.Controls
private void TriggerRelayout()
{
if (_layoutManager == null || _lyricsData == null || !_isLayoutChanged) return;
if (_lyricsData == null || !_isLayoutChanged) return;
_layoutManager.MeasureAndArrange(
LyricsLayoutManager.MeasureAndArrange(
resourceCreator: Canvas,
lyricsData: _lyricsData,
status: _liveStatesService.LiveStates.LyricsWindowStatus,
@@ -727,6 +731,10 @@ namespace BetterLyrics.WinUI3.Controls
{
_isLayoutChanged = true;
}
else if (message.PropertyName == nameof(LyricsStyleSettings.PlayingLineTopOffset))
{
_isLayoutChanged = true;
}
}
else if (message.Sender is LyricsEffectSettings)
{

View File

@@ -29,6 +29,26 @@
</ComboBox>
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE78A;}">
<local:ExtendedSlider
Default="50"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF579;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
Default="0.5"
Frequency="0.1"
Maximum="2"
Minimum="0"
Unit="x"
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D2;}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageCJK">
@@ -207,17 +227,6 @@
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF579;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
Default="0.5"
Frequency="0.1"
Maximum="2"
Minimum="0"
Unit="x"
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
</dev:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Logic
int playingLineIndex,
double canvasHeight,
double targetYScrollOffset,
double playingLineTopOffsetFactor,
LyricsEffectSettings lyricsEffect,
ValueTransition<double> canvasYScrollTransition,
Color bgColor,
@@ -45,7 +46,16 @@ namespace BetterLyrics.WinUI3.Logic
int lineCountDelta = i - playingLineIndex;
int absLineCountDelta = Math.Abs(lineCountDelta);
double distanceFromPlayingLine = Math.Abs(line.OriginalPosition.Y - currentPlayingLine.OriginalPosition.Y);
double distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight / 2), 0, 1);
double distanceFactor = 0;
if (lineCountDelta < 0)
{
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * playingLineTopOffsetFactor), 0, 1);
}
else
{
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * (1 - playingLineTopOffsetFactor)), 0, 1);
}
double yScrollDuration;
double yScrollDelay;

View File

@@ -23,7 +23,7 @@ namespace BetterLyrics.WinUI3.Logic
/// <param name="canvasHeight"></param>
/// <param name="lyricsWidth"></param>
/// <param name="lyricsHeight"></param>
public void MeasureAndArrange(
public static void MeasureAndArrange(
ICanvasAnimatedControl resourceCreator,
LyricsData? lyricsData,
LyricsWindowStatus status,
@@ -129,7 +129,7 @@ namespace BetterLyrics.WinUI3.Logic
/// <summary>
/// 计算为了让当前歌词行的竖直几何中心点对齐到 0原点画布应该移动的距离从画布最初始状态计算的值
/// </summary>
public double? CalculateTargetScrollOffset(
public static double? CalculateTargetScrollOffset(
LyricsData? lyricsData,
int playingLineIndex)
{
@@ -141,27 +141,26 @@ namespace BetterLyrics.WinUI3.Logic
if (currentLine?.OriginalCanvasTextLayout == null || firstLine == null) return null;
return -currentLine.OriginalPosition.Y
+ firstLine.OriginalPosition.Y
- (currentLine.TranslatedPosition.Y
+ (currentLine.TranslatedCanvasTextLayout?.LayoutBounds.Height ?? 0)
- currentLine.PhoneticPosition.Y) / 2.0;
return -currentLine.OriginalPosition.Y + firstLine.OriginalPosition.Y
- (currentLine.BottomRightPosition.Y - currentLine.TopLeftPosition.Y) / 2.0;
}
/// <summary>
/// 计算当前屏幕可见的行范围
/// 返回值: (StartVisibleIndex, EndVisibleIndex)
/// </summary>
public (int Start, int End) CalculateVisibleRange(
public static (int Start, int End) CalculateVisibleRange(
IList<LyricsLine>? lines,
double currentScrollOffset,
double lyricsY,
double lyricsHeight,
double canvasHeight)
double canvasHeight,
double playingLineTopOffsetFactor
)
{
if (lines == null || lines.Count == 0) return (-1, -1);
double offset = currentScrollOffset + lyricsY + lyricsHeight / 2;
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
int start = FindFirstVisibleLine(lines, offset, lyricsY);
int end = FindLastVisibleLine(lines, offset, lyricsY, lyricsHeight, canvasHeight);
@@ -175,34 +174,35 @@ namespace BetterLyrics.WinUI3.Logic
return (start, end);
}
public (int Start, int End) CalculateMaxRange(IList<LyricsLine>? lines)
public static (int Start, int End) CalculateMaxRange(IList<LyricsLine>? lines)
{
if (lines == null || lines.Count == 0) return (-1, -1);
return (0, lines.Count - 1);
}
public double CalculateActualHeight(IList<LyricsLine>? lines)
public static double CalculateActualHeight(IList<LyricsLine>? lines)
{
if (lines == null || lines.Count == 0) return 0;
return lines.Last().BottomRightPosition.Y;
}
public int FindMouseHoverLineIndex(
public static int FindMouseHoverLineIndex(
IList<LyricsLine>? lines,
bool isMouseInLyricsArea,
Point mousePosition,
double currentScrollOffset,
double lyricsY,
double lyricsHeight
double lyricsHeight,
double playingLineTopOffsetFactor
)
{
if (!isMouseInLyricsArea) return -1;
if (lines == null || lines.Count == 0) return -1;
double offset = currentScrollOffset + lyricsY + lyricsHeight / 2;
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
int left = 0, right = lines.Count - 1, result = -1;
while (left <= right)
@@ -217,7 +217,7 @@ namespace BetterLyrics.WinUI3.Logic
return result;
}
private int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
private static int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
{
int left = 0, right = lines.Count - 1, result = -1;
while (left <= right)
@@ -234,7 +234,7 @@ namespace BetterLyrics.WinUI3.Logic
return result;
}
private int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
private static int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
{
int left = 0, right = lines.Count - 1, result = -1;
while (left <= right)

View File

@@ -14,19 +14,27 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsFontSize { get; set; } = 12;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsFontSize { get; set; } = 24;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsFontSize { get; set; } = 12;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; } = TextAlignmentType.Left;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; } = 0;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomBgFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomFgFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomStrokeFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsBgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsFgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontWeight LyricsFontWeight { get; set; } = LyricsFontWeight.Bold;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayingLineTopOffset { get; set; } = 50; // 50 %
public LyricsStyleSettings() { }
public object Clone()
@@ -49,6 +57,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
LyricsLineSpacingFactor = this.LyricsLineSpacingFactor,
LyricsCJKFontFamily = this.LyricsCJKFontFamily,
LyricsWesternFontFamily = this.LyricsWesternFontFamily,
PlayingLineTopOffset = this.PlayingLineTopOffset,
};
}
}

View File

@@ -35,6 +35,7 @@ namespace BetterLyrics.WinUI3.Renderer
double lyricsHeight,
double userScrollOffset,
double lyricsOpacity,
double playingLineTopOffsetFactor,
LyricsWindowStatus windowStatus,
Color strokeColor,
Color bgColor,
@@ -63,6 +64,7 @@ namespace BetterLyrics.WinUI3.Renderer
lyricsWidth,
lyricsHeight,
userScrollOffset,
playingLineTopOffsetFactor,
windowStatus,
strokeColor,
bgColor,
@@ -93,6 +95,7 @@ namespace BetterLyrics.WinUI3.Renderer
lyricsWidth,
lyricsHeight,
userScrollOffset,
playingLineTopOffsetFactor,
windowStatus,
strokeColor,
bgColor,
@@ -116,6 +119,7 @@ namespace BetterLyrics.WinUI3.Renderer
double lyricsWidth,
double lyricsHeight,
double userScrollOffset,
double playingLineTopOffsetFactor,
LyricsWindowStatus windowStatus,
Color strokeColor,
Color bgColor,
@@ -140,7 +144,7 @@ namespace BetterLyrics.WinUI3.Renderer
if (line.OriginalCanvasTextLayout == null) continue;
if (line.OriginalCanvasTextLayout.LayoutBounds.Width <= 0) continue;
double yOffset = line.YOffsetTransition.Value + userScrollOffset + lyricsY + lyricsHeight / 2;
double yOffset = line.YOffsetTransition.Value + userScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
var transform =
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition) *

View File

@@ -1005,6 +1005,9 @@
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>Bold</value>
</data>
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
<value>Current line position</value>
</data>
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
<value>Lyrics area width factor</value>
</data>

View File

@@ -1005,6 +1005,9 @@
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>大胆な</value>
</data>
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
<value>現在の行の位置</value>
</data>
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
<value>歌詞エリア幅係数</value>
</data>

View File

@@ -1005,6 +1005,9 @@
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>용감한</value>
</data>
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
<value>현재 행 위치</value>
</data>
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
<value>가사 영역 너비 계수</value>
</data>

View File

@@ -1005,6 +1005,9 @@
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>粗体</value>
</data>
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
<value>当前行位置</value>
</data>
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
<value>歌词区域宽度因子</value>
</data>

View File

@@ -1005,6 +1005,9 @@
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>粗體</value>
</data>
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
<value>目前行位置</value>
</data>
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
<value>歌詞區域寬度因子</value>
</data>

View File

@@ -192,7 +192,8 @@
PointerMoved="LyricsScrollViewer_PointerMoved"
PointerPressed="LyricsScrollViewer_PointerPressed"
PointerReleased="LyricsScrollViewer_PointerReleased"
PointerWheelChanged="LyricsScrollViewer_PointerWheelChanged">
PointerWheelChanged="LyricsScrollViewer_PointerWheelChanged"
VerticalScrollBarVisibility="Hidden">
<Grid />
</ScrollViewer>
</Grid>