Compare commits

...

4 Commits

Author SHA1 Message Date
Zhe Fang
7b6eca6ff6 fix memory leaking 2025-08-12 11:43:14 -04:00
Zhe Fang
9fcb1ac869 fix 2025-08-12 09:52:17 -04:00
Zhe Fang
74ebda2b6d fix translation 2025-08-11 21:05:19 -04:00
Zhe Fang
e194dfaa70 fix #79 fix #80 2025-08-11 09:03:19 -04:00
31 changed files with 477 additions and 589 deletions

View File

@@ -12,7 +12,7 @@
<Identity
Name="37412.BetterLyrics"
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
Version="1.0.46.0" />
Version="1.0.49.0" />
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -17,7 +17,7 @@
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE71A;}">
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius">
<local:ExtendedSlider
Default="12"
Frequency="1"
@@ -27,6 +27,15 @@
Value="{x:Bind ViewModel.AppSettings.AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAlbumShadowAmount">
<local:ExtendedSlider
Default="12"
Frequency="1"
Maximum="64"
Minimum="0"
Value="{x:Bind ViewModel.AppSettings.AlbumArtLayoutSettings.CoverImageShadowAmount, Mode=TwoWay}" />
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">

View File

@@ -29,7 +29,7 @@
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D2;}">
<ComboBox ItemsSource="{x:Bind SystemFontNames, Mode=OneWay}" SelectedIndex="{x:Bind LyricsStyleSettings.SelectedFontFamilyIndex, Mode=TwoWay}">
<ComboBox ItemsSource="{x:Bind SystemFontNames, Mode=OneWay}" SelectedItem="{x:Bind LyricsStyleSettings.LyricsFontFamily, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />

View File

@@ -18,31 +18,6 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<NavigationView
VerticalAlignment="Top"
Background="Transparent"
IsBackButtonVisible="Collapsed"
IsBackEnabled="False"
IsSettingsVisible="False"
MenuItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
PaneDisplayMode="Top"
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
<NavigationView.MenuItemTemplate>
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
<NavigationViewItem>
<NavigationViewItem.Icon>
<ImageIcon Source="{Binding Provider, Converter={StaticResource MediaSourceProviderToLogoUriConverter}, Mode=OneWay}" />
</NavigationViewItem.Icon>
<NavigationViewItem.Content>
<TextBlock
MaxWidth="200"
Text="{Binding Provider, Converter={StaticResource MediaSourceProviderToDisplayedNameConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</NavigationViewItem.Content>
</NavigationViewItem>
</DataTemplate>
</NavigationView.MenuItemTemplate>
</NavigationView>
<ScrollViewer Margin="0,72,0,0" Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -143,6 +118,31 @@
</StackPanel>
</Grid>
</ScrollViewer>
<ListView
VerticalAlignment="Top"
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
<StackPanel Orientation="Horizontal" Spacing="6">
<ImageIcon Height="16" Source="{Binding Provider, Converter={StaticResource MediaSourceProviderToLogoUriConverter}, Mode=OneWay}" />
<TextBlock
MaxWidth="200"
Text="{Binding Provider, Converter={StaticResource MediaSourceProviderToDisplayedNameConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo.Count, Mode=OneWay}"

View File

@@ -1,11 +1,19 @@
using Microsoft.Graphics.Canvas.Brushes;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Effects;
using Windows.UI;
namespace BetterLyrics.WinUI3.Helper
@@ -29,5 +37,264 @@ namespace BetterLyrics.WinUI3.Helper
EndPoint = new Vector2((float)(startX + width), 0),
};
}
/// <summary>
/// 背景层
/// </summary>
/// <param name="lyricsLayerOpacity">_lyricsOpacityTransition.Value</param>
public static OpacityEffect CreateBackgroundEffect(LyricsLine lyricsLine, CanvasCommandList backgroundFontEffect, double lyricsLayerOpacity)
{
return new OpacityEffect
{
Source = new GaussianBlurEffect
{
Source = backgroundFontEffect,
BlurAmount = (float)lyricsLine.BlurAmountTransition.Value,
BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Speed,
},
Opacity = (float)(lyricsLine.OpacityTransition.Value * lyricsLayerOpacity),
};
}
public static CanvasCommandList CreateFontEffect(LyricsLine lyricsLine, ICanvasAnimatedControl control, Color strokeColor, int strokeWidth, Color fontColor)
{
CanvasCommandList list = new(control);
using var ds = list.CreateDrawingSession();
if (strokeWidth > 0)
{
ds.DrawGeometry(lyricsLine.TextGeometry, lyricsLine.Position, strokeColor, strokeWidth); // 描边
}
ds.FillGeometry(lyricsLine.TextGeometry, lyricsLine.Position, fontColor); // 填充
return list;
}
/// <summary>
/// 创建辉光效果层
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="lineRenderingType">_lyricsGlowEffectScope</param>
/// <param name="glowEffectAmount">_lyricsGlowEffectAmount</param>
public static GaussianBlurEffect CreateForegroundBlurEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask, double glowEffectAmount)
{
return new GaussianBlurEffect
{
Source = new AlphaMaskEffect
{
Source = foregroundFontEffect,
AlphaMask = mask,
},
BlurAmount = (float)glowEffectAmount,
Optimization = EffectOptimization.Speed,
};
}
/// <summary>
/// 仅当前播放行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="playingLineIndex"></param>
/// <param name="charStartIndex"></param>
/// <param name="charLength"></param>
/// <param name="charProgress"></param>
public static CanvasCommandList CreateCharMask(ICanvasAnimatedControl control, LyricsLine lyricsLine, int charStartIndex, int charLength, double charProgress)
{
var mask = new CanvasCommandList(control);
using var ds = mask.CreateDrawingSession();
if (lyricsLine.CanvasTextLayout == null)
{
return mask;
}
var highlightRegion = lyricsLine.CanvasTextLayout.GetCharacterRegions(charStartIndex, charLength).FirstOrDefault();
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
// Draw the highlight for the current character
double highlightWidth = highlightTotalWidth * charProgress;
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
// Rects
var highlightRect = new Rect(
highlightRegion.LayoutBounds.X,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
highlightWidth,
highlightRegion.LayoutBounds.Height
);
var fadeInRect = new Rect(
highlightRect.Right - fadingWidth,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
var fadeOutRect = new Rect(
highlightRect.Right,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
// Brushes
using var fadeInBrush = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 0f), (1f, 1f)],
(double)highlightRect.Right - fadingWidth,
fadingWidth
);
using var fadeOutBrush = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 1f), (1f, 0f)],
(double)highlightRect.Right,
fadingWidth
);
ds.FillRectangle(fadeInRect, fadeInBrush);
ds.FillRectangle(fadeOutRect, fadeOutBrush);
return mask;
}
/// <summary>
/// 仅当前播放行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="playingLineIndex"></param>
/// <param name="charStartIndex"></param>
/// <param name="charLength"></param>
/// <param name="charProgress"></param>
public static CanvasCommandList CreateLineStartToCharMask(ICanvasAnimatedControl control, LyricsLine lyricsLine, int charStartIndex, int charLength, double charProgress)
{
var mask = new CanvasCommandList(control);
if (lyricsLine.CanvasTextLayout == null)
{
return mask;
}
using var ds = mask.CreateDrawingSession();
var regions = lyricsLine.CanvasTextLayout.GetCharacterRegions(0, charStartIndex);
var highlightRegion = lyricsLine.CanvasTextLayout
.GetCharacterRegions(charStartIndex, charLength)
.FirstOrDefault();
if (regions.Length > 0)
{
// Draw the mask for the current line
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + lyricsLine.Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
ds.FillRectangle(rect, Color.FromArgb(255, 128, 128, 128));
}
}
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
// Draw the highlight for the current character
double highlightWidth = highlightTotalWidth * charProgress;
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
// Rects
var highlightRect = new Rect(
highlightRegion.LayoutBounds.X,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
highlightWidth,
highlightRegion.LayoutBounds.Height
);
var fadeInRect = new Rect(
highlightRect.Right - fadingWidth,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
var fadeOutRect = new Rect(
highlightRect.Right,
highlightRegion.LayoutBounds.Y + lyricsLine.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
// Brushes
using var fadeOutBrush = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 1f), (1f, 0f)],
(double)highlightRect.Right,
fadingWidth
);
ds.FillRectangle(highlightRect, Color.FromArgb(255, 128, 128, 128));
ds.FillRectangle(fadeOutRect, fadeOutBrush);
return mask;
}
/// <summary>
/// 创建行遮罩
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="control"></param>
public static CanvasCommandList CreateLineMask(ICanvasAnimatedControl control, LyricsLine lyricsLine)
{
var mask = new CanvasCommandList(control);
using var ds = mask.CreateDrawingSession();
if (lyricsLine.CanvasTextLayout == null)
{
return mask;
}
var regions = lyricsLine.CanvasTextLayout.GetCharacterRegions(0, lyricsLine.OriginalText.Length);
if (regions.Length > 0)
{
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + lyricsLine.Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
ds.FillRectangle(rect, Colors.White);
}
}
return mask;
}
/// <summary>
/// 创建高亮效果层
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="control"></param>
/// <param name="lineRenderingType"></param>
public static AlphaMaskEffect CreateForegroundHighlightEffect(CanvasCommandList foregroundFontEffect, IGraphicsEffectSource mask)
{
return new AlphaMaskEffect
{
Source = foregroundFontEffect,
AlphaMask = mask,
};
}
public static IGraphicsEffectSource GetAlphaMask(ICanvasAnimatedControl control, IGraphicsEffectSource charMask, IGraphicsEffectSource lineStartToCharMask, IGraphicsEffectSource lineMask, LineRenderingType lineRenderingType)
{
var result = lineRenderingType switch
{
LineRenderingType.CurrentChar => charMask,
LineRenderingType.LineStartToCurrentChar => lineStartToCharMask,
LineRenderingType.CurrentLine => lineMask,
_ => new CanvasCommandList(control),
};
return result;
}
}
}

View File

@@ -24,6 +24,11 @@ namespace BetterLyrics.WinUI3.Helper
private static readonly Dictionary<IntPtr, RECT> _originalPositions = [];
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyle = [];
public static bool IsEnabled(IntPtr hwnd)
{
return _registered.Contains(hwnd);
}
public static void Disable(Window window)
{
IntPtr hwnd = WindowNative.GetWindowHandle(window);

View File

@@ -11,10 +11,6 @@ namespace BetterLyrics.WinUI3.Helper
{
public static class FontHelper
{
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies();
public static string GetUserPreferredFontFamily() => SystemFontFamilies.ElementAtOrDefault(_settingsService.AppSettings.StandardLyricsStyleSettings.SelectedFontFamilyIndex) ?? "Segoe UI";
}
}

View File

@@ -16,7 +16,6 @@ namespace BetterLyrics.WinUI3.Services
{
private static readonly RankedLanguageIdentifierFactory _factory = new();
private static readonly RankedLanguageIdentifier _identifier;
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
public static List<Models.LanguageInfo> SupportedTargetLanguages =>
[
@@ -69,7 +68,7 @@ namespace BetterLyrics.WinUI3.Services
"simple" => "en",
"zh_classical" => "zh-Hant",
"zh_yue" => "zh-Hant",
"zh" => "zh-Hans",
"zh" => text == ChineseConverter.ConvertToSimplifiedChinese(text) ? "zh-Hans" : "zh-Hant",
_ => code
};
return code;
@@ -99,11 +98,6 @@ namespace BetterLyrics.WinUI3.Services
};
}
public static string GetUserTargetLanguageCode()
{
return SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
}
public static int GetDefaultTargetLanguageIndex()
{
int found = SupportedTargetLanguages.FindIndex(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);

View File

@@ -3,6 +3,7 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services;
using Lyricify.Lyrics.Helpers.General;
using Lyricify.Lyrics.Models;
using System;
using System.Collections.Generic;
@@ -47,10 +48,57 @@ namespace BetterLyrics.WinUI3.Helper
break;
}
}
FillChineseLyricsData();
_lyricsDataArr.Add(new LyricsData()); // 为机翻预留
return _lyricsDataArr;
}
private void FillChineseLyricsData()
{
var simplifiedChinese = _lyricsDataArr.Where(x => x.LanguageCode == "zh-Hans").FirstOrDefault();
var traditionalChinese = _lyricsDataArr.Where(x => x.LanguageCode == "zh-Hant").FirstOrDefault();
if (simplifiedChinese != null && traditionalChinese == null)
{
// 如果没有繁体中文歌词,则将简体中文歌词转换为繁体中文
_lyricsDataArr.Add(new LyricsData
{
LyricsLines = simplifiedChinese.LyricsLines.Select(line => new LyricsLine
{
StartMs = line.StartMs,
EndMs = line.EndMs,
OriginalText = ChineseConverter.ConvertToTraditionalChinese(line.OriginalText),
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
{
StartMs = c.StartMs,
EndMs = c.EndMs,
Text = ChineseConverter.ConvertToTraditionalChinese(c.Text),
StartIndex = c.StartIndex
}).ToList()
}).ToList()
});
}
else if (traditionalChinese != null && simplifiedChinese == null)
{
// 如果没有简体中文歌词,则将繁体中文歌词转换为简体中文
_lyricsDataArr.Add(new LyricsData
{
LyricsLines = traditionalChinese.LyricsLines.Select(line => new LyricsLine
{
StartMs = line.StartMs,
EndMs = line.EndMs,
OriginalText = ChineseConverter.ConvertToSimplifiedChinese(line.OriginalText),
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
{
StartMs = c.StartMs,
EndMs = c.EndMs,
Text = ChineseConverter.ConvertToSimplifiedChinese(c.Text),
StartIndex = c.StartIndex
}).ToList()
}).ToList()
});
}
}
private void ParseLrc(string raw)
{
var lines = raw.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);

View File

@@ -36,23 +36,7 @@ namespace BetterLyrics.WinUI3.Models
if (transLine != null)
{
if (translationData.LanguageCode?.StartsWith("zh") == true)
{
string tmp = "";
if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hant")
{
tmp = ChineseConverter.ConvertToTraditionalChinese(transLine.OriginalText);
}
else if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hans")
{
tmp = ChineseConverter.ConvertToSimplifiedChinese(transLine.OriginalText);
}
line.DisplayedText = $"{line.OriginalText}{separator}{tmp}";
}
else
{
line.DisplayedText = $"{line.OriginalText}{separator}{transLine.OriginalText}";
}
line.DisplayedText = $"{line.OriginalText}{separator}{transLine.OriginalText}";
}
else
{

View File

@@ -69,39 +69,6 @@ namespace BetterLyrics.WinUI3.Models
public CanvasGeometry? TextGeometry { get; private set; }
/// <summary>
/// 背景文字层(底字)
/// </summary>
public CanvasCommandList? BackgroundFontEffect { get; private set; }
/// <summary>
/// 背景层
/// </summary>
public OpacityEffect? BackgroundEffect { get; private set; }
/// <summary>
/// 辉光层
/// </summary>
public GaussianBlurEffect? ForegroundBlurEffect { get; private set; }
/// <summary>
/// 高亮层
/// </summary>
public AlphaMaskEffect? ForegroundHighlightEffect { get; private set; }
/// <summary>
/// 前景文字层
/// </summary>
public CanvasCommandList? ForegroundFontEffect { get; private set; }
public CanvasCommandList? ComposedLineEffect { get; private set; }
public CanvasCommandList? CurrentCharMask { get; private set; }
public CanvasCommandList? LineStartToCurrentCharMask { get; private set; }
public CanvasCommandList? CurrentLineMask { get; private set; }
public CanvasCommandList? PlaceholderEffect { get; private set; }
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
{
if (CanvasTextLayout == null)
@@ -118,10 +85,15 @@ namespace BetterLyrics.WinUI3.Models
};
}
public void RecreateTextLayout(ICanvasAnimatedControl control, CanvasTextFormat textFormat, double maxWidth, double maxHeight, TextAlignmentType type)
public void DisposeTextLayout()
{
CanvasTextLayout?.Dispose();
CanvasTextLayout = null;
}
public void RecreateTextLayout(ICanvasAnimatedControl control, CanvasTextFormat textFormat, double maxWidth, double maxHeight, TextAlignmentType type)
{
DisposeTextLayout();
CanvasTextLayout = new CanvasTextLayout(control, DisplayedText, textFormat, (float)maxWidth, (float)maxHeight);
CanvasTextLayout.HorizontalAlignment = type.ToCanvasHorizontalAlignment();
}
@@ -141,392 +113,5 @@ namespace BetterLyrics.WinUI3.Models
}
TextGeometry = CanvasGeometry.CreateText(CanvasTextLayout);
}
public void DisposeFontEffects()
{
BackgroundFontEffect?.Dispose();
BackgroundFontEffect = null;
ForegroundFontEffect?.Dispose();
ForegroundFontEffect = null;
}
public void RecreateFontEffect(ICanvasAnimatedControl control, Color strokeColor, int strokeWidth, Color bgFontColor, Color fgFontColor)
{
DisposeFontEffects();
if (TextGeometry == null)
{
return;
}
BackgroundFontEffect = new CanvasCommandList(control);
using var bgFontEffectDs = BackgroundFontEffect.CreateDrawingSession();
ForegroundFontEffect = new CanvasCommandList(control);
using var fgFontEffectDs = ForegroundFontEffect.CreateDrawingSession();
// 大于 0 才描边,避免不必要的资源浪费
if (strokeWidth > 0)
{
bgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边
fgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边
}
bgFontEffectDs.FillGeometry(TextGeometry, Position, bgFontColor); // 填充
fgFontEffectDs.FillGeometry(TextGeometry, Position, fgFontColor); // 填充
}
/// <summary>
/// 背景层
/// </summary>
/// <param name="lyricsLayerOpacity">_lyricsOpacityTransition.Value</param>
public void RecreateBackgroundEffect(double lyricsLayerOpacity)
{
BackgroundEffect?.Dispose();
BackgroundEffect = null;
if (BackgroundFontEffect == null)
{
return;
}
BackgroundEffect = new OpacityEffect
{
Source = new GaussianBlurEffect
{
Source = BackgroundFontEffect,
BlurAmount = (float)BlurAmountTransition.Value,
BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Speed,
},
Opacity = (float)(OpacityTransition.Value * lyricsLayerOpacity),
};
}
public void UpdateBackgroundEffect(double lyricsLayerOpacity)
{
BackgroundEffect?.Opacity = (float)(OpacityTransition.Value * lyricsLayerOpacity);
GaussianBlurEffect? blurEffect = (GaussianBlurEffect?)(BackgroundEffect?.Source);
blurEffect?.BlurAmount = (float)BlurAmountTransition.Value;
}
private IGraphicsEffectSource GetAlphaMask(ICanvasAnimatedControl control, LineRenderingType lineRenderingType)
{
if (PlaceholderEffect == null)
{
RecreatePlaceholder(control);
}
var result = lineRenderingType switch
{
LineRenderingType.CurrentChar => CurrentCharMask,
LineRenderingType.LineStartToCurrentChar => LineStartToCurrentCharMask,
// Here, cuz AlphaMask only takes care of alpha channel
// so ForegroundFontEffect can be a mask for CurrentLine
// And we don't need to create a new mask for CurrentLine
LineRenderingType.CurrentLine => CurrentLineMask,
_ => PlaceholderEffect
};
return result ?? PlaceholderEffect!;
}
/// <summary>
/// 销毁并重新创建辉光效果层
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="lineRenderingType">_lyricsGlowEffectScope</param>
/// <param name="glowEffectAmount">_lyricsGlowEffectAmount</param>
public void RecreateForegroundBlurEffect(ICanvasAnimatedControl control, LineRenderingType lineRenderingType, double glowEffectAmount)
{
ForegroundBlurEffect?.Dispose();
ForegroundBlurEffect = null;
if (ForegroundFontEffect == null)
{
return;
}
var mask = GetAlphaMask(control, lineRenderingType);
if (mask == null)
{
return;
}
ForegroundBlurEffect = new GaussianBlurEffect
{
Source = new AlphaMaskEffect
{
Source = ForegroundFontEffect,
AlphaMask = mask,
},
BlurAmount = (float)glowEffectAmount,
Optimization = EffectOptimization.Speed,
};
}
/// <summary>
/// 仅当前行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="lineRenderingType"></param>
/// <param name="glowEffectAmount"></param>
public void UpdateForegroundBlurEffect(ICanvasAnimatedControl control, LineRenderingType lineRenderingType, double glowEffectAmount)
{
if (ForegroundBlurEffect == null)
{
return;
}
if (ForegroundFontEffect == null)
{
return;
}
var mask = GetAlphaMask(control, lineRenderingType);
if (mask == null)
{
return;
}
ForegroundBlurEffect.BlurAmount = (float)glowEffectAmount;
var alphaMaskEffect = (AlphaMaskEffect)ForegroundBlurEffect.Source;
alphaMaskEffect.Source = ForegroundFontEffect;
alphaMaskEffect.AlphaMask = mask;
}
/// <summary>
/// 销毁并重新创建高亮效果层
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="control"></param>
/// <param name="lineRenderingType"></param>
public void RecreateForegroundHighlightEffect(ICanvasAnimatedControl control, LineRenderingType lineRenderingType)
{
ForegroundHighlightEffect?.Dispose();
ForegroundHighlightEffect = null;
if (ForegroundFontEffect == null)
{
return;
}
var mask = GetAlphaMask(control, lineRenderingType);
if (mask == null)
{
return;
}
ForegroundHighlightEffect = new AlphaMaskEffect
{
Source = ForegroundFontEffect,
AlphaMask = mask,
};
}
/// <summary>
/// 仅当前行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="lineRenderingType"></param>
public void UpdateForegroundHighlightEffect(ICanvasAnimatedControl control, LineRenderingType lineRenderingType)
{
if (ForegroundHighlightEffect == null)
{
return;
}
if (ForegroundFontEffect == null)
{
return;
}
var mask = GetAlphaMask(control, lineRenderingType);
if (mask == null)
{
return;
}
ForegroundHighlightEffect.Source = ForegroundFontEffect;
ForegroundHighlightEffect.AlphaMask = mask;
}
/// <summary>
/// 仅当前播放行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="playingLineIndex"></param>
/// <param name="charStartIndex"></param>
/// <param name="charLength"></param>
/// <param name="charProgress"></param>
public void RecreateCurrentCharMask(ICanvasAnimatedControl control, int charStartIndex, int charLength, double charProgress)
{
CurrentCharMask?.Dispose();
CurrentCharMask = null;
CurrentCharMask = new CanvasCommandList(control);
if (CanvasTextLayout == null)
{
return;
}
using var ds = CurrentCharMask.CreateDrawingSession();
var highlightRegion = CanvasTextLayout
.GetCharacterRegions(charStartIndex, charLength)
.FirstOrDefault();
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
// Draw the highlight for the current character
double highlightWidth = highlightTotalWidth * charProgress;
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
// Rects
var highlightRect = new Rect(
highlightRegion.LayoutBounds.X,
highlightRegion.LayoutBounds.Y + 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 = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 0f), (1f, 1f)],
(double)highlightRect.Right - fadingWidth,
fadingWidth
);
using var fadeOutBrush = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 1f), (1f, 0f)],
(double)highlightRect.Right,
fadingWidth
);
ds.FillRectangle(fadeInRect, fadeInBrush);
ds.FillRectangle(fadeOutRect, fadeOutBrush);
}
/// <summary>
/// 仅当前播放行需要调用此方法(每次 Update 都调用一次)
/// </summary>
/// <param name="control"></param>
/// <param name="playingLineIndex"></param>
/// <param name="charStartIndex"></param>
/// <param name="charLength"></param>
/// <param name="charProgress"></param>
public void RecreateLineStartToCurrentCharMask(ICanvasAnimatedControl control, int charStartIndex, int charLength, double charProgress)
{
LineStartToCurrentCharMask?.Dispose();
LineStartToCurrentCharMask = null;
LineStartToCurrentCharMask = new CanvasCommandList(control);
if (CanvasTextLayout == null)
{
return;
}
using var ds = LineStartToCurrentCharMask.CreateDrawingSession();
var regions = CanvasTextLayout.GetCharacterRegions(0, charStartIndex);
var highlightRegion = CanvasTextLayout
.GetCharacterRegions(charStartIndex, charLength)
.FirstOrDefault();
if (regions.Length > 0)
{
// Draw the mask for the current line
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
ds.FillRectangle(rect, Color.FromArgb(255, 128, 128, 128));
}
}
double highlightTotalWidth = (double)highlightRegion.LayoutBounds.Width;
// Draw the highlight for the current character
double highlightWidth = highlightTotalWidth * charProgress;
double fadingWidth = (double)highlightRegion.LayoutBounds.Height / 2;
// Rects
var highlightRect = new Rect(
highlightRegion.LayoutBounds.X,
highlightRegion.LayoutBounds.Y + 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 fadeOutBrush = CanvasHelper.CreateHorizontalFillBrush(
control,
[(0f, 1f), (1f, 0f)],
(double)highlightRect.Right,
fadingWidth
);
ds.FillRectangle(highlightRect, Color.FromArgb(255, 128, 128, 128));
ds.FillRectangle(fadeOutRect, fadeOutBrush);
}
/// <summary>
/// 重建当前行遮罩
/// 仅需在布局重构 (Relayout) 时调用
/// </summary>
/// <param name="control"></param>
public void RecreateCurrentLineMask(ICanvasAnimatedControl control)
{
CurrentLineMask?.Dispose();
CurrentLineMask = null;
if (CanvasTextLayout == null)
{
return;
}
CurrentLineMask = new CanvasCommandList(control);
using var ds = CurrentLineMask.CreateDrawingSession();
var regions = CanvasTextLayout.GetCharacterRegions(0, OriginalText.Length);
if (regions.Length > 0)
{
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
ds.FillRectangle(rect, Colors.White);
}
}
}
public void RecreatePlaceholder(ICanvasAnimatedControl control)
{
PlaceholderEffect?.Dispose();
PlaceholderEffect = null;
PlaceholderEffect = new CanvasCommandList(control);
}
}
}

View File

@@ -12,6 +12,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType SongInfoAlignmentType { get; set; } = TextAlignmentType.Left;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageRadius { get; set; } = 12; // 12 % of the cover image size
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageShadowAmount { get; set; } = 12;
public AlbumArtLayoutSettings() { }
}

View File

@@ -27,7 +27,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsTranslationSeparator { get; set; } = StringHelper.NewLine;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SelectedFontFamilyIndex { get; set; } = 0;
public LyricsStyleSettings() { }

View File

@@ -72,15 +72,19 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
{
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
{
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
try
{
Track track = new(file);
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
if (bytes != null)
if (track.Artist == artist && track.Title == title)
{
return bytes;
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;
if (bytes != null)
{
return bytes;
}
}
}
catch (Exception) { }
}
}
}

View File

@@ -197,9 +197,10 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
{
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
try
{
try
Track track = new(file);
if (track.Artist == artist && track.Title == title)
{
var plain = TagLib.File.Create(file).Tag.Lyrics;
if (plain != null && plain != string.Empty)
@@ -207,12 +208,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return plain;
}
}
catch (Exception) { }
}
catch (Exception) { }
}
}
}
return null;
}

View File

@@ -53,6 +53,8 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
AppSettings.LocalMediaFolders.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.LocalMediaFolders.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.Version = MetadataHelper.AppVersion;
EnsureMediaSourceProvidersInfo();
}

View File

@@ -3,8 +3,6 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Serialization;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using Lyricify.Lyrics.Helpers.General;
using Microsoft.UI.Dispatching;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -39,14 +37,6 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
{
return text; // No translation needed
}
else if (originalLangCode == "zh-Hant" && targetLangCode == "zh-Hans")
{
return ChineseConverter.ConvertToSimplifiedChinese(text);
}
else if (originalLangCode == "zh-Hans" && targetLangCode == "zh-Hant")
{
return ChineseConverter.ConvertToTraditionalChinese(text);
}
if (string.IsNullOrEmpty(_settingsService.AppSettings.TranslationSettings.LibreTranslateServer))
{
@@ -72,12 +62,12 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
public int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr)
{
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode().Substring(0, 2);
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
if (lyricsDataArr.Count > 1)
{
for (int i = 1; i < lyricsDataArr.Count; i++)
{
if (lyricsDataArr[i].LanguageCode?.Substring(0, 2) == targetLangCode)
if (lyricsDataArr[i].LanguageCode == targetLangCode)
{
return i; // Translation lyrics data found
}

View File

@@ -433,6 +433,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>Corner radius</value>
</data>
<data name="SettingsPageAlbumShadowAmount.Header" xml:space="preserve">
<value>How much the shadows are spreading</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>Album art area style</value>
</data>

View File

@@ -433,6 +433,9 @@
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>コーナー半径</value>
</data>
<data name="SettingsPageAlbumShadowAmount.Header" xml:space="preserve">
<value>影がどれだけ広がっているか</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>アルバムエリアスタイル</value>
</data>

View File

@@ -433,6 +433,9 @@
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>코너 반경</value>
</data>
<data name="SettingsPageAlbumShadowAmount.Header" xml:space="preserve">
<value>그림자가 얼마나 퍼져나가는지</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>앨범 영역 스타일</value>
</data>

View File

@@ -433,6 +433,9 @@
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>圆角半径</value>
</data>
<data name="SettingsPageAlbumShadowAmount.Header" xml:space="preserve">
<value>阴影扩散程度</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>专辑区域样式</value>
</data>

View File

@@ -433,6 +433,9 @@
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>圓角半徑</value>
</data>
<data name="SettingsPageAlbumShadowAmount.Header" xml:space="preserve">
<value>陰影擴散程度</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>專輯區域樣式</value>
</data>

View File

@@ -43,9 +43,7 @@ namespace BetterLyrics.WinUI3.ViewModels
IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
ShowTranslationOnly = _settingsService.AppSettings.TranslationSettings.ShowTranslationOnly;
UpdateHintMessageFontSize();
LyricsFontFamily = _settingsService.AppSettings.StandardLyricsStyleSettings.LyricsFontFamily;
UpdateLyricsStyleSettings();
OnIsImmersiveModeChanged(IsImmersiveMode);
@@ -90,12 +88,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial int Volume { get; set; }
[ObservableProperty]
public partial string LyricsFontFamily { get; set; }
[ObservableProperty]
public partial int HintMessageFontSize { get; set; }
[ObservableProperty]
public partial bool IsImmersiveMode { get; set; }
@@ -123,19 +115,22 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsSongPlaying { get; set; }
private void UpdateHintMessageFontSize()
[ObservableProperty]
public partial LyricsStyleSettings LyricsStyleSettings { get; set; }
private void UpdateLyricsStyleSettings()
{
if (_isDockMode)
{
HintMessageFontSize = _settingsService.AppSettings.DockLyricsStyleSettings.LyricsFontSize;
LyricsStyleSettings = _settingsService.AppSettings.DockLyricsStyleSettings;
}
else if (_isDesktopMode)
{
HintMessageFontSize = _settingsService.AppSettings.DesktopLyricsStyleSettings.LyricsFontSize;
LyricsStyleSettings = _settingsService.AppSettings.DesktopLyricsStyleSettings;
}
else
{
HintMessageFontSize = _settingsService.AppSettings.StandardLyricsStyleSettings.LyricsFontSize;
LyricsStyleSettings = _settingsService.AppSettings.StandardLyricsStyleSettings;
}
}
@@ -154,7 +149,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
DisplayType = _settingsService.AppSettings.GeneralSettings.DisplayType;
}
UpdateHintMessageFontSize();
UpdateLyricsStyleSettings();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
{
@@ -167,7 +162,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
DisplayType = _settingsService.AppSettings.GeneralSettings.DisplayType;
}
UpdateHintMessageFontSize();
UpdateLyricsStyleSettings();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsImmersiveMode))
{
@@ -236,7 +231,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontSize))
{
UpdateHintMessageFontSize();
UpdateLyricsStyleSettings();
}
}
}
@@ -247,7 +242,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontFamily))
{
LyricsFontFamily = message.NewValue;
UpdateLyricsStyleSettings();
}
}
}

View File

@@ -55,7 +55,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_mediaSessionsService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged;
_mediaSessionsService.TimelineChanged += PlaybackService_TimelineChanged;
_isPlaying = _mediaSessionsService.IsPlaying;
IsPlaying = _mediaSessionsService.IsPlaying;
UpdateColorConfig();
}

View File

@@ -147,10 +147,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var opacity = new CanvasCommandList(control.Device);
using var opacityDs = opacity.CreateDrawingSession();
opacityDs.DrawImage(new GaussianBlurEffect
opacityDs.DrawImage(new ShadowEffect
{
Source = albumArt,
BlurAmount = 12f,
ShadowColor = _grayedEnvironmentalColor,
BlurAmount = _settingsService.AppSettings.AlbumArtLayoutSettings.CoverImageShadowAmount,
Optimization = EffectOptimization.Speed,
});
opacityDs.DrawImage(albumArt);
@@ -237,7 +238,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
* Matrix3x2.CreateRotation((float)line.AngleTransition.Value, currentPlayingLine.Position)
* Matrix3x2.CreateTranslation((float)_lyricsXTransition.Value, (float)yOffset);
if (line.BackgroundFontEffect == null || line.ForegroundFontEffect == null) continue;
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor, _lyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
using var foregroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor, _lyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
using var combined = new CanvasCommandList(control.Device);
using var combinedDs = combined.CreateDrawingSession();
@@ -245,21 +247,29 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// Mock gradient blurred lyrics layer
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
// Current line will not be blurred
combinedDs.DrawImage(line.BackgroundEffect);
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
combinedDs.DrawImage(backgroundEffect);
if (line.HighlightOpacityTransition.Value != 0)
{
if (line.ForegroundBlurEffect == null || line.ForegroundHighlightEffect == null || line.PlaceholderEffect == null)
{
return;
}
GetLinePlayingProgress(i, out int charStartIndex, out int charLength, out double charProgress);
using var charMask = CanvasHelper.CreateCharMask(control, line, charStartIndex, charLength, charProgress);
using var lineStartToCharMask = CanvasHelper.CreateLineStartToCharMask(control, line, charStartIndex, charLength, charProgress);
using var lineMask = CanvasHelper.CreateLineMask(control, line);
var blurEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask, _lyricsEffectSettings.LyricsGlowEffectScope);
var highlightEffectMask = CanvasHelper.GetAlphaMask(control, charMask, lineStartToCharMask, lineMask, _lyricsEffectSettings.LyricsHighlightScope);
using var foregroundBlurEffect = CanvasHelper.CreateForegroundBlurEffect(foregroundFontEffect, blurEffectMask, _lyricsGlowEffectAmount);
using var foregroundHighlightEffect = CanvasHelper.CreateForegroundHighlightEffect(foregroundFontEffect, highlightEffectMask);
using var opacityEffect = new OpacityEffect
{
Source = new BlendEffect
{
Background = _lyricsEffectSettings.IsLyricsGlowEffectEnabled ? line.ForegroundBlurEffect : line.PlaceholderEffect,
Foreground = line.ForegroundHighlightEffect,
Background = _lyricsEffectSettings.IsLyricsGlowEffectEnabled ? foregroundBlurEffect : new CanvasCommandList(control),
Foreground = foregroundHighlightEffect,
},
Opacity = (float)(line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value),
};
@@ -273,7 +283,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
ds.DrawImage(new DisplacementMapEffect
{
Source = combined,
Displacement = line.LineStartToCurrentCharMask,
Displacement = lineStartToCharMask,
XChannelSelect = EffectChannelSelect.Red,
YChannelSelect = EffectChannelSelect.Alpha,
Amount = 1f,

View File

@@ -44,7 +44,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
_elapsedTime = args.Timing.ElapsedTime;
if (_isPlaying)
if (IsPlaying)
{
TotalTime += _elapsedTime;
_totalPlayingTime += _elapsedTime;
@@ -261,7 +261,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateVisibleLinesBoundary();
UpdateLinesProps(control);
UpdateVisibleLinesProps(control);
_isLayoutChanged = false;
@@ -322,18 +322,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
continue;
}
line.RecreatePlaceholder(control);
line.Position = new Vector2(0, (float)y);
line.RecreateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _lyricsStyleSettings.LyricsAlignmentType);
line.UpdateCenterPosition(_maxLyricsWidth, _lyricsStyleSettings.LyricsAlignmentType);
line.RecreateTextGeometry();
line.RecreateFontEffect(control, _strokeFontColor, _lyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor, _fgFontColor);
line.RecreateBackgroundEffect(_lyricsOpacityTransition.Value);
line.RecreateCurrentLineMask(control);
line.RecreateForegroundBlurEffect(control, _lyricsEffectSettings.LyricsGlowEffectScope, _lyricsGlowEffectAmount);
line.RecreateForegroundHighlightEffect(control, _lyricsEffectSettings.LyricsHighlightScope);
y +=
(double)line.CanvasTextLayout!.LayoutBounds.Height
@@ -445,7 +438,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
double brightness;
Color grayedEnvironmentalColor = Colors.Transparent;
bool isLight = ThemeTypeSent switch
{
@@ -459,14 +451,14 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
_adaptiveGrayedFontColor = _darkColor;
brightness = 0.7f;
grayedEnvironmentalColor = _lightColor;
_grayedEnvironmentalColor = _lightColor;
_albumArtAccentColorTransition.StartTransition(_albumArtLightAccentColor);
}
else
{
_adaptiveGrayedFontColor = _lightColor;
brightness = 0.3f;
grayedEnvironmentalColor = _darkColor;
_grayedEnvironmentalColor = _darkColor;
_albumArtAccentColorTransition.StartTransition(_albumArtDarkAccentColor);
}
@@ -521,7 +513,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
switch (_lyricsStyleSettings.LyricsStrokeFontColorType)
{
case LyricsFontColorType.AdaptiveGrayed:
_strokeFontColor = grayedEnvironmentalColor.WithBrightness(0.7);
_strokeFontColor = _grayedEnvironmentalColor.WithBrightness(0.7);
break;
case LyricsFontColorType.AdaptiveColored:
_strokeFontColor = _environmentalColor.WithBrightness(0.7);
@@ -536,7 +528,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLayoutChanged = true;
}
private void UpdateLinesProps(ICanvasAnimatedControl control)
private void UpdateVisibleLinesProps(ICanvasAnimatedControl control)
{
var currentPlayingLine = _lyricsDataArr
.ElementAtOrDefault(_langIndex)
@@ -550,24 +542,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (line == null) continue;
line.UpdateBackgroundEffect(_lyricsOpacityTransition.Value);
if (i == _playingLineIndex)
{
GetLinePlayingProgress(
_playingLineIndex,
out int charStartIndex,
out int charLength,
out double charProgress
);
line.RecreateCurrentCharMask(control, charStartIndex, charLength, charProgress);
line.RecreateLineStartToCurrentCharMask(control, charStartIndex, charLength, charProgress);
line.UpdateForegroundBlurEffect(control, _lyricsEffectSettings.LyricsGlowEffectScope, _lyricsGlowEffectAmount);
line.UpdateForegroundHighlightEffect(control, _lyricsEffectSettings.LyricsHighlightScope);
}
if (_isLayoutChanged || _isPlayingLineChanged)
{
int lineCountDelta = i - _playingLineIndex;

View File

@@ -115,6 +115,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private Color _albumArtLightAccentColor = Colors.Transparent;
private Color _albumArtDarkAccentColor = Colors.Transparent;
private Color _environmentalColor = Colors.Transparent;
private Color _grayedEnvironmentalColor = Colors.Transparent;
private Color _lightColor = Colors.White;
private Color _darkColor = Colors.Black;
@@ -132,7 +133,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isDesktopMode = false;
private bool _isDockMode = false;
private bool _isPlaying = true;
[ObservableProperty]
public partial bool IsPlaying { get; set; } = false;
private bool _isLyricsWindowLocked = false;
private bool _isMouseWithinWindow = false;
@@ -330,7 +332,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void PlaybackService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
{
_isPlaying = e.IsPlaying;
IsPlaying = e.IsPlaying;
}
private void PlaybackService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
@@ -430,7 +432,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
IsTranslating = true;
if (_settingsService.AppSettings.TranslationSettings.IsTranslationEnabled)
{
_ = _refreshLyricsRunner.RunAsync(async token =>
_refreshLyricsRunner.RunAsync(async token =>
{
await SetDisplayedAlongWithTranslationsAsync(token);
IsTranslating = false;
@@ -449,7 +451,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
{
_logger.LogInformation("Showing translation for lyrics...");
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode();
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
if (originalText == null) return;

View File

@@ -62,7 +62,7 @@ namespace BetterLyrics.WinUI3
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
[ObservableProperty]
@@ -101,7 +101,7 @@ namespace BetterLyrics.WinUI3
[ObservableProperty]
public partial string LockHotKey { get; set; } = "";
private void UpdateDockWindow()
private void UpdateDockOrDesktopWindow()
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
@@ -162,7 +162,7 @@ namespace BetterLyrics.WinUI3
else if (message.PropertyName == nameof(GeneralSettings.HideWindowWhenNotPlaying))
{
_hideWindowWhenNotPlaying = message.NewValue;
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
}
}
@@ -185,7 +185,7 @@ namespace BetterLyrics.WinUI3
if (message.PropertyName == nameof(DockModeSettings.DockWindowHeight))
{
_dockWindowHeight = message.NewValue;
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
}
else if (message.Sender is DesktopModeSettings)
@@ -278,7 +278,7 @@ namespace BetterLyrics.WinUI3
IsImmersiveMode = true;
}
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
[RelayCommand]
@@ -321,7 +321,7 @@ namespace BetterLyrics.WinUI3
DockModeHelper.Disable(window);
}
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
[RelayCommand]
@@ -337,7 +337,7 @@ namespace BetterLyrics.WinUI3
if (message.PropertyName == nameof(DockModeSettings.DockPlacement))
{
_dockPlacement = message.NewValue;
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
}
}
@@ -349,7 +349,7 @@ namespace BetterLyrics.WinUI3
if (message.PropertyName == nameof(DockModeSettings.DockMonitorDeviceName))
{
_dockMonitorDeviceName = message.NewValue;
UpdateDockWindow();
UpdateDockOrDesktopWindow();
}
}
}

View File

@@ -28,8 +28,8 @@
x:Uid="MainPageNoMusicPlaying"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{x:Bind ViewModel.LyricsFontFamily, Mode=OneWay}"
FontSize="{x:Bind ViewModel.HintMessageFontSize, Mode=OneWay}" />
FontFamily="{x:Bind ViewModel.LyricsStyleSettings.LyricsFontFamily, Mode=OneWay}"
FontSize="{x:Bind ViewModel.LyricsStyleSettings.LyricsFontSize, Mode=OneWay}" />
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
@@ -230,11 +230,12 @@
<Button.ContextFlyout>
<Flyout
x:Name="PlaybackSettingsFlyout"
Closed="PlaybackSettingsFlyout_Closed"
Placement="Right"
ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="MinWidth" Value="800" />
<Setter Property="MinWidth" Value="850" />
<Setter Property="Padding" Value="0" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
@@ -242,7 +243,6 @@
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
</Style>
</Flyout.FlyoutPresenterStyle>
<uc:PlaybackSettingsControl MaxWidth="800" MaxHeight="500" />
</Flyout>
</Button.ContextFlyout>
</Button>

View File

@@ -1,5 +1,6 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Controls;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
@@ -72,6 +73,11 @@ namespace BetterLyrics.WinUI3.Views
private void PlaybackSettingsShortcutButton_Click(object sender, RoutedEventArgs e)
{
PlaybackSettingsFlyout.Content = new PlaybackSettingsControl
{
MaxHeight = 500,
MaxWidth = 850,
};
PlaybackSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
}
@@ -131,5 +137,10 @@ namespace BetterLyrics.WinUI3.Views
{
_mediaSessionsService.ChangePosition(TimelineSlider.Value);
}
private void PlaybackSettingsFlyout_Closed(object sender, object e)
{
PlaybackSettingsFlyout.Content = null;
}
}
}

View File

@@ -115,21 +115,18 @@ namespace BetterLyrics.WinUI3.Views
}
else
{
App.DispatcherQueueTimer?.Debounce(() =>
if (ViewModel.IsDesktopMode)
{
_settingsService.AppSettings.DesktopModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
}
else if (ViewModel.IsDockMode)
{
if (ViewModel.IsDesktopMode)
{
_settingsService.AppSettings.DesktopModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
}
else if (ViewModel.IsDockMode)
{
}
else
{
_settingsService.AppSettings.StandardModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
}
}, Constants.Time.DebounceTimeout);
}
else
{
_settingsService.AppSettings.StandardModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
}
}
}
}