Compare commits

...

7 Commits

Author SHA1 Message Date
Zhe Fang
bfc877f924 update playback source settings structure 2025-08-10 22:38:19 -04:00
Zhe Fang
af55446004 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-08-09 12:32:36 -04:00
Zhe Fang
faf8acf669 change settings store way 2025-08-09 12:32:34 -04:00
Zhe Fang
5b79a54117 Merge pull request #78 from kusutori/dev
为列表项目添加拖动指示图标
2025-08-09 07:50:28 -04:00
kusutoriクスとり
4ef55f6ece 为列表项目添加拖动指示图标 2025-08-09 19:27:36 +08:00
Zhe Fang
0593f9aa3f add lyrics scrolling duration settings for first line and last line 2025-08-08 22:36:47 -04:00
Zhe Fang
b09a6494ff fix 2025-08-08 16:15:10 -04:00
103 changed files with 8104 additions and 7681 deletions

View File

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

View File

@@ -297,6 +297,17 @@
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
</Style> </Style>
<Style
x:Key="SettingsScrollViewerStyle"
BasedOn="{StaticResource DefaultScrollViewerStyle}"
TargetType="ScrollViewer">
<Setter Property="Padding" Value="36,0" />
</Style>
<Style x:Key="SettingsGridStyle" TargetType="Grid">
<Setter Property="Padding" Value="0,0,0,36" />
</Style>
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" /> <StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" /> <StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" /> <StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />

View File

@@ -12,7 +12,6 @@ using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService; using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel; using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel;
using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel;
using BetterLyrics.WinUI3.Views; using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -118,6 +117,12 @@ namespace BetterLyrics.WinUI3
.AddSingleton<ITranslateService, TranslateService>() .AddSingleton<ITranslateService, TranslateService>()
.AddSingleton<ILastFMService, LastFMService>() .AddSingleton<ILastFMService, LastFMService>()
// ViewModels // ViewModels
.AddSingleton<AppSettingsControlViewModel>()
.AddSingleton<LyricsBackgroundSettingsControlViewModel>()
.AddSingleton<AlbumArtLayoutSettingsControlViewModel>()
.AddSingleton<PlaybackSettingsControlViewModel>()
.AddSingleton<MediaSettingsControlViewModel>()
.AddSingleton<AllLyricsSettingsControlViewModel>()
.AddSingleton<LyricsWindowViewModel>() .AddSingleton<LyricsWindowViewModel>()
.AddSingleton<SettingsWindowViewModel>() .AddSingleton<SettingsWindowViewModel>()
.AddSingleton<SystemTrayViewModel>() .AddSingleton<SystemTrayViewModel>()

View File

@@ -22,6 +22,14 @@
<ItemGroup> <ItemGroup>
<None Remove="Assets\Segoe Fluent Icons.ttf" /> <None Remove="Assets\Segoe Fluent Icons.ttf" />
<None Remove="Assets\Wiki82.profile.xml" /> <None Remove="Assets\Wiki82.profile.xml" />
<None Remove="Controls\AlbumArtLayoutSettingsControl.xaml" />
<None Remove="Controls\AllLyricsSettingsControl.xaml" />
<None Remove="Controls\AppSettingsControl.xaml" />
<None Remove="Controls\ExtendedSlider.xaml" />
<None Remove="Controls\LyricsBavkgroundSettingsControl.xaml" />
<None Remove="Controls\LyricsSettingsControl.xaml" />
<None Remove="Controls\MediaSettingsControl.xaml" />
<None Remove="Controls\PlaybackSettingsControl.xaml" />
<None Remove="Controls\SystemTray.xaml" /> <None Remove="Controls\SystemTray.xaml" />
<None Remove="Views\MusicGalleryPage.xaml" /> <None Remove="Views\MusicGalleryPage.xaml" />
<None Remove="Views\MusicGalleryWindow.xaml" /> <None Remove="Views\MusicGalleryWindow.xaml" />
@@ -177,6 +185,46 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Controls\ExtendedSlider.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\AllLyricsSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\LyricsSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\MediaSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\PlaybackSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\AlbumArtLayoutSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\LyricsBavkgroundSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\AppSettingsControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="Views\MusicGalleryWindow.xaml"> <Page Update="Views\MusicGalleryWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View File

@@ -5,5 +5,6 @@ namespace BetterLyrics.WinUI3.Constants
public const string ApiKey = "Your api key here"; public const string ApiKey = "Your api key here";
public const string SharedSecret = "Your shared secret here"; public const string SharedSecret = "Your shared secret here";
public const string UnAuthUrl = "https://www.last.fm/settings/applications"; public const string UnAuthUrl = "https://www.last.fm/settings/applications";
public const string SessionKeyCredentialKey = "LastFMSessionKey";
} }
} }

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtLayoutSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE71A;}">
<local:ExtendedSlider
Default="12"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind ViewModel.AppSettings.AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
</controls:SettingsCard>
<TextBlock x:Uid="SettingsPageSongInfo" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageSongInfoAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageSongInfoLeft" />
<ComboBoxItem x:Uid="SettingsPageSongInfoCenter" />
<ComboBoxItem x:Uid="SettingsPageSongInfoRight" />
</ComboBox>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,32 @@
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class AlbumArtLayoutSettingsControl : UserControl
{
public AlbumArtLayoutSettingsControlViewModel ViewModel => (AlbumArtLayoutSettingsControlViewModel)DataContext;
public AlbumArtLayoutSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<AlbumArtLayoutSettingsControlViewModel>();
}
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.AllLyricsSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
mc:Ignorable="d">
<Grid>
<controls:SwitchPresenter Margin="0,72,0,0" Value="{Binding SelectedItem.Tag, ElementName=LyricsSettingsSegmentedControl}">
<controls:Case Value="Standard">
<uc:LyricsSettingsControl LyricsEffectSettings="{x:Bind ViewModel.AppSettings.StandardLyricsEffectSettings, Mode=OneWay}" LyricsStyleSettings="{x:Bind ViewModel.AppSettings.StandardLyricsStyleSettings, Mode=OneWay}" />
</controls:Case>
<controls:Case Value="Desktop">
<uc:LyricsSettingsControl LyricsEffectSettings="{x:Bind ViewModel.AppSettings.DesktopLyricsEffectSettings, Mode=OneWay}" LyricsStyleSettings="{x:Bind ViewModel.AppSettings.DesktopLyricsStyleSettings, Mode=OneWay}" />
</controls:Case>
<controls:Case Value="Dock">
<uc:LyricsSettingsControl LyricsEffectSettings="{x:Bind ViewModel.AppSettings.DockLyricsEffectSettings, Mode=OneWay}" LyricsStyleSettings="{x:Bind ViewModel.AppSettings.DockLyricsStyleSettings, Mode=OneWay}" />
</controls:Case>
</controls:SwitchPresenter>
<controls:Segmented
x:Name="LyricsSettingsSegmentedControl"
Margin="36,36,36,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
SelectedIndex="0">
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlStandard" Tag="Standard" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlDesktop" Tag="Desktop" />
<controls:SegmentedItem x:Uid="AllLyricsSettingsControlDock" Tag="Dock" />
</controls:Segmented>
</Grid>
</UserControl>

View File

@@ -0,0 +1,32 @@
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class AllLyricsSettingsControl : UserControl
{
public AllLyricsSettingsControlViewModel ViewModel => (AllLyricsSettingsControlViewModel)DataContext;
public AllLyricsSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<AllLyricsSettingsControlViewModel>();
}
}
}

View File

@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.AppSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- App appearance -->
<TextBlock x:Uid="SettingsPageAppAppearance" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Uid="SettingsPageLanguage"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF2B7;}"
IsExpanded="True">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.Language, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageSystemLanguage" />
<ComboBoxItem x:Uid="SettingsPageEN" />
<ComboBoxItem x:Uid="SettingsPageSC" />
<ComboBoxItem x:Uid="SettingsPageTC" />
<ComboBoxItem x:Uid="SettingsPageJA" />
<ComboBoxItem x:Uid="SettingsPageKO" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard>
<Button x:Uid="SettingsPageRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- App behavior -->
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF71C;}">
<ToggleSwitch
x:Name="AutoStartupToggleSwitch"
Loaded="AutoStartupToggleSwitch_Loaded"
Unloaded="AutoStartupToggleSwitch_Unloaded" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE736;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.GeneralSettings.AutoStartWindowType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageAutoStartInAppLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDockLyrics" />
<ComboBoxItem x:Uid="SettingsPageAutoStartDesktopLyrics" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE967;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreFullscreenWindow, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED1A;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.HideWindowWhenNotPlaying, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageGlobalDrag" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C2;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsDragEverywhereEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- Desktop mode -->
<TextBlock x:Uid="SettingsPageAppDesktop" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageAutoLock" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE755;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.DesktopModeSettings.AutoLockOnDesktopMode, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLockHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock
Margin="0,0,0,2"
VerticalAlignment="Center"
Text="Ctrl + Alt + " />
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DesktopModeSettings.LockHotKeyIndex, Mode=TwoWay}">
<ComboBoxItem Content="A" />
<ComboBoxItem Content="B" />
<ComboBoxItem Content="C" />
<ComboBoxItem Content="D" />
<ComboBoxItem Content="E" />
<ComboBoxItem Content="F" />
<ComboBoxItem Content="G" />
<ComboBoxItem Content="H" />
<ComboBoxItem Content="I" />
<ComboBoxItem Content="J" />
<ComboBoxItem Content="K" />
<ComboBoxItem Content="L" />
<ComboBoxItem Content="M" />
<ComboBoxItem Content="N" />
<ComboBoxItem Content="O" />
<ComboBoxItem Content="P" />
<ComboBoxItem Content="Q" />
<ComboBoxItem Content="R" />
<ComboBoxItem Content="S" />
<ComboBoxItem Content="T" />
<ComboBoxItem Content="U" />
<ComboBoxItem Content="V" />
<ComboBoxItem Content="W" />
<ComboBoxItem Content="X" />
<ComboBoxItem Content="Y" />
<ComboBoxItem Content="Z" />
</ComboBox>
</StackPanel>
</controls:SettingsCard>
<!-- Dock mode -->
<TextBlock x:Uid="SettingsPageAppDock" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageDockMonitor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7F4;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.AppSettings.DockModeSettings.DockMonitorDeviceName, Mode=TwoWay}" />
<Button
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE72C;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockWindowHeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xED5E;}">
<local:ExtendedSlider
Default="64"
Frequency="1"
Maximum="200"
Minimum="64"
Unit="px"
Value="{x:Bind ViewModel.AppSettings.DockModeSettings.DockWindowHeight, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.DockModeSettings.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
</ComboBox>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,50 @@
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class AppSettingsControl : UserControl
{
public AppSettingsControlViewModel ViewModel => (AppSettingsControlViewModel)DataContext;
public AppSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<AppSettingsControlViewModel>();
}
private async void AutoStartupToggleSwitch_Loaded(object sender, RoutedEventArgs e)
{
AutoStartupToggleSwitch.IsOn = await ViewModel.DetectIsAutoStartupEnabledAsync();
AutoStartupToggleSwitch.Toggled += AutoStartupToggleSwitch_Toggled;
}
private void AutoStartupToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
ViewModel.ToggleAutoStartupAsync(AutoStartupToggleSwitch.IsOn);
}
private void AutoStartupToggleSwitch_Unloaded(object sender, RoutedEventArgs e)
{
AutoStartupToggleSwitch.Toggled -= AutoStartupToggleSwitch_Toggled;
}
}
}

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.ExtendedSlider"
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.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="6,0,2,0"
VerticalAlignment="Center"
Text="{x:Bind Value, Mode=OneWay}" />
<TextBlock
Margin="0,0,14,0"
VerticalAlignment="Center"
Text="{x:Bind Unit, Mode=OneWay}" />
<Slider
Maximum="{x:Bind Maximum, Mode=OneWay}"
Minimum="{x:Bind Minimum, Mode=OneWay}"
SnapsTo="Ticks"
StepFrequency="{x:Bind Frequency, Mode=OneWay}"
TickFrequency="{x:Bind Frequency, Mode=OneWay}"
TickPlacement="Outside"
Value="{x:Bind Value, Mode=TwoWay}" />
<Button
Margin="3,0,0,0"
VerticalAlignment="Center"
Click="ResetButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}"
Visibility="{x:Bind ResetButtonVisibility, Mode=OneWay}" />
<Button
x:Name="SubtractButton"
Margin="3,0,0,0"
VerticalAlignment="Center"
Click="SubtractButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE738;}"
Style="{StaticResource GhostButtonStyle}" />
<Button
x:Name="AddButton"
Margin="3,0,0,0"
VerticalAlignment="Center"
Click="AddButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE710;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,129 @@
using Microsoft.UI.Input;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class ExtendedSlider : UserControl
{
public ExtendedSlider()
{
InitializeComponent();
}
private void Subtract()
{
if (Value - Frequency < Minimum)
{
Value = Minimum;
}
else
{
Value -= Frequency;
}
}
private void Add()
{
if (Value + Frequency > Maximum)
{
Value = Maximum;
}
else
{
Value += Frequency;
}
}
private void SubtractTimer_Tick(object? sender, object e)
{
Subtract();
}
private void AddTimer_Tick(object? sender, object e)
{
Add();
}
public static readonly DependencyProperty FrequencyProperty =
DependencyProperty.Register(nameof(Frequency), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register(nameof(Minimum), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register(nameof(Maximum), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(nameof(Value), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty DefaultProperty =
DependencyProperty.Register(nameof(Default), typeof(double), typeof(ExtendedSlider), new PropertyMetadata(default));
public static readonly DependencyProperty ResetButtonVisibilityProperty =
DependencyProperty.Register(nameof(ResetButtonVisibility), typeof(Visibility), typeof(ExtendedSlider), new PropertyMetadata(Visibility.Visible));
public static readonly DependencyProperty UnitProperty =
DependencyProperty.Register(nameof(Unit), typeof(string), typeof(ExtendedSlider), new PropertyMetadata(""));
public double Frequency
{
get => (double)GetValue(FrequencyProperty);
set => SetValue(FrequencyProperty, value);
}
public double Minimum
{
get => (double)GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
public double Maximum
{
get => (double)GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public double Default
{
get => (double)GetValue(DefaultProperty);
set => SetValue(DefaultProperty, value);
}
public Visibility ResetButtonVisibility
{
get => (Visibility)GetValue(ResetButtonVisibilityProperty);
set => SetValue(ResetButtonVisibilityProperty, value);
}
public string Unit
{
get => (string)GetValue(UnitProperty);
set => SetValue(UnitProperty, value);
}
private void ResetButton_Click(object sender, RoutedEventArgs e)
{
Value = Default;
}
private void SubtractButton_Click(object sender, RoutedEventArgs e)
{
Subtract();
}
private void AddButton_Click(object sender, RoutedEventArgs e)
{
Add();
}
}
}

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.LyricsBavkgroundSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE790;}">
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageFollowSystem" />
<ComboBoxItem x:Uid="SettingsPageLight" />
<ComboBoxItem x:Uid="SettingsPageDark" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsPureColorBgOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF573;}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.PureColorOverlayOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF573;}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.CoverOverlayOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundSpeed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC49;}">
<uc:ExtendedSlider
Default="50"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.CoverOverlaySpeed, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBackgroundBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE727;}">
<uc:ExtendedSlider
Default="100"
Frequency="1"
Maximum="100"
Minimum="0"
Value="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.CoverOverlayBlurAmount, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageBackgroundAcrylicEffectAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE727;}">
<uc:ExtendedSlider
Default="0"
Frequency="1"
Maximum="10"
Minimum="0"
Value="{x:Bind ViewModel.AppSettings.LyricsBackgroundSettings.CoverAcrylicEffectAmount, Mode=TwoWay}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,33 @@
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class LyricsBavkgroundSettingsControl : UserControl
{
public LyricsBackgroundSettingsControlViewModel ViewModel => (LyricsBackgroundSettingsControlViewModel)DataContext;
public LyricsBavkgroundSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<LyricsBackgroundSettingsControlViewModel>();
}
}
}

View File

@@ -0,0 +1,323 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.LyricsSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- Lyrics style -->
<TextBlock x:Uid="SettingsPageLyricsStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard x:Uid="SettingsPageLyricsAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E3;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsLeft" />
<ComboBoxItem x:Uid="SettingsPageLyricsCenter" />
<ComboBoxItem x:Uid="SettingsPageLyricsRight" />
</ComboBox>
</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.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontWeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8DD;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsFontWeight, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsThin" />
<ComboBoxItem x:Uid="SettingsPageLyricsExtraLight" />
<ComboBoxItem x:Uid="SettingsPageLyricsLight" />
<ComboBoxItem x:Uid="SettingsPageLyricsSemiLight" />
<ComboBoxItem x:Uid="SettingsPageLyricsNormal" />
<ComboBoxItem x:Uid="SettingsPageLyricsMedium" />
<ComboBoxItem x:Uid="SettingsPageLyricsSemiBold" />
<ComboBoxItem x:Uid="SettingsPageLyricsBold" />
<ComboBoxItem x:Uid="SettingsPageLyricsExtraBold" />
<ComboBoxItem x:Uid="SettingsPageLyricsBlack" />
<ComboBoxItem x:Uid="SettingsPageLyricsExtraBlack" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE799;}">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsStyleSettings.LyricsBgFontOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC12;}">
<local:ExtendedSlider
Default="0"
Frequency="1"
Maximum="5"
Minimum="0"
Value="{x:Bind LyricsStyleSettings.LyricsFontStrokeWidth, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsStrokeFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D3;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsStrokeFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
<ColorPicker
ColorSpectrumShape="Box"
IsAlphaEnabled="True"
IsAlphaSliderVisible="True"
IsAlphaTextInputVisible="True"
IsColorChannelTextInputVisible="True"
IsColorSliderVisible="True"
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomStrokeFontColor, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsStrokeFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="Equal"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsStrokeFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="NotEqual"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ColorPicker>
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D3;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsBgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
<ColorPicker
ColorSpectrumShape="Box"
IsAlphaEnabled="True"
IsAlphaSliderVisible="True"
IsAlphaTextInputVisible="True"
IsColorChannelTextInputVisible="True"
IsColorSliderVisible="True"
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomBgFontColor, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsBgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="Equal"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsBgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="NotEqual"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ColorPicker>
<controls:SettingsCard x:Uid="SettingsPageLyricsFgFontColor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D3;}">
<ComboBox SelectedIndex="{x:Bind LyricsStyleSettings.LyricsFgFontColorType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveColored" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorAdaptiveGrayed" />
<ComboBoxItem x:Uid="SettingsPageLyricsFontColorCustom" />
</ComboBox>
</controls:SettingsCard>
<ColorPicker
ColorSpectrumShape="Box"
IsAlphaEnabled="True"
IsAlphaSliderVisible="True"
IsAlphaTextInputVisible="True"
IsColorChannelTextInputVisible="True"
IsColorSliderVisible="True"
IsHexInputVisible="True"
IsMoreButtonVisible="True"
Color="{x:Bind LyricsStyleSettings.LyricsCustomFgFontColor, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsFgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="Equal"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind LyricsStyleSettings.LyricsFgFontColorType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}"
ComparisonCondition="NotEqual"
Value="2">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ColorPicker>
<controls:SettingsCard x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8E9;}">
<local:ExtendedSlider
Frequency="2"
Maximum="96"
Minimum="12"
ResetButtonVisibility="Collapsed"
Value="{x:Bind LyricsStyleSettings.LyricsFontSize, Mode=TwoWay}" />
</controls:SettingsCard>
<controls: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}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsTranslationSeparator" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF57B;}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox AcceptsReturn="True" Text="{x:Bind LyricsStyleSettings.LyricsTranslationSeparator, Mode=TwoWay}" />
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=&#xE8FB;}" Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<!-- Effect -->
<TextBlock
x:Uid="SettingsPageLyricsEffect"
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="Effect" />
<controls:SettingsCard x:Uid="SettingsPageLyricsVerticalEdgeOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF573;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsVerticalEdgeOpacitySlider"
Default="0"
Frequency="1"
Maximum="100"
Minimum="0"
Unit="%"
Value="{x:Bind LyricsEffectSettings.LyricsVerticalEdgeOpacity, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsBlurAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE727;}">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsBlurAmountExtendedSlider"
Default="5"
Frequency="1"
Maximum="10"
Minimum="0"
Value="{x:Bind LyricsEffectSettings.LyricsBlurAmount, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLyricsHighlightScope" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7E6;}">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsHighlightScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPageLyricsGlowEffect"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE9A9;}"
IsExpanded="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentLine" />
</ComboBox>
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8C5;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageFan" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEBC5;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsFanLyricsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsExpander
x:Uid="SettingsPageScrollEasing"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xECE7;}"
IsExpanded="True">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeSmoothStep" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutSine" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuad" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutCubic" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuart" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutQuint" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutExpo" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutCirc" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutBack" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutElastic" />
<ComboBoxItem x:Uid="SettingsPageEasingTypeEaseInOutBounce" />
</ComboBox>
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageScrollTopDuration">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsScrollTopDurationExtendedSlider"
Default="500"
Frequency="50"
Maximum="1000"
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDuration, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollDuration">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsScrollDurationExtendedSlider"
Default="500"
Frequency="50"
Maximum="1000"
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollDuration, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageScrollBottomDuration">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsScrollBottomDurationExtendedSlider"
Default="500"
Frequency="50"
Maximum="1000"
Minimum="50"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,53 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
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;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class LyricsSettingsControl : UserControl
{
public ObservableCollection<string> SystemFontNames { get; set; } = [.. FontHelper.SystemFontFamilies];
public LyricsSettingsControl()
{
InitializeComponent();
}
public static readonly DependencyProperty LyricsStyleSettingsProperty =
DependencyProperty.Register(nameof(LyricsStyleSettings), typeof(LyricsStyleSettings), typeof(LyricsSettingsControl), new PropertyMetadata(default));
public LyricsStyleSettings LyricsStyleSettings
{
get => (LyricsStyleSettings)GetValue(LyricsStyleSettingsProperty);
set => SetValue(LyricsStyleSettingsProperty, value);
}
public static readonly DependencyProperty LyricsEffectSettingsProperty =
DependencyProperty.Register(nameof(LyricsEffectSettings), typeof(LyricsEffectSettings), typeof(LyricsSettingsControl), new PropertyMetadata(default));
public LyricsEffectSettings LyricsEffectSettings
{
get => (LyricsEffectSettings)GetValue(LyricsEffectSettingsProperty);
set => SetValue(LyricsEffectSettingsProperty, value);
}
}
}

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.MediaSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid>
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Uid="SettingsPageMusicLib"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8B7;}"
IsExpanded="True"
ItemsSource="{x:Bind ViewModel.AppSettings.LocalMediaFolders, Mode=OneWay}">
<controls:SettingsExpander.ItemTemplate>
<DataTemplate>
<controls:SettingsCard>
<controls:SettingsCard.Header>
<HyperlinkButton
Click="LocalFolderHyperlinkButton_Click"
Content="{Binding Path, Mode=OneWay}"
Tag="{Binding Path, Mode=OneWay}" />
</controls:SettingsCard.Header>
<StackPanel Orientation="Horizontal">
<HyperlinkButton
x:Uid="SettingsPageRemovePath"
Click="SettingsPageRemovePathButton_Click"
Tag="{Binding}" />
<ToggleSwitch DataContext="{Binding}" IsOn="{Binding IsEnabled, Mode=TwoWay}" />
</StackPanel>
</controls:SettingsCard>
</DataTemplate>
</controls:SettingsExpander.ItemTemplate>
<controls:SettingsExpander.ItemsHeader>
<InfoBar
x:Uid="SettingsPageRemoveInfo"
BorderThickness="0"
CornerRadius="0"
IsClosable="False"
IsOpen="True"
Severity="Success">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.LocalMediaFolders.Count, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.LocalMediaFolders.Count, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</InfoBar>
</controls:SettingsExpander.ItemsHeader>
<controls:SettingsExpander.ItemsFooter>
<controls:SettingsCard x:Uid="SettingsPageAddFolder" Style="{StaticResource DefaultSettingsExpanderItemStyle}">
<Button
x:Uid="SettingsPageAddFolderButton"
Command="{x:Bind ViewModel.SelectAndAddFolderCommand}"
CommandParameter="{Binding ElementName=RootGrid}" />
</controls:SettingsCard>
</controls:SettingsExpander.ItemsFooter>
</controls:SettingsExpander>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,50 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
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 Windows.System;
// 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.Controls
{
public sealed partial class MediaSettingsControl : UserControl
{
public MediaSettingsControlViewModel ViewModel => (MediaSettingsControlViewModel)DataContext;
public MediaSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<MediaSettingsControlViewModel>();
}
private void SettingsPageRemovePathButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
ViewModel.RemoveFolderAsync((LocalMediaFolder)(sender as HyperlinkButton)!.Tag);
}
private async void LocalFolderHyperlinkButton_Click(object sender, RoutedEventArgs e)
{
if (sender is HyperlinkButton button && button.Tag is string uriStr)
{
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri))
{
await Launcher.LaunchUriAsync(uri);
}
}
}
}
}

View File

@@ -0,0 +1,320 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.PlaybackSettingsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid ColumnSpacing="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<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}">
<controls:SettingsCard x:Uid="SettingsPageMediaSourceProvidersConfig">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMTrack" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsLastFMTrackEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<!-- 时间轴相关配置 -->
<controls:SettingsCard x:Uid="SettingsPageLyricsTimelineThreshold">
<local:ExtendedSlider
x:Uid="SettingsPageLyricsTimelineThresholdReset"
Frequency="100"
Maximum="1000"
Minimum="0"
ResetButtonVisibility="Collapsed"
Unit="ms"
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.TimelineSyncThreshold, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsExpander x:Uid="MainPagePositionOffsetSlider" IsExpanded="True">
<local:ExtendedSlider
x:Uid="SettingsPagePositionOffsetReset"
Default="0"
Frequency="100"
Maximum="5000"
Minimum="-5000"
Unit="ms"
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.PositionOffset, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="LyricsPagePositionOffsetHint">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.ResetPositionOffsetOnSongChanged, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- 专辑封面源配置 -->
<TextBlock x:Uid="SettingsPageAlbumArtSearchProvidersConfig" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<ListView
x:Name="AlbumArtSearchProvidersListView"
AllowDrop="True"
CanDragItems="True"
CanReorderItems="True"
DragItemsCompleted="AlbumArtSearchProvidersListView_DragItemsCompleted"
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
ItemsSource="{x:Bind ViewModel.SelectedMediaSourceProvider.AlbumArtSearchProvidersInfo, Mode=OneWay}"
SelectionMode="None">
<ListView.OpacityTransition>
<ScalarTransition />
</ListView.OpacityTransition>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:AlbumArtSearchProviderInfo">
<controls:SettingsCard Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<controls:SettingsCard.HeaderIcon>
<FontIcon FontFamily="Segoe UI Symbol" Glyph="&#x283F;" />
</controls:SettingsCard.HeaderIcon>
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- 歌词源配置 -->
<TextBlock x:Uid="SettingsPageLyricsSearchProvidersConfig" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<ListView
x:Name="LyricsSearchProvidersListView"
AllowDrop="True"
CanDragItems="True"
CanReorderItems="True"
DragItemsCompleted="LyricsSearchProvidersListView_DragItemsCompleted"
ItemsSource="{x:Bind ViewModel.SelectedMediaSourceProvider.LyricsSearchProvidersInfo, Mode=OneWay}"
SelectionMode="None">
<ListView.OpacityTransition>
<ScalarTransition />
</ListView.OpacityTransition>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
<controls:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
<controls:SettingsCard.HeaderIcon>
<FontIcon FontFamily="Segoe UI Symbol" Glyph="&#x283F;" />
</controls:SettingsCard.HeaderIcon>
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</ScrollViewer>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo.Count, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo.Count, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Grid>
<StackPanel
Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="12">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo.Count, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo.Count, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<Image MaxWidth="200" Source="/Assets/Leaf.png" />
<TextBlock
x:Uid="SettingsPagePlaybackNotFound"
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
<Grid Grid.Column="1">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<!-- Last.fm -->
<TextBlock x:Uid="SettingsPageLastFM" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Uid="SettingsPageLastFMManager"
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/LastFM.png}"
IsExpanded="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<Button
x:Uid="SettingsPageLastFMAuth"
Command="{x:Bind ViewModel.LastFMAuthCommand}"
IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
<Button
x:Uid="SettingsPageLastFMUnAuth"
Command="{x:Bind ViewModel.LastFMUnAuthCommand}"
IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}" />
</StackPanel>
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageLastFMUsername" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<HyperlinkButton Content="{x:Bind ViewModel.LastFMUser.Name, Mode=OneWay}" NavigateUri="{x:Bind ViewModel.LastFMUser.Url, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMPlaycount" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Playcount, Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLastFMRegistered" IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<TextBlock Text="{x:Bind ViewModel.LastFMUser.Registered.ToLongDateString(), Mode=OneWay}" />
</controls:SettingsCard>
<controls:SettingsCard IsEnabled="{x:Bind ViewModel.IsLastFMAuthenticated, Mode=OneWay}">
<Button x:Uid="SettingsPageLastFMRefresh" Command="{x:Bind ViewModel.LastFMRefreshCommand}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<!-- Lyrics translation -->
<TextBlock x:Uid="SettingsPageTranslation" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsExpander
x:Uid="LyricsPageTranslationEnabled"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE774;}"
IsExpanded="True">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.TranslationSettings.SelectedTargetLanguageIndex, Mode=TwoWay}">
<ComboBoxItem Content="العربية" Tag="ar" />
<ComboBoxItem Content="Azərbaycan dili" Tag="az" />
<ComboBoxItem Content="简体中文" Tag="zh-Hans" />
<ComboBoxItem Content="繁體中文" Tag="zh-Hant" />
<ComboBoxItem Content="Čeština" Tag="cs" />
<ComboBoxItem Content="Dansk" Tag="da" />
<ComboBoxItem Content="Nederlands" Tag="nl" />
<ComboBoxItem Content="English" Tag="en" />
<ComboBoxItem Content="Esperanto" Tag="eo" />
<ComboBoxItem Content="Suomi" Tag="fi" />
<ComboBoxItem Content="Français" Tag="fr" />
<ComboBoxItem Content="Deutsch" Tag="de" />
<ComboBoxItem Content="Ελληνικά" Tag="el" />
<ComboBoxItem Content="עברית" Tag="he" />
<ComboBoxItem Content="हिन्दी" Tag="hi" />
<ComboBoxItem Content="Magyar" Tag="hu" />
<ComboBoxItem Content="Bahasa Indonesia" Tag="id" />
<ComboBoxItem Content="Gaeilge" Tag="ga" />
<ComboBoxItem Content="Italiano" Tag="it" />
<ComboBoxItem Content="日本語" Tag="ja" />
<ComboBoxItem Content="한국어" Tag="ko" />
<ComboBoxItem Content="فارسی" Tag="fa" />
<ComboBoxItem Content="Polski" Tag="pl" />
<ComboBoxItem Content="Português" Tag="pt" />
<ComboBoxItem Content="Русский" Tag="ru" />
<ComboBoxItem Content="Slovenčina" Tag="sk" />
<ComboBoxItem Content="Español" Tag="es" />
<ComboBoxItem Content="Svenska" Tag="sv" />
<ComboBoxItem Content="Türkçe" Tag="tr" />
<ComboBoxItem Content="Українська" Tag="uk" />
<ComboBoxItem Content="Tiếng Việt" Tag="vi" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslationConfig">
<controls:SettingsCard.Description>
<HyperlinkButton Margin="0,6,0,0" NavigateUri="https://github.com/LibreTranslate/LibreTranslate">
<TextBlock
x:Uid="SettingsPageTranslationInfoLink"
FontSize="14"
TextWrapping="Wrap" />
</HyperlinkButton>
</controls:SettingsCard.Description>
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=TwoWay}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsLibreTranslateEnabled, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="12">
<TextBox
x:Name="LibreTranslateServerTextBox"
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
PlaceholderText="http://localhost:5000"
Text="{x:Bind ViewModel.AppSettings.TranslationSettings.LibreTranslateServer, Mode=TwoWay}" />
<Button
x:Uid="SettingsPageServerTestButton"
Command="{x:Bind ViewModel.LibreTranslateServerTestCommand}"
IsEnabled="{x:Bind ViewModel.IsLibreTranslateServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsPageTranslationOnly" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.ShowTranslationOnly, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsPageTranslationProviderPrefix">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
</controls:SettingsCard>
<!-- LX music server -->
<TextBlock x:Uid="SettingsPageLXMusicServer" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<controls:SettingsCard>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
PlaceholderText="http://127.0.0.1:23330"
Text="{x:Bind ViewModel.AppSettings.GeneralSettings.LXMusicServer, Mode=TwoWay}" />
<Button
x:Uid="SettingsPageServerTestButton"
Command="{x:Bind ViewModel.LXMusicServerTestCommand}"
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,45 @@
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
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;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Controls
{
public sealed partial class PlaybackSettingsControl : UserControl
{
public PlaybackSettingsControlViewModel ViewModel => (PlaybackSettingsControlViewModel)DataContext;
public PlaybackSettingsControl()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<PlaybackSettingsControlViewModel>();
}
private void AlbumArtSearchProvidersListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
{
// <20><> AlbumArtSearchProvidersInfo <20><><EFBFBD><EFBFBD> CollectionChanged <20>¼<EFBFBD>
ViewModel.SelectedMediaSourceProvider?.AlbumArtSearchProvidersInfo?.Refresh();
}
private void LyricsSearchProvidersListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
{
// <20><> LyricsSearchProvidersInfo <20><><EFBFBD><EFBFBD> CollectionChanged <20>¼<EFBFBD>
ViewModel.SelectedMediaSourceProvider?.LyricsSearchProvidersInfo?.Refresh();
}
}
}

View File

@@ -15,9 +15,9 @@ namespace BetterLyrics.WinUI3.Converter
return provider switch return provider switch
{ {
TranslationSearchProvider.LrcLib => "LrcLib", TranslationSearchProvider.LrcLib => "LrcLib",
TranslationSearchProvider.QQ => "QQ", TranslationSearchProvider.QQ => "QQ 音乐",
TranslationSearchProvider.Netease => "Netease", TranslationSearchProvider.Netease => "网易云音乐",
TranslationSearchProvider.Kugou => "Kugou", TranslationSearchProvider.Kugou => "酷狗音乐",
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db", TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
TranslationSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"), TranslationSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
TranslationSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"), TranslationSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Enums
{
public enum SettingsStoreType
{
Container,
JSON
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Extensions
{
// https://stackoverflow.com/a/32013610/11048731
public class FullyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property is changed within an item.
/// </summary>
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public FullyObservableCollection() : base()
{ }
public FullyObservableCollection(List<T> list) : base(list)
{
ObserveAll();
}
public FullyObservableCollection(IEnumerable<T> enumerable) : base(enumerable)
{
ObserveAll();
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove ||
e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (T item in e.OldItems)
item.PropertyChanged -= ChildPropertyChanged;
}
if (e.Action == NotifyCollectionChangedAction.Add ||
e.Action == NotifyCollectionChangedAction.Replace)
{
foreach (T item in e.NewItems)
item.PropertyChanged += ChildPropertyChanged;
}
base.OnCollectionChanged(e);
}
protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ItemPropertyChanged?.Invoke(this, e);
}
protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
{
OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
}
protected override void ClearItems()
{
foreach (T item in Items)
item.PropertyChanged -= ChildPropertyChanged;
base.ClearItems();
}
private void ObserveAll()
{
foreach (T item in Items)
item.PropertyChanged += ChildPropertyChanged;
}
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
{
T typedSender = (T)sender;
int i = Items.IndexOf(typedSender);
if (i < 0)
throw new ArgumentException("Received property notification from item not in collection");
OnItemPropertyChanged(i, e);
}
public void Refresh()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
/// <summary>
/// Provides data for the <see cref="FullyObservableCollection{T}.ItemPropertyChanged"/> event.
/// </summary>
public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
{
/// <summary>
/// Gets the index in the collection for which the property change has occurred.
/// </summary>
/// <value>
/// Index in parent collection.
/// </value>
public int CollectionIndex { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
/// </summary>
/// <param name="index">The index in the collection of changed item.</param>
/// <param name="name">The name of the property that changed.</param>
public ItemPropertyChangedEventArgs(int index, string name) : base(name)
{
CollectionIndex = index;
}
/// <summary>
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
/// </summary>
/// <param name="index">The index.</param>
/// <param name="args">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
{ }
}
}

View File

@@ -0,0 +1,33 @@
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
namespace BetterLyrics.WinUI3.Helper
{
public class CanvasHelper
{
public static CanvasLinearGradientBrush CreateHorizontalFillBrush(
ICanvasAnimatedControl control,
List<(double position, double opacity)> stops,
double startX,
double width
)
{
return new CanvasLinearGradientBrush(control, stops.Select(stops => new CanvasGradientStop
{
Position = (float)stops.position,
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
}).ToArray())
{
StartPoint = new Vector2((float)startX, 0),
EndPoint = new Vector2((float)(startX + width), 0),
};
}
}
}

View File

@@ -52,7 +52,7 @@ namespace BetterLyrics.WinUI3.Helper
return Color.FromArgb(255, fg.R, fg.G, fg.B); return Color.FromArgb(255, fg.R, fg.G, fg.B);
} }
public static Color GetInterpolatedColor(float progress, Color startColor, Color targetColor) public static Color GetInterpolatedColor(double progress, Color startColor, Color targetColor)
{ {
byte Lerp(byte a, byte b) => (byte)(a + (progress * (b - a))); byte Lerp(byte a, byte b) => (byte)(a + (progress * (b - a)));
return Color.FromArgb( return Color.FromArgb(

View File

@@ -67,21 +67,8 @@ namespace BetterLyrics.WinUI3.Helper
); );
} }
// <20>Ӵ洢<D3B4><E6B4A2><EFBFBD><EFBFBD>ȡĿ<C8A1><C4BF><EFBFBD><EFBFBD><EFBFBD>ߺ<EFBFBD>λ<EFBFBD><CEBB>
int targetWidth = _settingsService.DesktopWindowWidth;
int targetHeight = _settingsService.DesktopWindowHeight;
int targetX = _settingsService.DesktopWindowLeft;
int targetY = _settingsService.DesktopWindowTop;
// <20><><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD><D0A1>λ<EFBFBD><CEBB> // <20><><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD><D0A1>λ<EFBFBD><CEBB>
window.AppWindow.MoveAndResize( window.AppWindow.MoveAndResize(_settingsService.AppSettings.DesktopModeSettings.WindowBounds.ToRectInt32());
new Windows.Graphics.RectInt32(
targetX,
targetY,
targetWidth,
targetHeight
)
);
// <20><><EFBFBD><EFBFBD>ԭTopMost״̬ // <20><><EFBFBD><EFBFBD>ԭTopMost״̬
if (!_originalTopmostStates.ContainsKey(hwnd)) if (!_originalTopmostStates.ContainsKey(hwnd))

View File

@@ -78,7 +78,7 @@ namespace BetterLyrics.WinUI3.Helper
RegisterAppBar(hwnd, monitorDeviceName, appBarHeight, dockPlacement); RegisterAppBar(hwnd, monitorDeviceName, appBarHeight, dockPlacement);
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(_settingsService.DockMonitorDeviceName); var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(_settingsService.AppSettings.DockModeSettings.DockMonitorDeviceName);
int screenWidth = monitorInfo.rcMonitor.Width; int screenWidth = monitorInfo.rcMonitor.Width;
int screenHeight = monitorInfo.rcMonitor.Bottom - monitorInfo.rcMonitor.Top; int screenHeight = monitorInfo.rcMonitor.Bottom - monitorInfo.rcMonitor.Top;

View File

@@ -10,67 +10,67 @@ namespace BetterLyrics.WinUI3.Helper
{ {
public class EasingHelper public class EasingHelper
{ {
public static float EaseInOutSine(float t) public static double EaseInOutSine(double t)
{ {
return -(MathF.Cos(MathF.PI * t) - 1f) / 2f; return -(Math.Cos(Math.PI * t) - 1f) / 2f;
} }
public static float EaseInOutQuad(float t) public static double EaseInOutQuad(double t)
{ {
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t; return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
} }
public static float EaseInOutCubic(float t) public static double EaseInOutCubic(double t)
{ {
return t < 0.5f ? 4 * t * t * t : 1 - MathF.Pow(-2 * t + 2, 3) / 2; return t < 0.5f ? 4 * t * t * t : 1 - Math.Pow(-2 * t + 2, 3) / 2;
} }
public static float EaseInOutQuart(float t) public static double EaseInOutQuart(double t)
{ {
return t < 0.5f ? 8 * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 4) / 2; return t < 0.5f ? 8 * t * t * t * t : 1 - Math.Pow(-2 * t + 2, 4) / 2;
} }
public static float EaseInOutQuint(float t) public static double EaseInOutQuint(double t)
{ {
return t < 0.5f ? 16 * t * t * t * t * t : 1 - MathF.Pow(-2 * t + 2, 5) / 2; return t < 0.5f ? 16 * t * t * t * t * t : 1 - Math.Pow(-2 * t + 2, 5) / 2;
} }
public static float EaseInOutExpo(float t) public static double EaseInOutExpo(double t)
{ {
return t == 0 return t == 0
? 0 ? 0
: t == 1 : t == 1
? 1 ? 1
: t < 0.5 ? MathF.Pow(2, 20 * t - 10) / 2 : t < 0.5 ? Math.Pow(2, 20 * t - 10) / 2
: (2 - MathF.Pow(2, -20 * t + 10)) / 2; : (2 - Math.Pow(2, -20 * t + 10)) / 2;
} }
public static float EaseInOutCirc(float t) public static double EaseInOutCirc(double t)
{ {
return t < 0.5f return t < 0.5f
? (1 - MathF.Sqrt(1 - MathF.Pow(2 * t, 2))) / 2 ? (1 - Math.Sqrt(1 - Math.Pow(2 * t, 2))) / 2
: (MathF.Sqrt(1 - MathF.Pow(-2 * t + 2, 2)) + 1) / 2; : (Math.Sqrt(1 - Math.Pow(-2 * t + 2, 2)) + 1) / 2;
} }
public static float EaseInOutBack(float t) public static double EaseInOutBack(double t)
{ {
float c1 = 1.70158f; double c1 = 1.70158f;
float c2 = c1 * 1.525f; double c2 = c1 * 1.525f;
return t < 0.5 return t < 0.5
? (MathF.Pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2 ? (Math.Pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
: (MathF.Pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2; : (Math.Pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;
} }
public static float EaseInOutElastic(float t) public static double EaseInOutElastic(double t)
{ {
if (t == 0 || t == 1) return t; if (t == 0 || t == 1) return t;
float p = 0.3f; double p = 0.3f;
float s = p / 4; double s = p / 4;
return t < 0.5f return t < 0.5f
? -(MathF.Pow(2, 20 * t - 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2 ? -(Math.Pow(2, 20 * t - 10) * Math.Sin((20 * t - 11.125f) * (2 * Math.PI) / p)) / 2
: (MathF.Pow(2, -20 * t + 10) * MathF.Sin((20 * t - 11.125f) * (2 * MathF.PI) / p)) / 2 + 1; : (Math.Pow(2, -20 * t + 10) * Math.Sin((20 * t - 11.125f) * (2 * Math.PI) / p)) / 2 + 1;
} }
private static float EaseOutBounce(float t) private static double EaseOutBounce(double t)
{ {
if (t < 4 / 11f) if (t < 4 / 11f)
{ {
@@ -90,7 +90,7 @@ namespace BetterLyrics.WinUI3.Helper
} }
} }
public static float EaseInOutBounce(float t) public static double EaseInOutBounce(double t)
{ {
if (t < 0.5f) if (t < 0.5f)
{ {
@@ -102,17 +102,17 @@ namespace BetterLyrics.WinUI3.Helper
} }
} }
public static float SmoothStep(float t) public static double SmoothStep(double t)
{ {
return t * t * (3f - 2f * t); return t * t * (3f - 2f * t);
} }
public static float CubicBezier(float t, float p0, float p1, float p2, float p3) public static double CubicBezier(double t, double p0, double p1, double p2, double p3)
{ {
float u = 1 - t; double u = 1 - t;
return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3; return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
} }
public static float Linear(float t) => t; public static double Linear(double t) => t;
} }
} }

View File

@@ -69,7 +69,7 @@ namespace BetterLyrics.WinUI3.Helper
public static bool IsSwitchableNormalizedMatch(string fileName, string q1, string q2) public static bool IsSwitchableNormalizedMatch(string fileName, string q1, string q2)
{ {
var normFileName = StringHelper.Normalize(fileName.Normalize()); var normFileName = StringHelper.Normalize(fileName);
var normQ1 = StringHelper.Normalize(q1); var normQ1 = StringHelper.Normalize(q1);
var normQ2 = StringHelper.Normalize(q2); var normQ2 = StringHelper.Normalize(q2);

View File

@@ -15,6 +15,6 @@ namespace BetterLyrics.WinUI3.Helper
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies(); public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies();
public static string GetUserPreferredFontFamily() => SystemFontFamilies.ElementAtOrDefault(_settingsService.SelectedFontFamilyIndex) ?? "Segoe UI"; public static string GetUserPreferredFontFamily() => SystemFontFamilies.ElementAtOrDefault(_settingsService.AppSettings.StandardLyricsStyleSettings.SelectedFontFamilyIndex) ?? "Segoe UI";
} }
} }

View File

@@ -138,7 +138,7 @@ namespace BetterLyrics.WinUI3.Helper
return buffer; return buffer;
} }
public static float GetAverageLuminance(CanvasBitmap bitmap) public static double GetAverageLuminance(CanvasBitmap bitmap)
{ {
var pixels = bitmap.GetPixelBytes(); var pixels = bitmap.GetPixelBytes();
double sum = 0; double sum = 0;
@@ -152,7 +152,7 @@ namespace BetterLyrics.WinUI3.Helper
double y = 0.299 * r + 0.587 * g + 0.114 * b; double y = 0.299 * r + 0.587 * g + 0.114 * b;
sum += y / 255.0; sum += y / 255.0;
} }
return (float)(sum / (pixels.Length / 4)); return (double)(sum / (pixels.Length / 4));
} }
public static byte[] MakeSquareWithThemeColor(byte[] imageBytes) public static byte[] MakeSquareWithThemeColor(byte[] imageBytes)
@@ -185,7 +185,7 @@ namespace BetterLyrics.WinUI3.Helper
{ {
using (Image image = Image.Load(imageBytes)) using (Image image = Image.Load(imageBytes))
{ {
var factor = Math.Max((float)size / image.Width, (float)size / image.Height); var factor = Math.Max((double)size / image.Width, (double)size / image.Height);
int width = (int)(image.Width * factor); int width = (int)(image.Width * factor);
int height = (int)(image.Height * factor); int height = (int)(image.Height * factor);

View File

@@ -101,7 +101,7 @@ namespace BetterLyrics.WinUI3.Services
public static string GetUserTargetLanguageCode() public static string GetUserTargetLanguageCode()
{ {
return SupportedTargetLanguages[_settingsService.SelectedTargetLanguageIndex].Code; return SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
} }
public static int GetDefaultTargetLanguageIndex() public static int GetDefaultTargetLanguageIndex()

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Security.Credentials;
namespace BetterLyrics.WinUI3.Helper
{
public static class PasswordVaultHelper
{
/// <summary>
/// 保存敏感数据到 PasswordVault
/// </summary>
/// <param name="resource">资源标识,比如 "MyApp"</param>
/// <param name="key">键名,比如 "SessionKey"</param>
/// <param name="value">要保存的值</param>
public static void Save(string resource, string key, string value)
{
var vault = new PasswordVault();
// 删除旧值(避免重复存储)
try
{
var oldCredential = vault.Retrieve(resource, key);
if (oldCredential != null)
{
vault.Remove(oldCredential);
}
}
catch
{
// 没有旧值就忽略
}
vault.Add(new PasswordCredential(resource, key, value));
}
/// <summary>
/// 从 PasswordVault 获取敏感数据
/// </summary>
/// <param name="resource">资源标识</param>
/// <param name="key">键名</param>
/// <returns>存储的值,若不存在则返回 null</returns>
public static string? Get(string resource, string key)
{
var vault = new PasswordVault();
try
{
var credential = vault.Retrieve(resource, key);
credential.RetrievePassword();
return credential.Password;
}
catch
{
return null;
}
}
/// <summary>
/// 删除指定的敏感数据
/// </summary>
public static void Delete(string resource, string key)
{
var vault = new PasswordVault();
try
{
var credential = vault.Retrieve(resource, key);
vault.Remove(credential);
}
catch
{
// 不存在就忽略
}
}
}
}

View File

@@ -11,11 +11,13 @@ namespace BetterLyrics.WinUI3.Helper
{ {
public class PathHelper public class PathHelper
{ {
private static string LocalFolder => ApplicationData.Current.LocalFolder.Path; public static string LocalFolder => ApplicationData.Current.LocalFolder.Path;
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path; public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets"); public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
//public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Core14.profile.xml"); public static string SettingsDirectory => Path.Combine(LocalFolder, "settings");
public static string SettingsFilePath => Path.Combine(SettingsDirectory, "settings.json");
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml"); public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml");
public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico"); public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico");
public static string AIMPLogoPath => Path.Combine(AssetsFolder, "AIMP.png"); public static string AIMPLogoPath => Path.Combine(AssetsFolder, "AIMP.png");
@@ -58,6 +60,8 @@ namespace BetterLyrics.WinUI3.Helper
public static void EnsureDirectories() public static void EnsureDirectories()
{ {
Directory.CreateDirectory(SettingsDirectory);
Directory.CreateDirectory(LogDirectory); Directory.CreateDirectory(LogDirectory);
Directory.CreateDirectory(LrcLibLyricsCacheDirectory); Directory.CreateDirectory(LrcLibLyricsCacheDirectory);

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Helper
{
public static class RectHelper
{
public static RectInt32 ToRectInt32(this Windows.Foundation.Rect rect)
{
return new RectInt32(
(int)rect.X,
(int)rect.Y,
(int)rect.Width,
(int)rect.Height
);
}
}
}

View File

@@ -38,7 +38,7 @@ namespace BetterLyrics.WinUI3.Helper
{ {
if (_endpointVolume != null) if (_endpointVolume != null)
{ {
float level = _endpointVolume.GetMasterVolumeLevelScalar(); double level = _endpointVolume.GetMasterVolumeLevelScalar();
_masterVolume = (int)(level * 100); _masterVolume = (int)(level * 100);
} }

View File

@@ -10,21 +10,22 @@ namespace BetterLyrics.WinUI3.Helper
where T : struct where T : struct
{ {
private T _currentValue; private T _currentValue;
private float _durationSeconds; private double _durationSeconds;
private EasingType? _easingType; private EasingType? _easingType;
private Func<T, T, float, T> _interpolator; private Func<T, T, double, T> _interpolator;
private bool _isTransitioning; private bool _isTransitioning;
private float _progress; private double _progress;
private T _startValue; private T _startValue;
private T _targetValue; private T _targetValue;
public float DurationSeconds => _durationSeconds; public double DurationSeconds => _durationSeconds;
public bool IsTransitioning => _isTransitioning; public bool IsTransitioning => _isTransitioning;
public T Value => _currentValue; public T Value => _currentValue;
public T TargetValue => _targetValue; public T TargetValue => _targetValue;
public EasingType? EasingType => _easingType;
public ValueTransition(T initialValue, float durationSeconds, Func<T, T, float, T>? interpolator = null, EasingType? easingType = null) public ValueTransition(T initialValue, double durationSeconds, Func<T, T, double, T>? interpolator = null, EasingType? easingType = null)
{ {
_currentValue = initialValue; _currentValue = initialValue;
_startValue = initialValue; _startValue = initialValue;
@@ -45,12 +46,12 @@ namespace BetterLyrics.WinUI3.Helper
} }
else else
{ {
_easingType = EasingType.Linear; _easingType = Enums.EasingType.Linear;
_interpolator = GetInterpolatorByEasingType(_easingType.Value); _interpolator = GetInterpolatorByEasingType(_easingType.Value);
} }
} }
public void SetDuration(float seconds) public void SetDuration(double seconds)
{ {
if (seconds <= 0) if (seconds <= 0)
throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive."); throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive.");
@@ -102,7 +103,7 @@ namespace BetterLyrics.WinUI3.Helper
{ {
if (!_isTransitioning) return; if (!_isTransitioning) return;
_progress += (float)(elapsedTime / TimeSpan.FromSeconds(_durationSeconds)); _progress += (double)(elapsedTime / TimeSpan.FromSeconds(_durationSeconds));
if (_progress >= 1f) if (_progress >= 1f)
{ {
_progress = 1f; _progress = 1f;
@@ -115,51 +116,51 @@ namespace BetterLyrics.WinUI3.Helper
} }
} }
private Func<T, T, float, T> GetInterpolatorByEasingType(EasingType type) private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType type)
{ {
if (typeof(T) == typeof(float)) if (typeof(T) == typeof(double))
{ {
return (start, end, progress) => return (start, end, progress) =>
{ {
float s = (float)(object)start; double s = (double)(object)start;
float e = (float)(object)end; double e = (double)(object)end;
float t = progress; double t = progress;
switch (type) switch (type)
{ {
case EasingType.EaseInOutSine: case Enums.EasingType.EaseInOutSine:
t = EasingHelper.EaseInOutSine(t); t = EasingHelper.EaseInOutSine(t);
break; break;
case EasingType.EaseInOutQuad: case Enums.EasingType.EaseInOutQuad:
t = EasingHelper.EaseInOutQuad(t); t = EasingHelper.EaseInOutQuad(t);
break; break;
case EasingType.EaseInOutCubic: case Enums.EasingType.EaseInOutCubic:
t = EasingHelper.EaseInOutCubic(t); t = EasingHelper.EaseInOutCubic(t);
break; break;
case EasingType.EaseInOutQuart: case Enums.EasingType.EaseInOutQuart:
t = EasingHelper.EaseInOutQuart(t); t = EasingHelper.EaseInOutQuart(t);
break; break;
case EasingType.EaseInOutQuint: case Enums.EasingType.EaseInOutQuint:
t = EasingHelper.EaseInOutQuint(t); t = EasingHelper.EaseInOutQuint(t);
break; break;
case EasingType.EaseInOutExpo: case Enums.EasingType.EaseInOutExpo:
t = EasingHelper.EaseInOutExpo(t); t = EasingHelper.EaseInOutExpo(t);
break; break;
case EasingType.EaseInOutCirc: case Enums.EasingType.EaseInOutCirc:
t = EasingHelper.EaseInOutCirc(t); t = EasingHelper.EaseInOutCirc(t);
break; break;
case EasingType.EaseInOutBack: case Enums.EasingType.EaseInOutBack:
t = EasingHelper.EaseInOutBack(t); t = EasingHelper.EaseInOutBack(t);
break; break;
case EasingType.EaseInOutElastic: case Enums.EasingType.EaseInOutElastic:
t = EasingHelper.EaseInOutElastic(t); t = EasingHelper.EaseInOutElastic(t);
break; break;
case EasingType.EaseInOutBounce: case Enums.EasingType.EaseInOutBounce:
t = EasingHelper.EaseInOutBounce(t); t = EasingHelper.EaseInOutBounce(t);
break; break;
case EasingType.SmoothStep: case Enums.EasingType.SmoothStep:
t = EasingHelper.SmoothStep(t); t = EasingHelper.SmoothStep(t);
break; break;
case EasingType.Linear: case Enums.EasingType.Linear:
t = EasingHelper.Linear(t); t = EasingHelper.Linear(t);
break; break;
default: default:

View File

@@ -5,13 +5,11 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models namespace BetterLyrics.WinUI3.Models
{ {
public partial class AlbumArtSearchProviderInfo : ObservableObject public partial class AlbumArtSearchProviderInfo : ObservableRecipient
{ {
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
public partial bool IsEnabled { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtSearchProvider Provider { get; set; }
public partial AlbumArtSearchProvider Provider { get; set; }
public AlbumArtSearchProviderInfo() { } public AlbumArtSearchProviderInfo() { }

View File

@@ -4,13 +4,10 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models namespace BetterLyrics.WinUI3.Models
{ {
public partial class LocalMediaFolder : ObservableObject public partial class LocalMediaFolder : ObservableRecipient
{ {
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
public partial bool IsEnabled { get; set; } [ObservableProperty][NotifyPropertyChangedRecipients] public partial string Path { get; set; }
[ObservableProperty]
public partial string Path { get; set; }
public LocalMediaFolder() { } public LocalMediaFolder() { }

View File

@@ -3,43 +3,55 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.Geometry; using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text; using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Shapes;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Numerics; using System.Numerics;
using Windows.Foundation;
using Windows.Graphics.Effects;
using Windows.UI; using Windows.UI;
namespace BetterLyrics.WinUI3.Models namespace BetterLyrics.WinUI3.Models
{ {
public class LyricsLine public class LyricsLine
{ {
private const float _animationDuration = 0.3f; private const double _animationDuration = 0.3;
public ValueTransition<float> AngleTransition { get; set; } = new( public ValueTransition<double> AngleTransition { get; set; } = new(
initialValue: 0f, initialValue: 0,
durationSeconds: _animationDuration, durationSeconds: _animationDuration,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
); );
public ValueTransition<float> BlurAmountTransition { get; set; } = new( public ValueTransition<double> BlurAmountTransition { get; set; } = new(
initialValue: 0f, initialValue: 0,
durationSeconds: _animationDuration, durationSeconds: _animationDuration,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
); );
public ValueTransition<float> HighlightOpacityTransition { get; set; } = new( public ValueTransition<double> HighlightOpacityTransition { get; set; } = new(
initialValue: 0f, initialValue: 0,
durationSeconds: _animationDuration, durationSeconds: _animationDuration,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
); );
public ValueTransition<float> OpacityTransition { get; set; } = new( public ValueTransition<double> OpacityTransition { get; set; } = new(
initialValue: 0f, initialValue: 0,
durationSeconds: _animationDuration, durationSeconds: _animationDuration,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
); );
public ValueTransition<float> ScaleTransition { get; set; } = new( public ValueTransition<double> ScaleTransition { get; set; } = new(
initialValue: 0f, initialValue: 0.75,
durationSeconds: _animationDuration, durationSeconds: _animationDuration,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
); );
public ValueTransition<double> YOffsetTransition { get; set; } = new(
initialValue: 0,
durationSeconds: 0.5,
easingType: EasingType.EaseInOutSine
);
public CanvasTextLayout? CanvasTextLayout { get; private set; } public CanvasTextLayout? CanvasTextLayout { get; private set; }
@@ -57,30 +69,60 @@ namespace BetterLyrics.WinUI3.Models
public CanvasGeometry? TextGeometry { get; private set; } public CanvasGeometry? TextGeometry { get; private set; }
/// <summary>
/// 背景文字层(底字)
/// </summary>
public CanvasCommandList? BackgroundFontEffect { get; private set; } 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? ForegroundFontEffect { get; private set; }
public void UpdateCenterPosition(float maxWidth, TextAlignmentType type) 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) if (CanvasTextLayout == null)
{ {
return; return;
} }
float centerY = Position.Y + (float)CanvasTextLayout.LayoutBounds.Height; double centerY = Position.Y + (double)CanvasTextLayout.LayoutBounds.Height;
CenterPosition = type switch CenterPosition = type switch
{ {
TextAlignmentType.Left => new Vector2(Position.X, centerY), TextAlignmentType.Left => new Vector2(Position.X, (float)centerY),
TextAlignmentType.Center => new Vector2(Position.X + maxWidth / 2, centerY), TextAlignmentType.Center => new Vector2((float)(Position.X + maxWidth / 2.0), (float)centerY),
TextAlignmentType.Right => new Vector2(Position.X + maxWidth, centerY), TextAlignmentType.Right => new Vector2((float)(Position.X + maxWidth), (float)centerY),
_ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null), _ => throw new System.ArgumentOutOfRangeException(nameof(type), type, null),
}; };
} }
public void UpdateTextLayout(ICanvasAnimatedControl control, CanvasTextFormat textFormat, float maxWidth, float maxHeight, TextAlignmentType type) public void RecreateTextLayout(ICanvasAnimatedControl control, CanvasTextFormat textFormat, double maxWidth, double maxHeight, TextAlignmentType type)
{ {
CanvasTextLayout?.Dispose(); CanvasTextLayout?.Dispose();
CanvasTextLayout = null; CanvasTextLayout = null;
CanvasTextLayout = new CanvasTextLayout(control, DisplayedText, textFormat, maxWidth, maxHeight); CanvasTextLayout = new CanvasTextLayout(control, DisplayedText, textFormat, (float)maxWidth, (float)maxHeight);
CanvasTextLayout.HorizontalAlignment = type.ToCanvasHorizontalAlignment(); CanvasTextLayout.HorizontalAlignment = type.ToCanvasHorizontalAlignment();
} }
@@ -90,7 +132,7 @@ namespace BetterLyrics.WinUI3.Models
TextGeometry = null; TextGeometry = null;
} }
public void UpdateTextGeometry() public void RecreateTextGeometry()
{ {
DisposeTextGeometry(); DisposeTextGeometry();
if (CanvasTextLayout == null) if (CanvasTextLayout == null)
@@ -108,7 +150,7 @@ namespace BetterLyrics.WinUI3.Models
ForegroundFontEffect = null; ForegroundFontEffect = null;
} }
public void UpdateFontEffect(ICanvasAnimatedControl control, bool drawStroke, Color strokeColor, int strokeWidth, Color fontColor) public void RecreateFontEffect(ICanvasAnimatedControl control, Color strokeColor, int strokeWidth, Color bgFontColor, Color fgFontColor)
{ {
DisposeFontEffects(); DisposeFontEffects();
if (TextGeometry == null) if (TextGeometry == null)
@@ -119,13 +161,372 @@ namespace BetterLyrics.WinUI3.Models
using var bgFontEffectDs = BackgroundFontEffect.CreateDrawingSession(); using var bgFontEffectDs = BackgroundFontEffect.CreateDrawingSession();
ForegroundFontEffect = new CanvasCommandList(control); ForegroundFontEffect = new CanvasCommandList(control);
using var fgFontEffectDs = ForegroundFontEffect.CreateDrawingSession(); using var fgFontEffectDs = ForegroundFontEffect.CreateDrawingSession();
if (drawStroke) // 大于 0 才描边,避免不必要的资源浪费
if (strokeWidth > 0)
{ {
bgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边 bgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边
fgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边 fgFontEffectDs.DrawGeometry(TextGeometry, Position, strokeColor, strokeWidth); // 描边
} }
bgFontEffectDs.FillGeometry(TextGeometry, Position, fontColor); // 填充 bgFontEffectDs.FillGeometry(TextGeometry, Position, bgFontColor); // 填充
fgFontEffectDs.FillGeometry(TextGeometry, Position, fontColor); // 填充 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

@@ -5,13 +5,11 @@ using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models namespace BetterLyrics.WinUI3.Models
{ {
public partial class LyricsSearchProviderInfo : ObservableObject public partial class LyricsSearchProviderInfo : ObservableRecipient
{ {
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
public partial bool IsEnabled { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; }
public partial LyricsSearchProvider Provider { get; set; }
public LyricsSearchProviderInfo() { } public LyricsSearchProviderInfo() { }

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang // 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -9,47 +10,84 @@ using System.Linq;
namespace BetterLyrics.WinUI3.Models namespace BetterLyrics.WinUI3.Models
{ {
public partial class MediaSourceProviderInfo : ObservableObject public partial class MediaSourceProviderInfo : ObservableRecipient
{ {
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
public partial bool IsEnabled { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial string Provider { get; set; }
public partial string Provider { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLastFMTrackEnabled { get; set; }
public partial bool IsLastFMTrackEnabled { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial int TimelineSyncThreshold { get; set; }
public partial int TimelineSyncThreshold { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial int PositionOffset { get; set; }
public partial int PositionOffset { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ResetPositionOffsetOnSongChanged { get; set; }
public partial bool ResetPositionOffsetOnSongChanged { get; set; }
[ObservableProperty] [ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
public partial ObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
public MediaSourceProviderInfo() { } public MediaSourceProviderInfo() { }
public MediaSourceProviderInfo(string provider) public MediaSourceProviderInfo(string provider) : base()
{ {
switch (provider)
{
case Constants.PlayerID.AppleMusic:
// Apple Music 的特性
TimelineSyncThreshold = 1000;
PositionOffset = 1000;
break;
default:
// 设置 100 以防不必要的重复同步
TimelineSyncThreshold = 100;
PositionOffset = 0;
break;
}
Provider = provider; Provider = provider;
IsEnabled = true; IsEnabled = true;
IsLastFMTrackEnabled = false; IsLastFMTrackEnabled = false;
if (provider == Constants.PlayerID.AppleMusic)
{
TimelineSyncThreshold = PositionOffset = 1000;
}
else
{
TimelineSyncThreshold = 0;
PositionOffset = 0;
}
ResetPositionOffsetOnSongChanged = false; ResetPositionOffsetOnSongChanged = false;
LyricsSearchProvidersInfo = [.. Enum.GetValues<LyricsSearchProvider>().Select(p => new LyricsSearchProviderInfo(p, true))]; LyricsSearchProvidersInfo = [.. Enum.GetValues<LyricsSearchProvider>().Select(p => new LyricsSearchProviderInfo(p, true))];
AlbumArtSearchProvidersInfo = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
}
partial void OnAlbumArtSearchProvidersInfoChanged(FullyObservableCollection<AlbumArtSearchProviderInfo> oldValue, FullyObservableCollection<AlbumArtSearchProviderInfo> newValue)
{
oldValue?.CollectionChanged -= AlbumArtSearchProvidersInfo_CollectionChanged;
oldValue?.ItemPropertyChanged -= AlbumArtSearchProvidersInfo_ItemPropertyChanged;
newValue?.CollectionChanged += AlbumArtSearchProvidersInfo_CollectionChanged;
newValue?.ItemPropertyChanged += AlbumArtSearchProvidersInfo_ItemPropertyChanged;
}
partial void OnLyricsSearchProvidersInfoChanged(FullyObservableCollection<LyricsSearchProviderInfo> oldValue, FullyObservableCollection<LyricsSearchProviderInfo> newValue)
{
oldValue?.CollectionChanged -= LyricsSearchProvidersInfo_CollectionChanged;
oldValue?.ItemPropertyChanged -= LyricsSearchProvidersInfo_ItemPropertyChanged;
newValue?.CollectionChanged += LyricsSearchProvidersInfo_CollectionChanged;
newValue?.ItemPropertyChanged += LyricsSearchProvidersInfo_ItemPropertyChanged;
}
private void AlbumArtSearchProvidersInfo_ItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(AlbumArtSearchProvidersInfo));
}
private void AlbumArtSearchProvidersInfo_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(AlbumArtSearchProvidersInfo));
}
private void LyricsSearchProvidersInfo_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(LyricsSearchProvidersInfo));
}
private void LyricsSearchProvidersInfo_ItemPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(LyricsSearchProvidersInfo));
} }
} }

View File

@@ -0,0 +1,18 @@
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class AlbumArtLayoutSettings : ObservableRecipient
{
[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
public AlbumArtLayoutSettings() { }
}
}

View File

@@ -0,0 +1,35 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class AppSettings : ObservableRecipient
{
public string Version { get; set; } = Helper.MetadataHelper.AppVersion;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsStyleSettings StandardLyricsStyleSettings { get; set; } = new LyricsStyleSettings(32, TextAlignmentType.Left, 0);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsStyleSettings DesktopLyricsStyleSettings { get; set; } = new LyricsStyleSettings(28, TextAlignmentType.Center, 2);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsStyleSettings DockLyricsStyleSettings { get; set; } = new LyricsStyleSettings(16, TextAlignmentType.Center, 0);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectSettings StandardLyricsEffectSettings { get; set; } = new LyricsEffectSettings(100, 500, 1000, EasingType.EaseInOutSine);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectSettings DesktopLyricsEffectSettings { get; set; } = new LyricsEffectSettings(500, 500, 500, EasingType.EaseInOutSine);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectSettings DockLyricsEffectSettings { get; set; } = new LyricsEffectSettings(500, 500, 500, EasingType.EaseInOutSine);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial StandardModeSettings StandardModeSettings { get; set; } = new StandardModeSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial DesktopModeSettings DesktopModeSettings { get; set; } = new DesktopModeSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial DockModeSettings DockModeSettings { get; set; } = new DockModeSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsBackgroundSettings LyricsBackgroundSettings { get; set; } = new LyricsBackgroundSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtLayoutSettings AlbumArtLayoutSettings { get; set; } = new AlbumArtLayoutSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSettings TranslationSettings { get; set; } = new TranslationSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial GeneralSettings GeneralSettings { get; set; } = new GeneralSettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial MusicGallerySettings MusicGallerySettings { get; set; } = new MusicGallerySettings();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<LocalMediaFolder> LocalMediaFolders { get; set; } = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; } = [];
public AppSettings() { }
}
}

View File

@@ -0,0 +1,20 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class DesktopModeSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect WindowBounds { get; set; } = new Rect(100, 100, 400, 200);
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoLockOnDesktopMode { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LockHotKeyIndex { get; set; } = 'U' - 'A'; // Default to 'U' key
public DesktopModeSettings() { }
}
}

View File

@@ -0,0 +1,20 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class DockModeSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial DockPlacement DockPlacement { get; set; } = DockPlacement.Top;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int DockWindowHeight { get; set; } = 64;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string DockMonitorDeviceName { get; set; } = MonitorHelper.GetPrimaryMonitorDeviceName();
public DockModeSettings() { }
}
}

View File

@@ -0,0 +1,24 @@
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class GeneralSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AutoStartWindowType AutoStartWindowType { get; set; } = AutoStartWindowType.StandardMode;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Language Language { get; set; } = Language.FollowSystem;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IgnoreFullscreenWindow { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LXMusicServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool HideWindowWhenNotPlaying { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsDragEverywhereEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsDisplayType DisplayType { get; set; } = LyricsDisplayType.SplitView;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsImmersiveMode { get; set; } = false;
public GeneralSettings() { }
}
}

View File

@@ -0,0 +1,14 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class LastFMSettings : ObservableRecipient
{
public LastFMSettings() { }
}
}

View File

@@ -0,0 +1,22 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class LyricsBackgroundSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ElementTheme LyricsBackgroundTheme { get; set; } = ElementTheme.Dark;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlayBlurAmount { get; set; } = 100; // 100 % of the cover image size
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlayOpacity { get; set; } = 100; // 100 % = 1.0
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PureColorOverlayOpacity { get; set; } = 100; // 100 % = 1.0
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverOverlaySpeed { get; set; } = 50; // 50 % of the base rotate speed
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverAcrylicEffectAmount { get; set; } = 0;
public LyricsBackgroundSettings() { }
}
}

View File

@@ -0,0 +1,33 @@
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class LyricsEffectSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsBlurAmount { get; set; } = 5;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType LyricsGlowEffectScope { get; set; } = LineRenderingType.CurrentChar;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LineRenderingType LyricsHighlightScope { get; set; } = LineRenderingType.LineStartToCurrentChar;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollTopDuration { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollBottomDuration { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsVerticalEdgeOpacity { get; set; } = 0; // 0% opacity
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFanLyricsEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial EasingType LyricsScrollEasingType { get; set; }
public LyricsEffectSettings(int lyricsScrollTopDuration, int lyricsScrollDuration, int lyricsScrollBottomDuration, EasingType lyricsScrollEasingType)
{
LyricsScrollTopDuration = lyricsScrollTopDuration;
LyricsScrollDuration = lyricsScrollDuration;
LyricsScrollBottomDuration = lyricsScrollBottomDuration;
LyricsScrollEasingType = lyricsScrollEasingType;
}
}
}

View File

@@ -0,0 +1,41 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class LyricsStyleSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontSize { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsBgFontOpacity { get; set; } = 30; // 30% opacity
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; }
[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 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() { }
public LyricsStyleSettings(int lyricsFontSize, TextAlignmentType lyricsAlignmentType, int lyricsFontStrokeWidth)
{
LyricsFontSize = lyricsFontSize;
LyricsAlignmentType = lyricsAlignmentType;
LyricsFontStrokeWidth = lyricsFontStrokeWidth;
}
}
}

View File

@@ -0,0 +1,17 @@
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class MusicGallerySettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial PlaybackOrder PlaybackOrder { get; set; } = PlaybackOrder.RepeatAll;
public MusicGallerySettings() { }
}
}

View File

@@ -0,0 +1,18 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class StandardModeSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect WindowBounds { get; set; } = new Rect(100, 100, 1000, 600);
public StandardModeSettings() { }
}
}

View File

@@ -0,0 +1,21 @@
using BetterLyrics.WinUI3.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models.Settings
{
public partial class TranslationSettings : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLibreTranslateEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LibreTranslateServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTranslationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowTranslationOnly { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SelectedTargetLanguageIndex { get; set; } = LanguageHelper.GetDefaultTargetLanguageIndex();
public TranslationSettings() { }
}
}

View File

@@ -1,20 +1,15 @@
// 2025/6/23 by Zhe Fang // 2025/6/23 by Zhe Fang
using System.Collections.Generic; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using BetterLyrics.WinUI3.Models;
namespace BetterLyrics.WinUI3.Serialization namespace BetterLyrics.WinUI3.Serialization
{ {
[JsonSerializable(typeof(List<AlbumArtSearchProviderInfo>))]
[JsonSerializable(typeof(List<LyricsSearchProviderInfo>))]
[JsonSerializable(typeof(List<MediaSourceProviderInfo>))]
[JsonSerializable(typeof(List<LocalMediaFolder>))]
[JsonSerializable(typeof(List<string>))]
[JsonSerializable(typeof(TranslateResponse))] [JsonSerializable(typeof(TranslateResponse))]
[JsonSerializable(typeof(JsonElement))] [JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(AppSettings))]
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]
internal partial class SourceGenerationContext : JsonSerializerContext { } internal partial class SourceGenerationContext : JsonSerializerContext { }
} }

View File

@@ -10,8 +10,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -31,11 +29,11 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
_iTunesHttpClinet = new(); _iTunesHttpClinet = new();
} }
public async Task<byte[]?> SearchAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null) public async Task<byte[]?> SearchAsync(string mediaSessionId, string title, string artist, string album, byte[]? bytesFromSMTC = null)
{ {
byte[]? result = null; byte[]? result = null;
foreach (var provider in _settingsService.AlbumArtSearchProvidersInfo) foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == mediaSessionId).FirstOrDefault()?.AlbumArtSearchProvidersInfo ?? [])
{ {
if (!provider.IsEnabled) if (!provider.IsEnabled)
{ {
@@ -45,7 +43,7 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
switch (provider.Provider) switch (provider.Provider)
{ {
case AlbumArtSearchProvider.Local: case AlbumArtSearchProvider.Local:
result = SearchFile(artist, album); result = SearchFile(artist, title);
break; break;
case AlbumArtSearchProvider.SMTC: case AlbumArtSearchProvider.SMTC:
result = bytesFromSMTC; result = bytesFromSMTC;
@@ -66,15 +64,15 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
return null; return null;
} }
private byte[]? SearchFile(string artist, string album) private byte[]? SearchFile(string artist, string title)
{ {
foreach (var folder in _settingsService.LocalMediaFolders) foreach (var folder in _settingsService.AppSettings.LocalMediaFolders)
{ {
if (Directory.Exists(folder.Path) && folder.IsEnabled) if (Directory.Exists(folder.Path) && folder.IsEnabled)
{ {
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories)) foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
{ {
if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), album, artist)) if (FileHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), title, artist))
{ {
Track track = new(file); Track track = new(file);
var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData; var bytes = track.EmbeddedPictures.FirstOrDefault()?.PictureData;

View File

@@ -8,6 +8,6 @@ namespace BetterLyrics.WinUI3.Services.AlbumArtSearchService
{ {
public interface IAlbumArtSearchService public interface IAlbumArtSearchService
{ {
Task<byte[]?> SearchAsync(string title, string artist, string album, byte[]? bytesFromSMTC = null); Task<byte[]?> SearchAsync(string mediaSessionId, string title, string artist, string album, byte[]? bytesFromSMTC = null);
} }
} }

View File

@@ -3,7 +3,6 @@ using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel;
using BetterLyrics.WinUI3.Views; using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@@ -40,7 +39,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
_settingsService = settingsService; _settingsService = settingsService;
_client = new LastfmClient(Constants.LastFM.ApiKey, Constants.LastFM.SharedSecret); _client = new LastfmClient(Constants.LastFM.ApiKey, Constants.LastFM.SharedSecret);
_client.Session.SessionKey = _settingsService.LastFMSessionKey; _client.Session.SessionKey = PasswordVaultHelper.Get(Constants.App.AppName, Constants.LastFM.SessionKeyCredentialKey) ?? string.Empty;
UpdateAuthStatusAsync(); UpdateAuthStatusAsync();
} }
@@ -49,7 +48,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
try try
{ {
await _client.AuthenticateViaWebAsync(); await _client.AuthenticateViaWebAsync();
_settingsService.LastFMSessionKey = _client.Session.SessionKey; PasswordVaultHelper.Save(Constants.App.AppName, Constants.LastFM.SessionKeyCredentialKey, _client.Session.SessionKey);
await UpdateAuthStatusAsync(); await UpdateAuthStatusAsync();
} }
catch (Exception) catch (Exception)
@@ -61,7 +60,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
public async Task ConfirmUnAuthAsync() public async Task ConfirmUnAuthAsync()
{ {
_client.Session.SessionKey = ""; _client.Session.SessionKey = "";
_settingsService.LastFMSessionKey = ""; PasswordVaultHelper.Delete(Constants.App.AppName, Constants.LastFM.SessionKeyCredentialKey);
await UpdateAuthStatusAsync(); await UpdateAuthStatusAsync();
} }

View File

@@ -13,7 +13,5 @@ namespace BetterLyrics.WinUI3.Services.LibWatcherService
public interface ILibWatcherService public interface ILibWatcherService
{ {
event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged; event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged;
public void UpdateWatchers(List<LocalMediaFolder> folders);
} }
} }

View File

@@ -14,11 +14,25 @@ namespace BetterLyrics.WinUI3.Services.LibWatcherService
{ {
public class LibWatcherService : BaseViewModel, IDisposable, ILibWatcherService public class LibWatcherService : BaseViewModel, IDisposable, ILibWatcherService
{ {
private readonly ISettingsService _settingsService;
private readonly Dictionary<string, FileSystemWatcher> _watchers = []; private readonly Dictionary<string, FileSystemWatcher> _watchers = [];
public LibWatcherService(ISettingsService settingsService) : base(settingsService) public LibWatcherService(ISettingsService settingsService)
{ {
UpdateWatchers(_settingsService.LocalMediaFolders); _settingsService = settingsService;
_settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
_settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
UpdateWatchers();
}
private void LocalMediaFolders_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
{
UpdateWatchers();
}
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
UpdateWatchers();
} }
public event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged; public event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged;
@@ -32,8 +46,9 @@ namespace BetterLyrics.WinUI3.Services.LibWatcherService
_watchers.Clear(); _watchers.Clear();
} }
public void UpdateWatchers(List<LocalMediaFolder> folders) private void UpdateWatchers()
{ {
var folders = _settingsService.AppSettings.LocalMediaFolders;
// 移除不再监听的 // 移除不再监听的
foreach (var key in _watchers.Keys.ToList()) foreach (var key in _watchers.Keys.ToList())
{ {

View File

@@ -92,7 +92,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
try try
{ {
foreach (var provider in _settingsService.MediaSourceProvidersInfo.Where(x => x.Provider == mediaSessionId).FirstOrDefault()?.LyricsSearchProvidersInfo ?? []) foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == mediaSessionId).FirstOrDefault()?.LyricsSearchProvidersInfo ?? [])
{ {
if (!provider.IsEnabled) if (!provider.IsEnabled)
{ {
@@ -169,7 +169,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
private async Task<string?> SearchFile(string title, string artist, LyricsFormat format) private async Task<string?> SearchFile(string title, string artist, LyricsFormat format)
{ {
foreach (var folder in _settingsService.LocalMediaFolders) foreach (var folder in _settingsService.AppSettings.LocalMediaFolders)
{ {
if (Directory.Exists(folder.Path) && folder.IsEnabled) if (Directory.Exists(folder.Path) && folder.IsEnabled)
{ {
@@ -191,7 +191,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
private string? SearchEmbedded(string title, string artist) private string? SearchEmbedded(string title, string artist)
{ {
foreach (var folder in _settingsService.LocalMediaFolders) foreach (var folder in _settingsService.AppSettings.LocalMediaFolders)
{ {
if (Directory.Exists(folder.Path) && folder.IsEnabled) if (Directory.Exists(folder.Path) && folder.IsEnabled)
{ {

View File

@@ -21,6 +21,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
Task NextAsync(); Task NextAsync();
Task ChangePosition(double seconds); Task ChangePosition(double seconds);
MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo();
bool IsPlaying { get; } bool IsPlaying { get; }
SongInfo? SongInfo { get; } SongInfo? SongInfo { get; }
TimeSpan Position { get; } TimeSpan Position { get; }

View File

@@ -2,18 +2,20 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.AlbumArtSearchService; using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.LastFMService; using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using EvtSource; using EvtSource;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Graphics.Canvas;
using Microsoft.UI.Dispatching; using Microsoft.UI.Dispatching;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System; using System;
@@ -33,11 +35,12 @@ using WindowsMediaController;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{ {
public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService, public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService,
IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>, IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>>> IRecipient<PropertyChangedMessage<FullyObservableCollection<AlbumArtSearchProviderInfo>>>
{ {
private readonly IAlbumArtSearchService _albumArtSearchService; private readonly IAlbumArtSearchService _albumArtSearchService;
private readonly ILogger<MediaSessionsService> _logger; private readonly ILogger<MediaSessionsService> _logger;
private readonly ISettingsService _settingsService;
private double _lxMusicPositionSeconds = 0; private double _lxMusicPositionSeconds = 0;
private double _lxMusicDurationSeconds = 0; private double _lxMusicDurationSeconds = 0;
@@ -53,9 +56,8 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new(); private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new();
private SongInfo? _cachedSongInfo; private SongInfo? _cachedSongInfo;
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
private byte[]? _SMTCAlbumArtBytes = null; private byte[]? _SMTCAlbumArtBytes = null;
private int _targetAlbumArtSize = 400; private int _targetAlbumArtSize = 500;
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged; public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
public event EventHandler<TimelineChangedEventArgs>? TimelineChanged; public event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
@@ -63,22 +65,39 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged; public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged; public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
public MediaSessionsService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService) : base(settingsService) public MediaSessionsService(ISettingsService settingsService, IAlbumArtSearchService albumArtSearchService)
{ {
_settingsService = settingsService;
_albumArtSearchService = albumArtSearchService; _albumArtSearchService = albumArtSearchService;
_logger = Ioc.Default.GetRequiredService<ILogger<MediaSessionsService>>(); _logger = Ioc.Default.GetRequiredService<ILogger<MediaSessionsService>>();
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo; _settingsService.AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += MediaSourceProvidersInfo_ItemPropertyChanged;
InitMediaManager(); InitMediaManager();
} }
private void MediaSourceProvidersInfo_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(MediaSourceProviderInfo.AlbumArtSearchProvidersInfo):
_ = _albumArtRefreshRunner.RunAsync(async tokne =>
{
await UpdateAlbumArtRelated(tokne);
});
break;
default:
break;
}
}
public bool IsPlaying => _cachedIsPlaying; public bool IsPlaying => _cachedIsPlaying;
public SongInfo? SongInfo => _cachedSongInfo; public SongInfo? SongInfo => _cachedSongInfo;
public TimeSpan Position => _cachedPosition; public TimeSpan Position => _cachedPosition;
private bool IsMediaSourceEnabled(string id) private bool IsMediaSourceEnabled(string id)
{ {
return _mediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true; return _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true;
} }
private void InitMediaManager() private void InitMediaManager()
@@ -94,6 +113,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
Task.Run(() => Task.Run(() =>
{ {
MediaManager_OnFocusedSessionChanged(null); MediaManager_OnFocusedSessionChanged(null);
_mediaManager.CurrentMediaSessions.ToList().ForEach(x => RecordMediaSourceProviderInfo(x.Value));
}); });
} }
@@ -138,7 +158,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
var focusedSession = _mediaManager.GetFocusedSession(); var focusedSession = _mediaManager.GetFocusedSession();
RecordMediaSourceProviderInfo(mediaSession); //RecordMediaSourceProviderInfo(mediaSession);
if (mediaSession != focusedSession) return; if (mediaSession != focusedSession) return;
if (!IsMediaSourceEnabled(mediaSession.Id)) if (!IsMediaSourceEnabled(mediaSession.Id))
@@ -169,7 +189,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
var focusedSession = _mediaManager.GetFocusedSession(); var focusedSession = _mediaManager.GetFocusedSession();
RecordMediaSourceProviderInfo(mediaSession); //RecordMediaSourceProviderInfo(mediaSession);
if (mediaSession != focusedSession) return; if (mediaSession != focusedSession) return;
if (!IsMediaSourceEnabled(id)) if (!IsMediaSourceEnabled(id))
@@ -204,6 +224,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
} }
else else
{ {
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
var currentMediaSourceProviderInfo = GetCurrentMediaSourceProviderInfo();
if (currentMediaSourceProviderInfo?.ResetPositionOffsetOnSongChanged == true)
{
currentMediaSourceProviderInfo?.PositionOffset = 0;
}
});
_cachedSongInfo = new SongInfo _cachedSongInfo = new SongInfo
{ {
Title = mediaProperties.Title, Title = mediaProperties.Title,
@@ -282,18 +311,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
var id = mediaSession?.Id; var id = mediaSession?.Id;
if (string.IsNullOrEmpty(id)) return; if (string.IsNullOrEmpty(id)) return;
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id); _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
if (found == null)
{ {
_mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id)); var found = _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
// 在这里就写进设置 if (found == null)
// 因为 SettingsPageViewModel 可能还没有初始化
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{ {
MediaSourceProvidersInfoChanged?.Invoke(this, new MediaSourceProvidersInfoEventArgs(_mediaSourceProvidersInfo)); _settingsService.AppSettings.MediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id));
}); }
} });
} }
private void SendNullMessages() private void SendNullMessages()
@@ -328,6 +353,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
} }
byte[]? bytes = await _albumArtSearchService.SearchAsync( byte[]? bytes = await _albumArtSearchService.SearchAsync(
SongInfo?.SourceAppUserModelId ?? "",
_cachedSongInfo.Title, _cachedSongInfo.Title,
_cachedSongInfo.Artist, _cachedSongInfo.Artist,
_cachedSongInfo?.Album ?? string.Empty, _cachedSongInfo?.Album ?? string.Empty,
@@ -367,7 +393,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{ {
try try
{ {
_sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}{Constants.LXMusic.QuerySuffix}")).Start(); _sse = new EventSourceReader(new Uri($"{_settingsService.AppSettings.GeneralSettings.LXMusicServer}{Constants.LXMusic.QuerySuffix}")).Start();
_sse.MessageReceived += Sse_MessageReceived; _sse.MessageReceived += Sse_MessageReceived;
_sse.Disconnected += Sse_Disconnected; _sse.Disconnected += Sse_Disconnected;
} }
@@ -470,26 +496,24 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
} }
} }
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message) public MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo()
{ {
if (message.Sender is SettingsPageViewModel) return _settingsService.AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == _cachedSongInfo?.SourceAppUserModelId)?.FirstOrDefault();
}
public async void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is MediaSourceProviderInfo)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.MediaSourceProvidersInfo)) if (message.PropertyName == nameof(MediaSourceProviderInfo.IsEnabled))
{ {
_mediaSourceProvidersInfo = [.. message.NewValue];
MediaManager_OnFocusedSessionChanged(null); MediaManager_OnFocusedSessionChanged(null);
} }
} }
} else if (message.Sender is AlbumArtSearchProviderInfo)
public async void Receive(PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>> message)
{
if (message.Sender is SettingsPageViewModel)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.AlbumArtSearchProvidersInfo)) if (message.PropertyName == nameof(AlbumArtSearchProviderInfo.IsEnabled))
{ {
// Album art search providers info changed, re-fetch album art
_logger.LogInformation("Album art search providers info changed, refreshing album art.");
await _albumArtRefreshRunner.RunAsync(async tokne => await _albumArtRefreshRunner.RunAsync(async tokne =>
{ {
await UpdateAlbumArtRelated(tokne); await UpdateAlbumArtRelated(tokne);
@@ -497,5 +521,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
} }
} }
} }
public void Receive(PropertyChangedMessage<FullyObservableCollection<AlbumArtSearchProviderInfo>> message)
{
}
} }
} }

View File

@@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Windows.UI; using Windows.UI;
@@ -10,104 +11,10 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
{ {
public interface ISettingsService public interface ISettingsService
{ {
AppSettings AppSettings { get; set; }
// App behavior // App behavior
AutoStartWindowType AutoStartWindowType { get; set; } bool ImportSettings(string importPath);
void ExportSettings(string exportPath);
int CoverImageRadius { get; set; }
int CoverOverlayBlurAmount { get; set; }
int CoverOverlayOpacity { get; set; }
bool IsDynamicCoverOverlayEnabled { get; set; }
int CoverAcrylicEffectAmount { get; set; }
bool IsFanLyricsEnabled { get; set; }
bool IsFirstRun { get; set; }
bool IsLyricsGlowEffectEnabled { get; set; }
Language Language { get; set; }
int DesktopWindowLeft { get; set; }
int DesktopWindowTop { get; set; }
int DesktopWindowWidth { get; set; }
int DesktopWindowHeight { get; set; }
int StandardWindowWidth { get; set; }
int StandardWindowHeight { get; set; }
int StandardWindowLeft { get; set; }
int StandardWindowTop { get; set; }
bool AutoLockOnDesktopMode { get; set; }
string LibreTranslateServer { get; set; }
int SelectedTargetLanguageIndex { get; set; }
int PositionOffset { get; set; }
// Lyrics lib
List<LocalMediaFolder> LocalMediaFolders { get; set; }
// Lyrics style and effetc
TextAlignmentType LyricsAlignmentType { get; set; }
TextAlignmentType SongInfoAlignmentType { get; set; }
int LyricsBlurAmount { get; set; }
Color LyricsCustomBgFontColor { get; set; }
Color LyricsCustomFgFontColor { get; set; }
Color LyricsCustomStrokeFontColor { get; set; }
int LyricsBgFontOpacity { get; set; }
LyricsFontColorType LyricsBgFontColorType { get; set; }
LyricsFontColorType LyricsFgFontColorType { get; set; }
LyricsFontColorType LyricsStrokeFontColorType { get; set; }
int LyricsStandardFontSize { get; set; }
int LyricsDockFontSize { get; set; }
int LyricsDesktopFontSize { get; set; }
ElementTheme LyricsBackgroundTheme { get; set; }
int LyricsFontStrokeWidth { get; set; }
LyricsFontWeight LyricsFontWeight { get; set; }
LineRenderingType LyricsGlowEffectScope { get; set; }
LineRenderingType LyricsHighlightScope { get; set; }
bool IsLyricsFloatAnimationEnabled { get; set; }
float LyricsLineSpacingFactor { get; set; }
List<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
List<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
EasingType LyricsScrollEasingType { get; set; }
int LyricsScrollDuration { get; set; }
int LyricsVerticalEdgeOpacity { get; set; }
bool IgnoreFullscreenWindow { get; set; }
bool IsTranslationEnabled { get; set; }
bool ShowTranslationOnly { get; set; }
LyricsDisplayType DisplayType { get; set; }
int LockHotKeyIndex { get; set; }
bool IsImmersiveMode { get; set; }
string LXMusicServer { get; set; }
DockPlacement DockPlacement { get; set; }
bool HideWindowWhenNotPlaying { get; set; }
int DockWindowHeight { get; set; }
int SelectedFontFamilyIndex { get; set; }
string LyricsFontFamily { get; set; }
bool IsDragEverywhereEnabled { get; set; }
PlaybackOrder PlaybackOrder { get; set; }
bool IsLibreTranslateEnabled { get; set; }
string DockMonitorDeviceName { get; set; }
// LastFM
string LastFMSessionKey { get; set; }
string LyricsTranslationSeparator { get; set; }
} }
} }

View File

@@ -1,708 +1,211 @@
// 2025/6/23 by Zhe Fang // 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Serialization; using BetterLyrics.WinUI3.Serialization;
using CommunityToolkit.WinUI.Helpers; using BetterLyrics.WinUI3.ViewModels;
using Microsoft.UI; using BetterLyrics.WinUI3.Views;
using Microsoft.UI.Xaml; using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using System; using System;
using System.Collections.Generic; using System.IO;
using System.Linq; using System.Linq;
using Windows.Media.Core; using Windows.Globalization;
using Windows.Storage;
using Windows.UI;
namespace BetterLyrics.WinUI3.Services.SettingsService namespace BetterLyrics.WinUI3.Services.SettingsService
{ {
public class SettingsService : ISettingsService // TODO 初始化时从文件读取到对象,后续独写操作先操纵对象,写入用 Debounce 写入文件
// 新建一个 AppSettings 类
public partial class SettingsService : BaseViewModel, ISettingsService
{ {
public const string LyricsCustomBgFontColorKey = "LyricsCustomBgFontColor"; public AppSettings AppSettings { get; set; }
public const string LyricsCustomFgFontColorKey = "LyricsCustomFgFontColor";
public const string LyricsCustomStrokeFontColorKey = "LyricsCustomStrokeFontColor";
// App behavior
private const string AutoStartWindowTypeKey = "AutoStartWindowType";
private const string CoverImageRadiusKey = "AlbumArtCornerRadius";
private const string CoverOverlayBlurAmountKey = "CoverOverlayBlurAmount";
private const string CoverOverlayOpacityKey = "CoverOverlayOpacity";
private const string IsCoverOverlayEnabledKey = "IsCoverOverlayEnabled";
private const string CoverAcrylicEffectAmountKey = "CoverAcrylicEffectAmount";
private const string DesktopWindowLeftKey = "DesktopWindowLeft";
private const string DesktopWindowTopKey = "DesktopWindowTop";
private const string DesktopWindowWidthKey = "DesktopWindowWidth";
private const string DesktopWindowHeightKey = "DesktopWindowHeight";
private const string StandardWindowLeftKey = "StandardWindowLeft";
private const string StandardWindowTopKey = "StandardWindowTop";
private const string StandardWindowWidthKey = "StandardWindowWidth";
private const string StandardWindowHeightKey = "StandardWindowHeight";
private const string AutoLockOnDesktopModeKey = "AutoLockOnDesktopMode";
private const string IsImmersiveModeKey = "IsImmersiveMode";
private const string IsDynamicCoverOverlayEnabledKey = "IsDynamicCoverOverlayEnabled";
private const string IsFanLyricsEnabledKey = "IsFanLyricsEnabled";
private const string IsFirstRunKey = "IsFirstRun";
private const string IsLyricsGlowEffectEnabledKey = "IsLyricsGlowEffectEnabled";
private const string LanguageKey = "Language";
private const string LocalMediaFoldersKey = "LocalLyricsFolders";
private const string LyricsAlignmentTypeKey = "TextAlignmentType";
private const string SongInfoAlignmentTypeKey = "SongInfoAlignmentType";
private const string LyricsBlurAmountKey = "LyricsBlurAmount";
private const string LyricsBgFontColorTypeKey = "_lyricsBgFontColorType";
private const string LyricsFgFontColorTypeKey = "LyricsFgFontColorType";
private const string LyricsStrokeFontColorTypeKey = "LyricsStrokeFontColorType";
private const string LyricsFontStrokeWidthKey = "LyricsFontStrokeWidth";
// Lyrics font size
private const string LyricsStandardFontSizeKey = "LyricsStandardFontSize";
private const string LyricsDockFontSizeKey = "LyricsDockFontSize";
private const string LyricsDesktopFontSizeKey = "LyricsDesktopFontSize";
private const string LyricsFontWeightKey = "LyricsFontWeightKey";
private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope";
private const string LyricsHighlightSopeKey = "LyricsHighlightSope";
private const string LyricsLineSpacingFactorKey = "LyricsLineSpacingFactor";
private const string AlbumArtSearchProvidersInfoKey = "AlbumArtSearchProvidersInfo";
private const string LyricsVerticalEdgeOpacityKey = "LyricsVerticalEdgeOpacity";
private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo";
// Translation
private const string IsTranslationEnabledKey = "IsTranslationEnabled";
private const string ShowTranslationOnlyKey = "ShowTranslationOnly";
private const string IsLibreTranslateEnabledKey = "IsLibreTranslateEnabled";
private const string LibreTranslateServerKey = "LibreTranslateServer";
private const string SelectedTargetLanguageIndexKey = "SelectedTargetLanguageIndex";
// LX Music
private const string LXMusicServerKey = "LXMusicServer";
private const string LyricsBackgroundThemeKey = "LyricsBackgroundTheme";
private const string IgnoreFullscreenWindowKey = "IgnoreFullscreenWindow";
private const string PreferredDisplayTypeKey = "PreferredDisplayTypeKey";
private const string LyricsScrollEasingTypeKey = "LyricsScrollEasingType";
private const string LyricsScrollDurationKey = "LyricsScrollDuration";
private const string IsLyricsFloatAnimationEnabledKey = "IsLyricsFloatAnimationEnabled";
private const string PlaybackOrderKey = "PlaybackOrder";
private const string PositionOffsetKey = "PositionOffset";
private const string LockHotKeyIndexKey = "LockHotKeyIndex";
private const string DockPlacementKey = "DockPlacement";
private const string LyricsBgFontOpacityKey = "LyricsBgFontOpacity";
private const string HideWindowWhenNotPlayingKey = "HideWindowWhenNotPlaying";
private const string DockWindowHeightKey = "DockWindowHeight";
private const string SelectedFontFamilyIndexKey = "SelectedFontFamilyIndex";
private const string LyricsFontFamilyKey = "LyricsFontFamily";
private const string IsDragEverywhereEnabledKey = "IsDragEverywhereEnabled";
private const string DockMonitorDeviceNameKey = "DockMonitorDeviceName";
// LastFM
private const string LastFMSessionKeyKey = "LastFMSessionKey";
private const string LyricsTranslationSeparatorKey = "LyricsTranslationSeparator";
private readonly ApplicationDataContainer _localSettings;
public SettingsService() public SettingsService()
{ {
_localSettings = ApplicationData.Current.LocalSettings; AppSettings = ReadAppSettings();
SetDefault(IsFirstRunKey, true); AppSettings.PropertyChanged += AppSettings_PropertyChanged;
// Lyrics lib
SetDefault(LocalMediaFoldersKey, "[]");
SetDefault(
AlbumArtSearchProvidersInfoKey,
System.Text.Json.JsonSerializer.Serialize(
Enum.GetValues<AlbumArtSearchProvider>()
.Select(p => new AlbumArtSearchProviderInfo(p, true))
.ToList(),
SourceGenerationContext.Default.ListAlbumArtSearchProviderInfo
)
);
if (AlbumArtSearchProvidersInfo.Count != Enum.GetValues<AlbumArtSearchProvider>().Length)
{
AlbumArtSearchProvidersInfo = Enum.GetValues<AlbumArtSearchProvider>()
.Select(p => new AlbumArtSearchProviderInfo(
p,
AlbumArtSearchProvidersInfo
.Where(x => x.Provider == p)
.FirstOrDefault()
?.IsEnabled ?? true
))
.ToList();
}
SetDefault(MediaSourceProvidersInfoKey, "[]"); AppSettings.StandardModeSettings.PropertyChanged += AppSettings_PropertyChanged;
var tmp = MediaSourceProvidersInfo; AppSettings.DesktopModeSettings.PropertyChanged += AppSettings_PropertyChanged;
for (int i = 0; i < tmp.Count; i++) AppSettings.DockModeSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.StandardLyricsStyleSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.DesktopLyricsStyleSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.DockLyricsStyleSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.StandardLyricsEffectSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.DesktopLyricsEffectSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.DockLyricsEffectSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.LyricsBackgroundSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.AlbumArtLayoutSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.TranslationSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.GeneralSettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.MusicGallerySettings.PropertyChanged += AppSettings_PropertyChanged;
AppSettings.MediaSourceProvidersInfo.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
AppSettings.LocalMediaFolders.CollectionChanged += AppSettings_CollectionChanged;
AppSettings.LocalMediaFolders.ItemPropertyChanged += AppSettings_ItemPropertyChanged;
EnsureMediaSourceProvidersInfo();
}
private void EnsureMediaSourceProvidersInfo()
{
// 确保当 LyricsSearchProvider 和 AlbumArtSearchProvider 枚举更新时AppSettings 中的相关信息也能更新
foreach (var x in AppSettings.MediaSourceProvidersInfo)
{ {
var mediaSource = tmp[i]; // 更新 LyricsSearchProvidersInfo
if (mediaSource.LyricsSearchProvidersInfo == null || mediaSource.LyricsSearchProvidersInfo.Count != Enum.GetValues<LyricsSearchProvider>().Length) foreach (var p in Enum.GetValues<LyricsSearchProvider>())
{ {
mediaSource.LyricsSearchProvidersInfo = [..Enum.GetValues<LyricsSearchProvider>() var item = x.LyricsSearchProvidersInfo.FirstOrDefault(i => i.Provider == p);
.Select(p => new LyricsSearchProviderInfo( if (item == null)
p, {
mediaSource.LyricsSearchProvidersInfo? x.LyricsSearchProvidersInfo.Add(new LyricsSearchProviderInfo(p, true));
.Where(x => x.Provider == p) }
.FirstOrDefault() // 可根据需要更新 item.IsEnabled
?.IsEnabled ?? true }
))]; // 移除多余项
for (int i = x.LyricsSearchProvidersInfo.Count - 1; i >= 0; i--)
{
if (!Enum.IsDefined(typeof(LyricsSearchProvider), x.LyricsSearchProvidersInfo[i].Provider))
x.LyricsSearchProvidersInfo.RemoveAt(i);
}
// 更新 AlbumArtSearchProvidersInfo
foreach (var p in Enum.GetValues<AlbumArtSearchProvider>())
{
var item = x.AlbumArtSearchProvidersInfo.FirstOrDefault(i => i.Provider == p);
if (item == null)
{
x.AlbumArtSearchProvidersInfo.Add(new AlbumArtSearchProviderInfo(p, true));
}
// 可根据需要更新 item.IsEnabled
}
for (int i = x.AlbumArtSearchProvidersInfo.Count - 1; i >= 0; i--)
{
if (!Enum.IsDefined(typeof(AlbumArtSearchProvider), x.AlbumArtSearchProvidersInfo[i].Provider))
x.AlbumArtSearchProvidersInfo.RemoveAt(i);
} }
} }
MediaSourceProvidersInfo = tmp;
// App appearance
SetDefault(LanguageKey, (int)Language.FollowSystem);
SetDefault(DesktopWindowHeightKey, 600);
SetDefault(DesktopWindowLeftKey, 200);
SetDefault(DesktopWindowTopKey, 200);
SetDefault(DesktopWindowWidthKey, 1200);
SetDefault(StandardWindowHeightKey, 800);
SetDefault(StandardWindowLeftKey, 200);
SetDefault(StandardWindowTopKey, 200);
SetDefault(StandardWindowWidthKey, 1600);
SetDefault(AutoLockOnDesktopModeKey, false);
SetDefault(IsImmersiveModeKey, false);
// App behavior
SetDefault(AutoStartWindowTypeKey, (int)AutoStartWindowType.StandardMode);
// Album art
SetDefault(IsCoverOverlayEnabledKey, true);
SetDefault(IsDynamicCoverOverlayEnabledKey, true);
SetDefault(CoverOverlayOpacityKey, 100); // 100 % = 1.0
SetDefault(CoverOverlayBlurAmountKey, 100);
SetDefault(CoverImageRadiusKey, 12); // 12 %
SetDefault(CoverAcrylicEffectAmountKey, 0);
// Lyrics
SetDefault(LyricsAlignmentTypeKey, (int)TextAlignmentType.Left);
SetDefault(SongInfoAlignmentTypeKey, (int)TextAlignmentType.Left);
SetDefault(LyricsFontWeightKey, (int)LyricsFontWeight.Bold);
SetDefault(LyricsBlurAmountKey, 5);
SetDefault(LyricsBackgroundThemeKey, (int)ElementTheme.Dark);
SetDefault(LyricsBgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
SetDefault(LyricsFgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
SetDefault(LyricsStrokeFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
SetDefault(LyricsCustomBgFontColorKey, Colors.White.ToInt());
SetDefault(LyricsCustomFgFontColorKey, Colors.White.ToInt());
SetDefault(LyricsCustomStrokeFontColorKey, Colors.White.ToInt());
SetDefault(LyricsStandardFontSizeKey, 32);
SetDefault(LyricsDockFontSizeKey, 16);
SetDefault(LyricsDesktopFontSizeKey, 28);
SetDefault(LyricsLineSpacingFactorKey, 0.5f);
SetDefault(LyricsVerticalEdgeOpacityKey, 0);
SetDefault(IsLyricsGlowEffectEnabledKey, true);
SetDefault(LyricsGlowEffectScopeKey, (int)LineRenderingType.CurrentChar);
SetDefault(LyricsHighlightSopeKey, (int)LineRenderingType.LineStartToCurrentChar);
SetDefault(IsFanLyricsEnabledKey, false);
SetDefault(LibreTranslateServerKey, "");
SetDefault(IsLibreTranslateEnabledKey, false);
SetDefault(IsTranslationEnabledKey, true);
SetDefault(ShowTranslationOnlyKey, false);
SetDefault(SelectedTargetLanguageIndexKey, LanguageHelper.GetDefaultTargetLanguageIndex());
SetDefault(LXMusicServerKey, "");
SetDefault(LyricsFontStrokeWidthKey, 3);
SetDefault(IgnoreFullscreenWindowKey, false);
SetDefault(PreferredDisplayTypeKey, (int)LyricsDisplayType.SplitView);
SetDefault(LyricsScrollEasingTypeKey, (int)EasingType.EaseInOutSine);
SetDefault(LyricsScrollDurationKey, 500); // 500ms
SetDefault(IsLyricsFloatAnimationEnabledKey, true);
SetDefault(PositionOffsetKey, 0);
SetDefault(LockHotKeyIndexKey, 'U' - 'A');
SetDefault(DockPlacementKey, (int)DockPlacement.Top);
SetDefault(LyricsBgFontOpacityKey, 30); // 30%
SetDefault(HideWindowWhenNotPlayingKey, false);
SetDefault(DockWindowHeightKey, 64); // 64px
SetDefault(SelectedFontFamilyIndexKey, 0);
SetDefault(LyricsFontFamilyKey, FontHelper.SystemFontFamilies.ElementAtOrDefault(0));
SetDefault(IsDragEverywhereEnabledKey, false);
SetDefault(DockMonitorDeviceNameKey, MonitorHelper.GetPrimaryMonitorDeviceName());
SetDefault(LastFMSessionKeyKey, "");
SetDefault(LyricsTranslationSeparatorKey, StringHelper.NewLine);
}
public bool IsDragEverywhereEnabled
{
get => GetValue<bool>(IsDragEverywhereEnabledKey);
set => SetValue(IsDragEverywhereEnabledKey, value);
}
public string LyricsFontFamily
{
get => GetValue<string>(LyricsFontFamilyKey)!;
set => SetValue(LyricsFontFamilyKey, value);
}
public int SelectedFontFamilyIndex
{
get => GetValue<int>(SelectedFontFamilyIndexKey);
set => SetValue(SelectedFontFamilyIndexKey, value);
}
public bool HideWindowWhenNotPlaying
{
get => GetValue<bool>(HideWindowWhenNotPlayingKey);
set => SetValue(HideWindowWhenNotPlayingKey, value);
}
public int DockWindowHeight
{
get => GetValue<int>(DockWindowHeightKey);
set => SetValue(DockWindowHeightKey, value);
}
public int LyricsBgFontOpacity
{
get => GetValue<int>(LyricsBgFontOpacityKey);
set => SetValue(LyricsBgFontOpacityKey, value);
}
public bool ShowTranslationOnly
{
get => GetValue<bool>(ShowTranslationOnlyKey);
set => SetValue(ShowTranslationOnlyKey, value);
}
public DockPlacement DockPlacement
{
get => (DockPlacement)GetValue<int>(DockPlacementKey);
set => SetValue(DockPlacementKey, (int)value);
}
public int LockHotKeyIndex
{
get => GetValue<int>(LockHotKeyIndexKey);
set => SetValue(LockHotKeyIndexKey, value);
}
public EasingType LyricsScrollEasingType
{
get => (EasingType)GetValue<int>(LyricsScrollEasingTypeKey);
set => SetValue(LyricsScrollEasingTypeKey, (int)value);
}
public int LyricsScrollDuration
{
get => GetValue<int>(LyricsScrollDurationKey);
set => SetValue(LyricsScrollDurationKey, value);
}
public LyricsDisplayType DisplayType
{
get => (LyricsDisplayType)GetValue<int>(PreferredDisplayTypeKey);
set => SetValue(PreferredDisplayTypeKey, (int)value);
} }
public ElementTheme LyricsBackgroundTheme private void AppSettings_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
{ {
get => (ElementTheme)GetValue<int>(LyricsBackgroundThemeKey); WriteAppSettingsDebounce();
set => SetValue(LyricsBackgroundThemeKey, (int)value);
} }
public AutoStartWindowType AutoStartWindowType private void AppSettings_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{ {
get => (AutoStartWindowType)GetValue<int>(AutoStartWindowTypeKey); WriteAppSettingsDebounce();
set => SetValue(AutoStartWindowTypeKey, (int)value);
} }
public int DesktopWindowLeft private void AppSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
get => GetValue<int>(DesktopWindowLeftKey);
set => SetValue(DesktopWindowLeftKey, value);
}
public int DesktopWindowTop
{
get => GetValue<int>(DesktopWindowTopKey);
set => SetValue(DesktopWindowTopKey, value);
}
public int DesktopWindowWidth
{
get => GetValue<int>(DesktopWindowWidthKey);
set => SetValue(DesktopWindowWidthKey, value);
}
public int DesktopWindowHeight
{
get => GetValue<int>(DesktopWindowHeightKey);
set => SetValue(DesktopWindowHeightKey, value);
}
public int StandardWindowLeft
{
get => GetValue<int>(StandardWindowLeftKey);
set => SetValue(StandardWindowLeftKey, value);
}
public int StandardWindowTop
{
get => GetValue<int>(StandardWindowTopKey);
set => SetValue(StandardWindowTopKey, value);
}
public int StandardWindowWidth
{
get => GetValue<int>(StandardWindowWidthKey);
set => SetValue(StandardWindowWidthKey, value);
}
public int StandardWindowHeight
{
get => GetValue<int>(StandardWindowHeightKey);
set => SetValue(StandardWindowHeightKey, value);
}
public bool AutoLockOnDesktopMode
{
get => GetValue<bool>(AutoLockOnDesktopModeKey);
set => SetValue(AutoLockOnDesktopModeKey, value);
}
public int CoverImageRadius
{
get => GetValue<int>(CoverImageRadiusKey);
set => SetValue(CoverImageRadiusKey, value);
}
public int CoverOverlayBlurAmount
{
get => GetValue<int>(CoverOverlayBlurAmountKey);
set => SetValue(CoverOverlayBlurAmountKey, value);
}
public int CoverOverlayOpacity
{
get => GetValue<int>(CoverOverlayOpacityKey);
set => SetValue(CoverOverlayOpacityKey, value);
}
public bool IsDynamicCoverOverlayEnabled
{
get => GetValue<bool>(IsDynamicCoverOverlayEnabledKey);
set => SetValue(IsDynamicCoverOverlayEnabledKey, value);
}
public int CoverAcrylicEffectAmount
{
get => GetValue<int>(CoverAcrylicEffectAmountKey);
set => SetValue(CoverAcrylicEffectAmountKey, value);
}
public bool IsFanLyricsEnabled
{
get => GetValue<bool>(IsFanLyricsEnabledKey);
set => SetValue(IsFanLyricsEnabledKey, value);
}
public bool IsFirstRun
{
get => GetValue<bool>(IsFirstRunKey);
set => SetValue(IsFirstRunKey, value);
}
public bool IsLyricsGlowEffectEnabled
{
get => GetValue<bool>(IsLyricsGlowEffectEnabledKey);
set => SetValue(IsLyricsGlowEffectEnabledKey, value);
}
public Language Language
{
get => (Language)GetValue<int>(LanguageKey);
set => SetValue(LanguageKey, (int)value);
}
public List<LocalMediaFolder> LocalMediaFolders
{
get =>
System.Text.Json.JsonSerializer.Deserialize(
GetValue<string>(LocalMediaFoldersKey) ?? "[]",
SourceGenerationContext.Default.ListLocalMediaFolder
)!;
set =>
SetValue(
LocalMediaFoldersKey,
System.Text.Json.JsonSerializer.Serialize(
value,
SourceGenerationContext.Default.ListLocalMediaFolder
)
);
}
public TextAlignmentType LyricsAlignmentType
{
get => (TextAlignmentType)GetValue<int>(LyricsAlignmentTypeKey);
set => SetValue(LyricsAlignmentTypeKey, (int)value);
}
public TextAlignmentType SongInfoAlignmentType
{
get => (TextAlignmentType)GetValue<int>(SongInfoAlignmentTypeKey);
set => SetValue(SongInfoAlignmentTypeKey, (int)value);
}
public int LyricsBlurAmount
{
get => GetValue<int>(LyricsBlurAmountKey);
set => SetValue(LyricsBlurAmountKey, value);
}
public Color LyricsCustomBgFontColor
{
get => GetValue<int>(LyricsCustomBgFontColorKey)!.ToColor();
set => SetValue(LyricsCustomBgFontColorKey, value.ToInt());
}
public Color LyricsCustomFgFontColor
{
get => GetValue<int>(LyricsCustomFgFontColorKey)!.ToColor();
set => SetValue(LyricsCustomFgFontColorKey, value.ToInt());
}
public Color LyricsCustomStrokeFontColor
{
get => GetValue<int>(LyricsCustomStrokeFontColorKey)!.ToColor();
set => SetValue(LyricsCustomStrokeFontColorKey, value.ToInt());
}
public LyricsFontColorType LyricsBgFontColorType
{
get => (LyricsFontColorType)GetValue<int>(LyricsBgFontColorTypeKey);
set => SetValue(LyricsBgFontColorTypeKey, (int)value);
}
public LyricsFontColorType LyricsFgFontColorType
{
get => (LyricsFontColorType)GetValue<int>(LyricsFgFontColorTypeKey);
set => SetValue(LyricsFgFontColorTypeKey, (int)value);
}
public LyricsFontColorType LyricsStrokeFontColorType
{
get => (LyricsFontColorType)GetValue<int>(LyricsStrokeFontColorTypeKey);
set => SetValue(LyricsStrokeFontColorTypeKey, (int)value);
}
public int LyricsFontStrokeWidth
{
get => GetValue<int>(LyricsFontStrokeWidthKey);
set => SetValue(LyricsFontStrokeWidthKey, value);
}
public int LyricsStandardFontSize
{
get => GetValue<int>(LyricsStandardFontSizeKey);
set => SetValue(LyricsStandardFontSizeKey, value);
}
public int LyricsDockFontSize
{
get => GetValue<int>(LyricsDockFontSizeKey);
set => SetValue(LyricsDockFontSizeKey, value);
}
public int LyricsDesktopFontSize
{
get => GetValue<int>(LyricsDesktopFontSizeKey);
set => SetValue(LyricsDesktopFontSizeKey, value);
}
public LyricsFontWeight LyricsFontWeight
{
get => (LyricsFontWeight)GetValue<int>(LyricsFontWeightKey);
set => SetValue(LyricsFontWeightKey, (int)value);
}
public LineRenderingType LyricsGlowEffectScope
{
get => (LineRenderingType)GetValue<int>(LyricsGlowEffectScopeKey);
set => SetValue(LyricsGlowEffectScopeKey, (int)value);
}
public LineRenderingType LyricsHighlightScope
{
get => (LineRenderingType)GetValue<int>(LyricsHighlightSopeKey);
set => SetValue(LyricsHighlightSopeKey, (int)value);
}
public float LyricsLineSpacingFactor
{
get => GetValue<float>(LyricsLineSpacingFactorKey);
set => SetValue(LyricsLineSpacingFactorKey, value);
}
public List<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo
{
get =>
System.Text.Json.JsonSerializer.Deserialize(
GetValue<string>(AlbumArtSearchProvidersInfoKey) ?? "[]",
SourceGenerationContext.Default.ListAlbumArtSearchProviderInfo
)!;
set =>
SetValue(
AlbumArtSearchProvidersInfoKey,
System.Text.Json.JsonSerializer.Serialize(
value,
SourceGenerationContext.Default.ListAlbumArtSearchProviderInfo
)
);
}
public List<MediaSourceProviderInfo> MediaSourceProvidersInfo
{
get =>
System.Text.Json.JsonSerializer.Deserialize(
GetValue<string>(MediaSourceProvidersInfoKey) ?? "[]",
SourceGenerationContext.Default.ListMediaSourceProviderInfo
)!;
set =>
SetValue(
MediaSourceProvidersInfoKey,
System.Text.Json.JsonSerializer.Serialize(
value,
SourceGenerationContext.Default.ListMediaSourceProviderInfo
)
);
}
public int LyricsVerticalEdgeOpacity
{
get => GetValue<int>(LyricsVerticalEdgeOpacityKey);
set => SetValue(LyricsVerticalEdgeOpacityKey, value);
}
public string LibreTranslateServer
{
get => GetValue<string>(LibreTranslateServerKey)!;
set => SetValue(LibreTranslateServerKey, value);
}
public bool IsTranslationEnabled
{
get => GetValue<bool>(IsTranslationEnabledKey);
set => SetValue(IsTranslationEnabledKey, value);
}
public bool IsLibreTranslateEnabled
{
get => GetValue<bool>(IsLibreTranslateEnabledKey);
set => SetValue(IsLibreTranslateEnabledKey, value);
}
public int SelectedTargetLanguageIndex
{
get => GetValue<int>(SelectedTargetLanguageIndexKey);
set => SetValue(SelectedTargetLanguageIndexKey, value);
}
public string LXMusicServer
{
get => GetValue<string>(LXMusicServerKey)!;
set => SetValue(LXMusicServerKey, value);
}
public bool IgnoreFullscreenWindow
{
get => GetValue<bool>(IgnoreFullscreenWindowKey);
set => SetValue(IgnoreFullscreenWindowKey, value);
}
public bool IsLyricsFloatAnimationEnabled
{
get => GetValue<bool>(IsLyricsFloatAnimationEnabledKey);
set => SetValue(IsLyricsFloatAnimationEnabledKey, value);
}
public PlaybackOrder PlaybackOrder
{
get => (PlaybackOrder)GetValue<int>(PlaybackOrderKey);
set => SetValue(PlaybackOrderKey, (int)value);
}
public int PositionOffset
{
get => GetValue<int>(PositionOffsetKey);
set => SetValue(PositionOffsetKey, value);
}
public bool IsImmersiveMode
{
get => GetValue<bool>(IsImmersiveModeKey);
set => SetValue(IsImmersiveModeKey, value);
}
public string DockMonitorDeviceName
{
get => GetValue<string>(DockMonitorDeviceNameKey)!;
set => SetValue(DockMonitorDeviceNameKey, value);
}
// LastFM
public string LastFMSessionKey
{
get => GetValue<string>(LastFMSessionKeyKey)!;
set => SetValue(LastFMSessionKeyKey, value);
}
public string LyricsTranslationSeparator
{
get => GetValue<string>(LyricsTranslationSeparatorKey)!;
set => SetValue(LyricsTranslationSeparatorKey, value);
}
// Common methods
private T? GetValue<T>(string key)
{ {
if (_localSettings.Values.TryGetValue(key, out object? value)) switch (e.PropertyName)
{ {
return (T)value; case nameof(GeneralSettings.IsDragEverywhereEnabled):
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
lyricsWindow?.UpdateTitleBarArea();
break;
case nameof(GeneralSettings.Language):
switch (AppSettings.GeneralSettings.Language)
{
case Enums.Language.FollowSystem:
ApplicationLanguages.PrimaryLanguageOverride = "";
break;
case Enums.Language.English:
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
break;
case Enums.Language.SimplifiedChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";
break;
case Enums.Language.TraditionalChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-TW";
break;
case Enums.Language.Japanese:
ApplicationLanguages.PrimaryLanguageOverride = "ja-JP";
break;
case Enums.Language.Korean:
ApplicationLanguages.PrimaryLanguageOverride = "ko-KR";
break;
default:
break;
}
break;
default:
break;
} }
return default; WriteAppSettingsDebounce();
} }
private void SetDefault<T>(string key, T value) /// <summary>
/// Export settings to specific folder
/// </summary>
/// <param name="exportPath">Target folder path (not file path)</param>
public void ExportSettings(string exportPath)
{ {
if (_localSettings.Values.ContainsKey(key) && _localSettings.Values[key] is T) // 导出到文件
return; var exportJson = System.Text.Json.JsonSerializer.Serialize(AppSettings, SourceGenerationContext.Default.AppSettings);
_localSettings.Values[key] = value; File.WriteAllText(Path.Combine(exportPath, $"BetterLyrics_Settings_Export_{DateTime.Now:yyyyMMdd_HHmmss}.json"), exportJson);
} }
private void SetValue<T>(string key, T value) /// <summary>
/// Indicate a value whether import action is successfullt done
/// </summary>
/// <param name="importPath"></param>
/// <returns></returns>
public bool ImportSettings(string importPath)
{ {
_localSettings.Values[key] = value; // TODO 导入有问题
if (!File.Exists(importPath))
return false;
var importJson = File.ReadAllText(importPath);
var importData = System.Text.Json.JsonSerializer.Deserialize(importJson, SourceGenerationContext.Default.AppSettings);
if (importData == null)
return false;
AppSettings = importData;
SaveAppSettings();
return true;
}
private static AppSettings ReadAppSettings()
{
if (!File.Exists(PathHelper.SettingsFilePath))
return new AppSettings();
var json = File.ReadAllText(PathHelper.SettingsFilePath);
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.AppSettings);
if (data == null)
return new AppSettings();
return data;
}
private void WriteAppSettingsDebounce()
{
_dispatcherQueueTimer.Debounce(() =>
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
SaveAppSettings();
});
}, Constants.Time.DebounceTimeout);
}
private void SaveAppSettings()
{
File.WriteAllText(PathHelper.SettingsFilePath, System.Text.Json.JsonSerializer.Serialize(AppSettings, SourceGenerationContext.Default.AppSettings));
} }
} }
} }

View File

@@ -18,10 +18,12 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
{ {
public class TranslateService : BaseViewModel, ITranslateService public class TranslateService : BaseViewModel, ITranslateService
{ {
private readonly ISettingsService _settingsService;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public TranslateService(ISettingsService settingsService) : base(settingsService) public TranslateService(ISettingsService settingsService)
{ {
_settingsService = settingsService;
_httpClient = new HttpClient(); _httpClient = new HttpClient();
} }
@@ -46,12 +48,12 @@ namespace BetterLyrics.WinUI3.Services.TranslateService
return ChineseConverter.ConvertToTraditionalChinese(text); return ChineseConverter.ConvertToTraditionalChinese(text);
} }
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer)) if (string.IsNullOrEmpty(_settingsService.AppSettings.TranslationSettings.LibreTranslateServer))
{ {
throw new Exception("LibreTranslate server URL is not set in settings."); throw new Exception("LibreTranslate server URL is not set in settings.");
} }
var url = $"{_settingsService.LibreTranslateServer}/translate"; var url = $"{_settingsService.AppSettings.TranslationSettings.LibreTranslateServer}/translate";
var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent( var response = await _httpClient.PostAsync(url, new FormUrlEncodedContent(
[ [
new("q", text), new("q", text),

View File

@@ -0,0 +1,25 @@
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class AlbumArtLayoutSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public AlbumArtLayoutSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
}
}

View File

@@ -0,0 +1,25 @@
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class AllLyricsSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public AllLyricsSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
}
}

View File

@@ -0,0 +1,77 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.ApplicationModel;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class AppSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
public partial ObservableCollection<string> MonitorDeviceNames { get; set; }
public AppSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
AppSettings = _settingsService.AppSettings;
}
[RelayCommand]
private static void RestartApp()
{
WindowHelper.RestartApp();
}
[RelayCommand]
private void RefreshMonitorDeviceNames()
{
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
AppSettings.DockModeSettings.DockMonitorDeviceName = MonitorHelper.GetPrimaryMonitorDeviceName();
}
public async Task<bool> ToggleAutoStartupAsync(bool target)
{
StartupTask startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);
if (target)
{
await startupTask.RequestEnableAsync();
}
else
{
startupTask.Disable();
}
return await DetectIsAutoStartupEnabledAsync();
}
public async Task<bool> DetectIsAutoStartupEnabledAsync()
{
bool result = false;
var startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);
switch (startupTask.State)
{
case StartupTaskState.Disabled:
case StartupTaskState.DisabledByUser:
case StartupTaskState.DisabledByPolicy:
result = false;
break;
case StartupTaskState.Enabled:
result = true;
break;
}
return result;
}
}
}

View File

@@ -12,15 +12,13 @@ namespace BetterLyrics.WinUI3.ViewModels
{ {
private protected readonly DispatcherQueue _dispatcherQueue; private protected readonly DispatcherQueue _dispatcherQueue;
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer; private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
private protected readonly ISettingsService _settingsService;
public BaseViewModel(ISettingsService settingsService) public BaseViewModel()
{ {
IsActive = true; IsActive = true;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread(); _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer(); _dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
_settingsService = settingsService;
} }
} }
} }

View File

@@ -11,7 +11,7 @@ using Microsoft.UI.Xaml;
namespace BetterLyrics.WinUI3.ViewModels namespace BetterLyrics.WinUI3.ViewModels
{ {
public partial class BaseWindowViewModel(ISettingsService settingsService) : BaseViewModel(settingsService) public partial class BaseWindowViewModel : BaseViewModel
{ {
} }
} }

View File

@@ -0,0 +1,25 @@
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class LyricsBackgroundSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public LyricsBackgroundSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
}
}

View File

@@ -3,6 +3,7 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService; using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views; using BetterLyrics.WinUI3.Views;
@@ -24,32 +25,27 @@ namespace BetterLyrics.WinUI3.ViewModels
IRecipient<PropertyChangedMessage<bool>>, IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<int>>, IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<string>>, IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<TimeSpan>>, IRecipient<PropertyChangedMessage<TimeSpan>>
IRecipient<PropertyChangedMessage<LyricsSearchProvider?>>,
IRecipient<PropertyChangedMessage<TranslationSearchProvider?>>
{ {
private readonly IMediaSessionsService _mediaSessionsService; private readonly IMediaSessionsService _mediaSessionsService;
private readonly ISettingsService _settingsService;
private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1)); private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1));
private bool _isDockMode = false; private bool _isDockMode = false;
private bool _isDesktopMode = false; private bool _isDesktopMode = false;
private int _lyricsStandardFontSize = 8; public LyricsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService)
private int _lyricsDockFontSize = 8;
private int _lyricsDesktopFontSize = 8;
public LyricsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService) : base(settingsService)
{ {
IsFirstRun = _settingsService.IsFirstRun; _settingsService = settingsService;
IsTranslationEnabled = _settingsService.IsTranslationEnabled; IsTranslationEnabled = _settingsService.AppSettings.TranslationSettings.IsTranslationEnabled;
DisplayType = _settingsService.DisplayType; DisplayType = _settingsService.AppSettings.GeneralSettings.DisplayType;
PositionOffset = _settingsService.PositionOffset; IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
IsImmersiveMode = _settingsService.IsImmersiveMode; ShowTranslationOnly = _settingsService.AppSettings.TranslationSettings.ShowTranslationOnly;
ShowTranslationOnly = _settingsService.ShowTranslationOnly;
UpdateHintMessageFontSize(); UpdateHintMessageFontSize();
LyricsFontFamily = _settingsService.LyricsFontFamily; LyricsFontFamily = _settingsService.AppSettings.StandardLyricsStyleSettings.LyricsFontFamily;
OnIsImmersiveModeChanged(IsImmersiveMode); OnIsImmersiveModeChanged(IsImmersiveMode);
@@ -104,28 +100,18 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial bool IsImmersiveMode { get; set; } public partial bool IsImmersiveMode { get; set; }
[ObservableProperty] [ObservableProperty]
public partial float BottomCommandGridOpacity { get; set; } public partial double BottomCommandGridOpacity { get; set; }
[ObservableProperty] [ObservableProperty]
public partial float BottomCommandFlyoutTriggerOpacity { get; set; } public partial double BottomCommandFlyoutTriggerOpacity { get; set; }
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedRecipients] [NotifyPropertyChangedRecipients]
public partial LyricsDisplayType DisplayType { get; set; } public partial LyricsDisplayType DisplayType { get; set; }
[ObservableProperty]
public partial bool IsFirstRun { get; set; }
[ObservableProperty]
public partial bool IsWelcomeTeachingTipOpen { get; set; }
[ObservableProperty] [ObservableProperty]
public partial SongInfo? SongInfo { get; set; } = null; public partial SongInfo? SongInfo { get; set; } = null;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int PositionOffset { get; set; }
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedRecipients] [NotifyPropertyChangedRecipients]
public partial bool IsTranslationEnabled { get; set; } public partial bool IsTranslationEnabled { get; set; }
@@ -137,25 +123,19 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty] [ObservableProperty]
public partial bool IsSongPlaying { get; set; } public partial bool IsSongPlaying { get; set; }
[ObservableProperty]
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
[ObservableProperty]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
private void UpdateHintMessageFontSize() private void UpdateHintMessageFontSize()
{ {
if (_isDockMode) if (_isDockMode)
{ {
HintMessageFontSize = _settingsService.LyricsDockFontSize; HintMessageFontSize = _settingsService.AppSettings.DockLyricsStyleSettings.LyricsFontSize;
} }
else if (_isDesktopMode) else if (_isDesktopMode)
{ {
HintMessageFontSize = _settingsService.LyricsDesktopFontSize; HintMessageFontSize = _settingsService.AppSettings.DesktopLyricsStyleSettings.LyricsFontSize;
} }
else else
{ {
HintMessageFontSize = _settingsService.LyricsStandardFontSize; HintMessageFontSize = _settingsService.AppSettings.StandardLyricsStyleSettings.LyricsFontSize;
} }
} }
@@ -172,7 +152,7 @@ namespace BetterLyrics.WinUI3.ViewModels
} }
else else
{ {
DisplayType = _settingsService.DisplayType; DisplayType = _settingsService.AppSettings.GeneralSettings.DisplayType;
} }
UpdateHintMessageFontSize(); UpdateHintMessageFontSize();
} }
@@ -185,7 +165,7 @@ namespace BetterLyrics.WinUI3.ViewModels
} }
else else
{ {
DisplayType = _settingsService.DisplayType; DisplayType = _settingsService.AppSettings.GeneralSettings.DisplayType;
} }
UpdateHintMessageFontSize(); UpdateHintMessageFontSize();
} }
@@ -226,20 +206,9 @@ namespace BetterLyrics.WinUI3.ViewModels
await _mediaSessionsService.NextAsync(); await _mediaSessionsService.NextAsync();
} }
partial void OnIsFirstRunChanged(bool value)
{
IsWelcomeTeachingTipOpen = value;
_settingsService.IsFirstRun = false;
}
partial void OnIsTranslationEnabledChanged(bool value) partial void OnIsTranslationEnabledChanged(bool value)
{ {
_settingsService.IsTranslationEnabled = value; _settingsService.AppSettings.TranslationSettings.IsTranslationEnabled = value;
}
partial void OnPositionOffsetChanged(int value)
{
_settingsService.PositionOffset = value;
} }
partial void OnIsImmersiveModeChanged(bool value) partial void OnIsImmersiveModeChanged(bool value)
@@ -258,22 +227,14 @@ namespace BetterLyrics.WinUI3.ViewModels
partial void OnShowTranslationOnlyChanged(bool value) partial void OnShowTranslationOnlyChanged(bool value)
{ {
_settingsService.ShowTranslationOnly = value; _settingsService.AppSettings.TranslationSettings.ShowTranslationOnly = value;
} }
public void Receive(PropertyChangedMessage<int> message) public void Receive(PropertyChangedMessage<int> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStandardFontSize)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontSize))
{
UpdateHintMessageFontSize();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDockFontSize))
{
UpdateHintMessageFontSize();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDesktopFontSize))
{ {
UpdateHintMessageFontSize(); UpdateHintMessageFontSize();
} }
@@ -282,9 +243,9 @@ namespace BetterLyrics.WinUI3.ViewModels
public void Receive(PropertyChangedMessage<string> message) public void Receive(PropertyChangedMessage<string> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontFamily)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontFamily))
{ {
LyricsFontFamily = message.NewValue; LyricsFontFamily = message.NewValue;
} }
@@ -312,27 +273,5 @@ namespace BetterLyrics.WinUI3.ViewModels
} }
} }
} }
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
{
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
{
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.LyricsSearchProvider))
{
LyricsSearchProvider = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<TranslationSearchProvider?> message)
{
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
{
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.TranslationSearchProvider))
{
TranslationSearchProvider = message.NewValue;
}
}
}
} }
} }

View File

@@ -1,4 +1,6 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LastFMService; using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService; using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LyricsSearchService; using BetterLyrics.WinUI3.Services.LyricsSearchService;
@@ -21,8 +23,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
ILibWatcherService libWatcherService, ILibWatcherService libWatcherService,
ITranslateService libreTranslateService, ITranslateService libreTranslateService,
ILastFMService lastFMService ILastFMService lastFMService
) : base(settingsService) )
{ {
_settingsService = settingsService;
_lyrcsSearchService = musicSearchService; _lyrcsSearchService = musicSearchService;
_mediaSessionsService = mediaSessionsService; _mediaSessionsService = mediaSessionsService;
_libWatcherService = libWatcherService; _libWatcherService = libWatcherService;
@@ -30,69 +33,22 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_lastFMService = lastFMService; _lastFMService = lastFMService;
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo;
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>(); _logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
_albumArtCornerRadius = _settingsService.CoverImageRadius; _settingsService.AppSettings.MediaSourceProvidersInfo.ItemPropertyChanged += MediaSourceProvidersInfo_ItemPropertyChanged;
_isDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled; _settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
_albumArtBgOpacity = _settingsService.CoverOverlayOpacity; _settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
_albumArtBgBlurAmount = _settingsService.CoverOverlayBlurAmount;
_coverAcrylicEffectAmount = _settingsService.CoverAcrylicEffectAmount;
_lyricsBgFontColorType = _settingsService.LyricsBgFontColorType; _lyricsStyleSettings = _settingsService.AppSettings.StandardLyricsStyleSettings;
_lyricsFgFontColorType = _settingsService.LyricsFgFontColorType; _lyricsEffectSettings = _settingsService.AppSettings.StandardLyricsEffectSettings;
_lyricsTextFormat.FontWeight = _settingsService.LyricsFontWeight.ToFontWeight(); _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = _settingsService.LyricsFontFamily;
_lyricsAlignmentType = _settingsService.LyricsAlignmentType;
_lyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
_lyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
_lyricsStandardFontSize = _settingsService.LyricsStandardFontSize;
_lyricsDockFontSize = _settingsService.LyricsDockFontSize;
_lyricsDesktopFontSize = _settingsService.LyricsDesktopFontSize;
_lyricsBlurAmount = _settingsService.LyricsBlurAmount;
_isLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
_lyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
_lyricsHighlightScope = _settingsService.LyricsHighlightScope;
_customBgFontColor = _settingsService.LyricsCustomBgFontColor;
_customFgFontColor = _settingsService.LyricsCustomFgFontColor;
_lyricsBgTheme = _settingsService.LyricsBackgroundTheme;
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
// 歌词描边
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
_lyricsStrokeFontColorType = _settingsService.LyricsStrokeFontColorType;
_customStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
_showTranslationOnly = _settingsService.ShowTranslationOnly;
_isLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled;
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
_lyricsTranslationSeparator = _settingsService.LyricsTranslationSeparator;
_dockPlacement = _settingsService.DockPlacement;
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
_timelineSyncThreshold = 0; _timelineSyncThreshold = 0;
_canvasYScrollTransition.SetDuration(_settingsService.LyricsScrollDuration / 1000f); _displayType = _displayTypeReceived = _settingsService.AppSettings.GeneralSettings.DisplayType;
_canvasYScrollTransition.SetEasingType(_settingsService.LyricsScrollEasingType);
_defaultOpacity = _settingsService.LyricsBgFontOpacity / 100f;
_isLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled; _libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
_displayType = _displayTypeReceived = _settingsService.DisplayType;
_libWatcherService.MusicLibraryFilesChanged +=
LibWatcherService_MusicLibraryFilesChanged;
_mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged; _mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
_mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged; _mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged;
@@ -103,5 +59,41 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateColorConfig(); UpdateColorConfig();
} }
private void LocalMediaFolders_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
{
// Music lib changed, re-fetch lyrics
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async tokne =>
{
await RefreshLyricsAsync(tokne);
});
}
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// Music lib changed, re-fetch lyrics
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async tokne =>
{
await RefreshLyricsAsync(tokne);
});
}
private void MediaSourceProvidersInfo_ItemPropertyChanged(object? sender, Extensions.ItemPropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(MediaSourceProviderInfo.LyricsSearchProvidersInfo):
_logger.LogInformation("MediaSourceProviderInfo.LyricsSearchProvidersInfo changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async token =>
{
await RefreshLyricsAsync(token);
});
break;
default:
break;
}
}
} }
} }

View File

@@ -35,15 +35,15 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (_isDockMode) if (_isDockMode)
{ {
FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _albumArtBgOpacity / 100f); FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _settingsService.AppSettings.LyricsBackgroundSettings.PureColorOverlayOpacity / 100f);
} }
else if (_isDesktopMode) else if (_isDesktopMode)
{ {
FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _albumArtBgOpacity / 100f); FillBackground(control, combinedDs, _immersiveBgColorTransition.Value, 0f, _immersiveBgOpacityTransition.Value * _settingsService.AppSettings.LyricsBackgroundSettings.PureColorOverlayOpacity / 100f);
} }
else else
{ {
FillBackground(control, combinedDs, _albumArtAccentColorTransition.Value, 0f, _albumArtBgOpacity / 100f); FillBackground(control, combinedDs, _albumArtAccentColorTransition.Value, 0f, _settingsService.AppSettings.LyricsBackgroundSettings.PureColorOverlayOpacity / 100f);
DrawAlbumArtBackground(control, combinedDs); DrawAlbumArtBackground(control, combinedDs);
} }
@@ -68,7 +68,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_playingLineIndex, _playingLineIndex,
out int charStartIndex, out int charStartIndex,
out int charLength, out int charLength,
out float charProgress out double charProgress
); );
ds.DrawText( ds.DrawText(
@@ -102,21 +102,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void DrawBackgroundImgae(OpacityEffect effect, CanvasDrawingSession ds, CanvasBitmap canvasBitmap) private void DrawBackgroundImgae(OpacityEffect effect, CanvasDrawingSession ds, CanvasBitmap canvasBitmap)
{ {
float imageWidth = (float)canvasBitmap.Size.Width; double imageWidth = (double)canvasBitmap.Size.Width;
float imageHeight = (float)canvasBitmap.Size.Height; double imageHeight = (double)canvasBitmap.Size.Height;
float targetSize = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)); double targetSize = Math.Sqrt(Math.Pow(_canvasWidth, 2) + Math.Pow(_canvasHeight, 2));
float scaleFactor = targetSize / MathF.Min(imageWidth, imageHeight); double scaleFactor = targetSize / Math.Min(imageWidth, imageHeight);
float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2; double x = _canvasWidth / 2 - imageWidth * scaleFactor / 2;
float y = _canvasHeight / 2 - imageHeight * scaleFactor / 2; double y = _canvasHeight / 2 - imageHeight * scaleFactor / 2;
ds.DrawImage(effect, new Vector2(x, y)); ds.DrawImage(effect, new Vector2((float)x, (float)y));
} }
private void DrawForegroundImgae(OpacityEffect effect, CanvasDrawingSession ds) private void DrawForegroundImgae(OpacityEffect effect, CanvasDrawingSession ds)
{ {
ds.DrawImage(effect, new Vector2(_albumArtXTransition.Value, _albumArtYTransition.Value)); ds.DrawImage(effect, new Vector2((float)_albumArtXTransition.Value, (float)_albumArtYTransition.Value));
} }
private void DrawAlbumArtBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds) private void DrawAlbumArtBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds)
@@ -126,7 +126,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
return; return;
} }
ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f); ds.Transform = Matrix3x2.CreateRotation((float)_rotateAngle, control.Size.ToVector2() * 0.5f);
ds.DrawImage(_albumArtBgEffect); ds.DrawImage(_albumArtBgEffect);
ds.Transform = Matrix3x2.Identity; ds.Transform = Matrix3x2.Identity;
} }
@@ -158,7 +158,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
ds.DrawImage(new OpacityEffect ds.DrawImage(new OpacityEffect
{ {
Source = opacity, Source = opacity,
Opacity = _albumArtOpacityTransition.Value Opacity = (float)_albumArtOpacityTransition.Value
}); });
} }
@@ -174,7 +174,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
} }
} }
private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, float opacity) private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, double opacity)
{ {
var maxWidth = _lyricsLayoutOrientation switch var maxWidth = _lyricsLayoutOrientation switch
{ {
@@ -189,19 +189,19 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using CanvasTextLayout titleLayout = new( using CanvasTextLayout titleLayout = new(
control, title ?? string.Empty, control, title ?? string.Empty,
_titleTextFormat, maxWidth, _canvasHeight _titleTextFormat, (float)maxWidth, (float)_canvasHeight
); );
using CanvasTextLayout artistLayout = new( using CanvasTextLayout artistLayout = new(
control, artist ?? string.Empty, control, artist ?? string.Empty,
_artistTextFormat, maxWidth, _canvasHeight _artistTextFormat, (float)maxWidth, (float)_canvasHeight
); );
ds.DrawTextLayout( ds.DrawTextLayout(
titleLayout, titleLayout,
new Vector2(_titleXTransition.Value, _titleYTransition.Value), new Vector2((float)_titleXTransition.Value, (float)_titleYTransition.Value),
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity))); _bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity)));
ds.DrawTextLayout( ds.DrawTextLayout(
artistLayout, artistLayout,
new Vector2(_titleXTransition.Value, _titleYTransition.Value + (float)titleLayout.LayoutBounds.Height), new Vector2((float)_titleXTransition.Value, (float)(_titleYTransition.Value + titleLayout.LayoutBounds.Height)),
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity))); _bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity)));
} }
@@ -224,18 +224,18 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
var textLayout = line.CanvasTextLayout; var textLayout = line.CanvasTextLayout;
if (textLayout == null) continue; if (textLayout == null) continue;
float layoutWidth = (float)textLayout.LayoutBounds.Width; double layoutWidth = (double)textLayout.LayoutBounds.Width;
float layoutHeight = (float)textLayout.LayoutBounds.Height; double layoutHeight = (double)textLayout.LayoutBounds.Height;
if (layoutWidth <= 0 || layoutHeight <= 0) continue; if (layoutWidth <= 0 || layoutHeight <= 0) continue;
float yOffset = _canvasYScrollTransition.Value + _canvasHeight / 2 + _lyricsYTransition.Value; double yOffset = line.YOffsetTransition.Value + _canvasHeight / 2 + _lyricsYTransition.Value;
// 组合变换:缩放 -> 旋转 -> 平移 // 组合变换:缩放 -> 旋转 -> 平移
ds.Transform = ds.Transform =
Matrix3x2.CreateScale(line.ScaleTransition.Value, line.CenterPosition) Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition)
* Matrix3x2.CreateRotation(line.AngleTransition.Value, currentPlayingLine.Position) * Matrix3x2.CreateRotation((float)line.AngleTransition.Value, currentPlayingLine.Position)
* Matrix3x2.CreateTranslation(_lyricsXTransition.Value, yOffset); * Matrix3x2.CreateTranslation((float)_lyricsXTransition.Value, (float)yOffset);
if (line.BackgroundFontEffect == null || line.ForegroundFontEffect == null) continue; if (line.BackgroundFontEffect == null || line.ForegroundFontEffect == null) continue;
@@ -245,183 +245,35 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// Mock gradient blurred lyrics layer // Mock gradient blurred lyrics layer
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次) // 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
// Current line will not be blurred // Current line will not be blurred
combinedDs.DrawImage( combinedDs.DrawImage(line.BackgroundEffect);
new OpacityEffect
{
Source = new GaussianBlurEffect
{
Source = line.BackgroundFontEffect,
BlurAmount = line.BlurAmountTransition.Value,
BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Speed,
},
Opacity = line.OpacityTransition.Value * _lyricsOpacityTransition.Value,
}
);
if (line.HighlightOpacityTransition.Value != 0) if (line.HighlightOpacityTransition.Value != 0)
{ {
// 再叠加高亮行歌词层(前景歌词层) if (line.ForegroundBlurEffect == null || line.ForegroundHighlightEffect == null || line.PlaceholderEffect == null)
using var mask = new CanvasCommandList(control.Device);
using var maskDs = mask.CreateDrawingSession();
using var highlightMask = new CanvasCommandList(control.Device);
using var highlightMaskDs = highlightMask.CreateDrawingSession();
if (i == _playingLineIndex)
{ {
GetLinePlayingProgress( return;
i,
out int charStartIndex,
out int charLength,
out float charProgress
);
var regions = textLayout.GetCharacterRegions(0, charStartIndex);
var highlightRegion = textLayout
.GetCharacterRegions(charStartIndex, charLength)
.FirstOrDefault();
if (regions.Length > 0)
{
// Draw the mask for the current line
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + line.Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
maskDs.FillRectangle(rect, Color.FromArgb(255, 128, 128, 128));
}
}
float highlightTotalWidth = (float)highlightRegion.LayoutBounds.Width;
// Draw the highlight for the current character
float highlightWidth = highlightTotalWidth * charProgress;
float fadingWidth = (float)highlightRegion.LayoutBounds.Height / 2;
// Rects
var highlightRect = new Rect(
highlightRegion.LayoutBounds.X,
highlightRegion.LayoutBounds.Y + line.Position.Y,
highlightWidth,
highlightRegion.LayoutBounds.Height
);
var fadeInRect = new Rect(
highlightRect.Right - fadingWidth,
highlightRegion.LayoutBounds.Y + line.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
var fadeOutRect = new Rect(
highlightRect.Right,
highlightRegion.LayoutBounds.Y + line.Position.Y,
fadingWidth,
highlightRegion.LayoutBounds.Height
);
// Brushes
using var fadeInBrush = CreateHorizontalFillBrush(
control,
[(0f, 0f), (1f, 1f)],
(float)highlightRect.Right - fadingWidth,
fadingWidth
);
using var fadeOutBrush = CreateHorizontalFillBrush(
control,
[(0f, 1f), (1f, 0f)],
(float)highlightRect.Right,
fadingWidth
);
maskDs.FillRectangle(highlightRect, Color.FromArgb(255, 128, 128, 128));
maskDs.FillRectangle(fadeOutRect, fadeOutBrush);
highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush);
highlightMaskDs.FillRectangle(fadeOutRect, fadeOutBrush);
}
else
{
//float height = 0f;
var regions = textLayout.GetCharacterRegions(0, line.OriginalText.Length);
if (regions.Length > 0)
{
//height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top;
for (int j = 0; j < regions.Length; j++)
{
var region = regions[j];
var rect = new Rect(
region.LayoutBounds.X,
region.LayoutBounds.Y + line.Position.Y,
region.LayoutBounds.Width,
region.LayoutBounds.Height
);
maskDs.FillRectangle(rect, Colors.White);
}
}
//maskDs.FillRectangle(
// new Rect(
// textLayout.LayoutBounds.X,
// line.Position.Y,
// textLayout.LayoutBounds.Width,
// height
// ),
// Colors.White
//);
} }
using var opacityEffect = new OpacityEffect using var opacityEffect = new OpacityEffect
{ {
Source = new BlendEffect Source = new BlendEffect
{ {
Background = _isLyricsGlowEffectEnabled Background = _lyricsEffectSettings.IsLyricsGlowEffectEnabled ? line.ForegroundBlurEffect : line.PlaceholderEffect,
? new GaussianBlurEffect Foreground = line.ForegroundHighlightEffect,
{
Source = new AlphaMaskEffect
{
Source = line.ForegroundFontEffect,
AlphaMask = _lyricsGlowEffectScope switch
{
LineRenderingType.CurrentChar => highlightMask,
LineRenderingType.LineStartToCurrentChar => mask,
LineRenderingType.CurrentLine => line.ForegroundFontEffect,
_ => mask,
},
},
BlurAmount = _lyricsGlowEffectAmount,
Optimization = EffectOptimization.Speed,
}
: new CanvasCommandList(control.Device),
Foreground = new AlphaMaskEffect
{
Source = line.ForegroundFontEffect,
AlphaMask = _lyricsHighlightScope switch
{
LineRenderingType.CurrentChar => highlightMask,
LineRenderingType.LineStartToCurrentChar => mask,
LineRenderingType.CurrentLine => line.ForegroundFontEffect,
_ => mask,
},
},
}, },
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value, Opacity = (float)(line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value),
}; };
combinedDs.DrawImage(opacityEffect); combinedDs.DrawImage(opacityEffect);
if (i == _playingLineIndex) if (i == _playingLineIndex)
{ {
if (_isLyricsFloatAnimationEnabled) if (_lyricsEffectSettings.IsLyricsFloatAnimationEnabled)
{ {
ds.DrawImage(new DisplacementMapEffect ds.DrawImage(new DisplacementMapEffect
{ {
Source = combined, Source = combined,
Displacement = mask, Displacement = line.LineStartToCurrentCharMask,
XChannelSelect = EffectChannelSelect.Red, XChannelSelect = EffectChannelSelect.Red,
YChannelSelect = EffectChannelSelect.Alpha, YChannelSelect = EffectChannelSelect.Alpha,
Amount = 1f, Amount = 1f,
@@ -444,65 +296,44 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// Reset scale // Reset scale
ds.Transform = Matrix3x2.Identity; ds.Transform = Matrix3x2.Identity;
line.DisposeFontEffects();
line.DisposeTextGeometry();
} }
} }
private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, float radius, float opacity) private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, double radius, double opacity)
{ {
ds.FillRoundedRectangle( ds.FillRoundedRectangle(
new Rect(0, 0, _canvasWidth, _canvasHeight), new Rect(0, 0, _canvasWidth, _canvasHeight),
radius, (float)radius,
radius, (float)radius,
color.WithAlpha((byte)(opacity * 255)) color.WithAlpha((byte)(opacity * 255))
); );
} }
private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasLinearGradientBrush brush, float radius, float opacity) private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasLinearGradientBrush brush, double radius, double opacity)
{ {
ds.FillRoundedRectangle( ds.FillRoundedRectangle(
new Rect(0, 0, _canvasWidth, _canvasHeight), new Rect(0, 0, _canvasWidth, _canvasHeight),
radius, (float)radius,
radius, (float)radius,
brush brush
); );
} }
private CanvasLinearGradientBrush CreateHorizontalFillBrush(
ICanvasAnimatedControl control,
List<(float position, float opacity)> stops,
float startX,
float width
)
{
return new CanvasLinearGradientBrush(control, stops.Select(stops => new CanvasGradientStop
{
Position = stops.position,
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
}).ToArray())
{
StartPoint = new Vector2(startX, 0),
EndPoint = new Vector2(startX + width, 0),
};
}
private CanvasLinearGradientBrush CreateVerticalFillBrush( private CanvasLinearGradientBrush CreateVerticalFillBrush(
ICanvasAnimatedControl control, ICanvasAnimatedControl control,
List<(float position, Color color)> stops, List<(double position, Color color)> stops,
float startY, double startY,
float height double height
) )
{ {
return new CanvasLinearGradientBrush(control, stops.Select(x => new CanvasGradientStop return new CanvasLinearGradientBrush(control, stops.Select(x => new CanvasGradientStop
{ {
Position = x.position, Position = (float)x.position,
Color = x.color, Color = x.color,
}).ToArray()) }).ToArray())
{ {
StartPoint = new Vector2(0, startY), StartPoint = new Vector2(0, (float)startY),
EndPoint = new Vector2(0, startY + height), EndPoint = new Vector2(0, (float)(startY + height)),
}; };
} }
} }

View File

@@ -19,21 +19,21 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private CanvasCommandList? _albumArtBgEffect; private CanvasCommandList? _albumArtBgEffect;
private OpacityEffect CreateBgImageEffect(CanvasBitmap canvasBitmap, float opacity) private OpacityEffect CreateBgImageEffect(CanvasBitmap canvasBitmap, double opacity)
{ {
float imageWidth = (float)canvasBitmap.Size.Width; double imageWidth = (double)canvasBitmap.Size.Width;
float imageHeight = (float)canvasBitmap.Size.Height; double imageHeight = (double)canvasBitmap.Size.Height;
float targetSize = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)); double targetSize = Math.Sqrt(Math.Pow(_canvasWidth, 2) + Math.Pow(_canvasHeight, 2));
float scaleFactor = targetSize / MathF.Min(imageWidth, imageHeight); double scaleFactor = targetSize / Math.Min(imageWidth, imageHeight);
// Original source: https://zhuanlan.zhihu.com/p/37178216 // Original source: https://zhuanlan.zhihu.com/p/37178216
float gain = _lyricsBgBrightnessTransition.Value; double gain = _lyricsBgBrightnessTransition.Value;
float whiteX = 1 - 0.5f * gain; double whiteX = 1 - 0.5f * gain;
float whiteY = 0.5f + 0.5f * gain; double whiteY = 0.5f + 0.5f * gain;
float blackX = 0.5f - 0.5f * gain; double blackX = 0.5f - 0.5f * gain;
float blackY = 0 + 0.5f * gain; double blackY = 0 + 0.5f * gain;
return new OpacityEffect return new OpacityEffect
{ {
@@ -41,34 +41,33 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
Source = new ScaleEffect Source = new ScaleEffect
{ {
Scale = new Vector2(scaleFactor), Scale = new Vector2((float)scaleFactor),
Source = canvasBitmap, Source = canvasBitmap,
}, },
WhitePoint = new Vector2(whiteX, whiteY), WhitePoint = new Vector2((float)whiteX, (float)whiteY),
BlackPoint = new Vector2(blackX, blackY), BlackPoint = new Vector2((float)blackX, (float)blackY),
}, },
Opacity = opacity, Opacity = (float)opacity,
}; };
} }
private OpacityEffect? CreateFgImageEffect(ICanvasAnimatedControl control, CanvasBitmap canvasBitmap, float opacity) private OpacityEffect? CreateFgImageEffect(ICanvasAnimatedControl control, CanvasBitmap canvasBitmap, double opacity)
{ {
// TODO 最大化/还原时图片大小未跟随改变
if (opacity == 0) return null; if (opacity == 0) return null;
float imageWidth = (float)canvasBitmap.Size.Width; double imageWidth = (double)canvasBitmap.Size.Width;
float imageHeight = (float)canvasBitmap.Size.Height; double imageHeight = (double)canvasBitmap.Size.Height;
float scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight); double scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight);
if (scaleFactor < 0.01f) return null; if (scaleFactor < 0.01f) return null;
float cornerRadius = _albumArtCornerRadius / 100f * _albumArtSize / 2; double cornerRadius = _settingsService.AppSettings.AlbumArtLayoutSettings.CoverImageRadius / 100f * _albumArtSize / 2;
var cornerRadiusMask = new CanvasCommandList(control); var cornerRadiusMask = new CanvasCommandList(control);
using var cornerRadiusMaskDs = cornerRadiusMask.CreateDrawingSession(); using var cornerRadiusMaskDs = cornerRadiusMask.CreateDrawingSession();
cornerRadiusMaskDs.FillRoundedRectangle( cornerRadiusMaskDs.FillRoundedRectangle(
new Rect(0, 0, imageWidth * scaleFactor, imageHeight * scaleFactor), new Rect(0, 0, imageWidth * scaleFactor, imageHeight * scaleFactor),
cornerRadius, cornerRadius, Colors.White (float)cornerRadius, (float)cornerRadius, Colors.White
); );
return new OpacityEffect return new OpacityEffect
@@ -77,12 +76,12 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
Source = new ScaleEffect Source = new ScaleEffect
{ {
Scale = new Vector2(scaleFactor), Scale = new Vector2((float)scaleFactor),
Source = canvasBitmap, Source = canvasBitmap,
}, },
AlphaMask = cornerRadiusMask, AlphaMask = cornerRadiusMask,
}, },
Opacity = opacity, Opacity = (float)opacity,
}; };
} }
@@ -105,7 +104,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var blurredCover = new GaussianBlurEffect using var blurredCover = new GaussianBlurEffect
{ {
BlurAmount = _albumArtBgBlurAmount, BlurAmount = _settingsService.AppSettings.LyricsBackgroundSettings.CoverOverlayBlurAmount,
Source = overlappedCovers, Source = overlappedCovers,
BorderMode = EffectBorderMode.Soft, BorderMode = EffectBorderMode.Soft,
Optimization = EffectOptimization.Speed, Optimization = EffectOptimization.Speed,
@@ -114,7 +113,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var combined = new CanvasCommandList(control); using var combined = new CanvasCommandList(control);
using var combinedDs = combined.CreateDrawingSession(); using var combinedDs = combined.CreateDrawingSession();
if (_coverAcrylicEffectAmount > 0 && _coverAcrylicNoiseCanvasBitmap != null) if (_settingsService.AppSettings.LyricsBackgroundSettings.CoverAcrylicEffectAmount > 0 && _coverAcrylicNoiseCanvasBitmap != null)
{ {
// 应用亚克力噪点效果 // 应用亚克力噪点效果
combinedDs.DrawImage(new BlendEffect combinedDs.DrawImage(new BlendEffect
@@ -124,7 +123,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
Foreground = new OpacityEffect Foreground = new OpacityEffect
{ {
Source = _coverAcrylicNoiseCanvasBitmap, Source = _coverAcrylicNoiseCanvasBitmap,
Opacity = _coverAcrylicEffectAmount / 100f, Opacity = _settingsService.AppSettings.LyricsBackgroundSettings.CoverAcrylicEffectAmount / 100f,
}, },
}); });
} }
@@ -137,7 +136,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
using var albumArtBgDs = _albumArtBgEffect.CreateDrawingSession(); using var albumArtBgDs = _albumArtBgEffect.CreateDrawingSession();
albumArtBgDs.DrawImage(new OpacityEffect albumArtBgDs.DrawImage(new OpacityEffect
{ {
Opacity = _albumArtBgOpacity / 100f, Opacity = _settingsService.AppSettings.LyricsBackgroundSettings.CoverOverlayOpacity / 100f,
Source = combined, Source = combined,
}); });
} }

View File

@@ -1,5 +1,7 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -15,7 +17,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public partial class LyricsRendererViewModel public partial class LyricsRendererViewModel
: IRecipient<PropertyChangedMessage<int>>, : IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<string>>, IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<float>>, IRecipient<PropertyChangedMessage<double>>,
IRecipient<PropertyChangedMessage<bool>>, IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<Color>>, IRecipient<PropertyChangedMessage<Color>>,
IRecipient<PropertyChangedMessage<LyricsDisplayType>>, IRecipient<PropertyChangedMessage<LyricsDisplayType>>,
@@ -24,78 +26,45 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
IRecipient<PropertyChangedMessage<LyricsFontWeight>>, IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
IRecipient<PropertyChangedMessage<LineRenderingType>>, IRecipient<PropertyChangedMessage<LineRenderingType>>,
IRecipient<PropertyChangedMessage<ElementTheme>>, IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<EasingType>>, IRecipient<PropertyChangedMessage<EasingType>>
IRecipient<PropertyChangedMessage<DockPlacement>>,
IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>,
IRecipient<PropertyChangedMessage<ObservableCollection<LocalMediaFolder>>>
{ {
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> message)
{
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LocalMediaFolders))
{
// Music lib changed, re-fetch lyrics
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async tokne =>
{
await RefreshLyricsAsync(tokne);
});
}
}
}
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message)
{
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.MediaSourceProvidersInfo))
{
_mediaSourceProvidersInfo = message.NewValue.ToList();
UpdateTimelineSyncThreshold();
UpdatePositionOffset();
UpdateIsLastFMTrackEnabled();
// Media source providers info changed (maybe include lyrics search providers info changed), re-fetch lyrics
_logger.LogInformation("Lyrics search providers info changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async token =>
{
await RefreshLyricsAsync(token);
});
}
}
}
public void Receive(PropertyChangedMessage<bool> message) public void Receive(PropertyChangedMessage<bool> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is SettingsPageViewModel)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsDynamicCoverOverlayEnabled)) if (message.PropertyName == nameof(SettingsPageViewModel.IsDebugOverlayEnabled))
{
_isDynamicCoverOverlayEnabled = message.NewValue;
}
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsDebugOverlayEnabled))
{ {
_isDebugOverlayEnabled = message.NewValue; _isDebugOverlayEnabled = message.NewValue;
_isDebugOverlayEnabledChanged = true; _isDebugOverlayEnabledChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLyricsGlowEffectEnabled)) }
else if (message.Sender is LyricsEffectSettings)
{
if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsGlowEffectEnabled))
{ {
_isLyricsGlowEffectEnabled = message.NewValue;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsFanLyricsEnabled)) else if (message.PropertyName == nameof(LyricsEffectSettings.IsFanLyricsEnabled))
{ {
_isFanLyricsEnabled = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLyricsFloatAnimationEnabled)) else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsFloatAnimationEnabled))
{ {
_isLyricsFloatAnimationEnabled = message.NewValue;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.IsLibreTranslateEnabled)) }
else if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.IsLibreTranslateEnabled))
{
UpdateTranslations();
}
else if (message.PropertyName == nameof(TranslationSettings.IsTranslationEnabled))
{
_logger.LogInformation("Translation enabled state changed: {IsEnabled}", _settingsService.AppSettings.TranslationSettings.IsTranslationEnabled);
UpdateTranslations();
}
else if (message.PropertyName == nameof(TranslationSettings.ShowTranslationOnly))
{ {
_isLibreTranslateEnabled = message.NewValue;
UpdateTranslations(); UpdateTranslations();
} }
} }
@@ -126,20 +95,25 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateImmersiveBackgroundOpacity(); UpdateImmersiveBackgroundOpacity();
} }
} }
else if (message.Sender is LyricsPageViewModel) else if (message.Sender is MediaSourceProviderInfo)
{ {
if (message.PropertyName == nameof(LyricsPageViewModel.IsTranslationEnabled)) if (message.PropertyName == nameof(MediaSourceProviderInfo.IsLastFMTrackEnabled))
{ {
_isTranslationEnabled = message.NewValue; UpdateIsLastFMTrackEnabled();
_logger.LogInformation("Translation enabled state changed: {IsEnabled}", _isTranslationEnabled);
UpdateTranslations();
}
else if (message.PropertyName == nameof(LyricsPageViewModel.ShowTranslationOnly))
{
_showTranslationOnly = message.NewValue;
UpdateTranslations();
} }
} }
if (message.Sender is LyricsSearchProviderInfo)
{
if (message.PropertyName == nameof(LyricsSearchProviderInfo.IsEnabled))
{
_logger.LogInformation("LyricsSearchProviderInfo.IsEnabled changed, refreshing lyrics.");
_ = _refreshLyricsRunner.RunAsync(async token =>
{
await RefreshLyricsAsync(token);
});
}
}
} }
public void Receive(PropertyChangedMessage<Color> message) public void Receive(PropertyChangedMessage<Color> message)
@@ -153,33 +127,29 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateColorConfig(); UpdateColorConfig();
} }
} }
else if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) else if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomBgFontColor)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomBgFontColor))
{ {
_customBgFontColor = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomFgFontColor)) else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomFgFontColor))
{ {
_customFgFontColor = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsCustomStrokeFontColor)) else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomStrokeFontColor))
{ {
_customStrokeFontColor = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
} }
} }
public void Receive(PropertyChangedMessage<float> message) public void Receive(PropertyChangedMessage<double> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsLineSpacingFactor)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsLineSpacingFactor))
{ {
_lyricsLineSpacingFactor = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
} }
@@ -187,102 +157,120 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<int> message) public void Receive(PropertyChangedMessage<int> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is AlbumArtLayoutSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverImageRadius)) if (message.PropertyName == nameof(AlbumArtLayoutSettings.CoverImageRadius))
{ {
_albumArtCornerRadius = message.NewValue;
_isAlbumArtCornerRadiusChanged = true; _isAlbumArtCornerRadiusChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverOverlayOpacity)) }
else if (message.Sender is LyricsBackgroundSettings)
{
if (message.PropertyName == nameof(LyricsBackgroundSettings.CoverOverlayOpacity))
{ {
_albumArtBgOpacity = message.NewValue; _isAlbumArtBgOpacityChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverOverlayBlurAmount)) else if (message.PropertyName == nameof(LyricsBackgroundSettings.CoverOverlayBlurAmount))
{ {
_albumArtBgBlurAmount = message.NewValue; _isAlbumArtBgBlurAmountChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.CoverAcrylicEffectAmount)) else if (message.PropertyName == nameof(LyricsBackgroundSettings.CoverAcrylicEffectAmount))
{ {
_coverAcrylicEffectAmount = message.NewValue;
_isCoverAcrylicEffectAmountChanged = true; _isCoverAcrylicEffectAmountChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsVerticalEdgeOpacity)) else if (message.PropertyName == nameof(LyricsBackgroundSettings.CoverOverlaySpeed))
{
}
}
else if (message.Sender is LyricsEffectSettings)
{
if (message.PropertyName == nameof(LyricsEffectSettings.LyricsVerticalEdgeOpacity))
{ {
_lyricsVerticalEdgeOpacity = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBlurAmount)) else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsBlurAmount))
{ {
_lyricsBlurAmount = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStandardFontSize)) else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollDuration))
{ {
_lyricsStandardFontSize = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDockFontSize)) else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollTopDuration))
{ {
_lyricsDockFontSize = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsDesktopFontSize)) else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollBottomDuration))
{ {
_lyricsDesktopFontSize = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.SelectedTargetLanguageIndex)) }
else if (message.Sender is LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontSize))
{ {
_targetLanguageIndex = message.NewValue; _isLayoutChanged = true;
_logger.LogInformation("Target language index changed: {Index}", _targetLanguageIndex); }
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontStrokeWidth))
{
_isLayoutChanged = true;
}
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsBgFontOpacity))
{
_isLayoutChanged = true;
}
}
else if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageIndex))
{
_logger.LogInformation("Target language index changed: {Index}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex);
UpdateTranslations(); UpdateTranslations();
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontStrokeWidth)) }
else if (message.Sender is MediaSourceProviderInfo)
{
if (message.PropertyName == nameof(MediaSourceProviderInfo.TimelineSyncThreshold))
{ {
_lyricsFontStrokeWidth = message.NewValue; UpdateTimelineSyncThreshold();
_isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsScrollDuration)) else if (message.PropertyName == nameof(MediaSourceProviderInfo.PositionOffset))
{ {
_canvasYScrollTransition.SetDuration(message.NewValue / 1000f); UpdatePositionOffset();
}
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBgFontOpacity))
{
_defaultOpacity = message.NewValue / 100f;
_isLayoutChanged = true;
} }
} }
} }
public void Receive(PropertyChangedMessage<LineRenderingType> message) public void Receive(PropertyChangedMessage<LineRenderingType> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsEffectSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsGlowEffectScope)) if (message.PropertyName == nameof(LyricsEffectSettings.LyricsGlowEffectScope))
{ {
_lyricsGlowEffectScope = message.NewValue; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsHighlightScope)) else if (message.PropertyName == nameof(LyricsEffectSettings.LyricsHighlightScope))
{ {
_lyricsHighlightScope = message.NewValue; _isLayoutChanged = true;
} }
} }
} }
public void Receive(PropertyChangedMessage<TextAlignmentType> message) public void Receive(PropertyChangedMessage<TextAlignmentType> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsAlignmentType)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsAlignmentType))
{ {
_lyricsAlignmentType = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.SongInfoAlignmentType)) }
else if (message.Sender is AlbumArtLayoutSettings)
{
if (message.PropertyName == nameof(AlbumArtLayoutSettings.SongInfoAlignmentType))
{ {
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment =
message.NewValue.ToCanvasHorizontalAlignment(); _settingsService.AppSettings.AlbumArtLayoutSettings.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
} }
} }
} }
@@ -294,21 +282,18 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<LyricsFontColorType> message) public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBgFontColorType)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsBgFontColorType))
{ {
_lyricsBgFontColorType = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFgFontColorType)) else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFgFontColorType))
{ {
_lyricsFgFontColorType = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsStrokeFontColorType)) else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsStrokeFontColorType))
{ {
_lyricsStrokeFontColorType = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
} }
@@ -316,11 +301,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<LyricsFontWeight> message) public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontWeight)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontWeight))
{ {
_lyricsTextFormat.FontWeight = message.NewValue.ToFontWeight();
_isLayoutChanged = true; _isLayoutChanged = true;
} }
} }
@@ -328,11 +312,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<ElementTheme> message) public void Receive(PropertyChangedMessage<ElementTheme> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsBackgroundSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsBackgroundTheme)) if (message.PropertyName == nameof(LyricsBackgroundSettings.LyricsBackgroundTheme))
{ {
_lyricsBgTheme = message.NewValue;
UpdateColorConfig(); UpdateColorConfig();
} }
} }
@@ -340,9 +323,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<EasingType> message) public void Receive(PropertyChangedMessage<EasingType> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsEffectSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsScrollEasingType)) if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollEasingType))
{ {
_canvasYScrollTransition.SetEasingType(message.NewValue); _canvasYScrollTransition.SetEasingType(message.NewValue);
} }
@@ -351,30 +334,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
public void Receive(PropertyChangedMessage<string> message) public void Receive(PropertyChangedMessage<string> message)
{ {
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel) if (message.Sender is LyricsStyleSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsFontFamily)) if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontFamily))
{ {
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = message.NewValue;
_isLayoutChanged = true; _isLayoutChanged = true;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LyricsTranslationSeparator)) else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsTranslationSeparator))
{ {
_lyricsTranslationSeparator = message.NewValue;
UpdateTranslations(); UpdateTranslations();
} }
} }
} }
public void Receive(PropertyChangedMessage<DockPlacement> message)
{
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.DockPlacement))
{
_dockPlacement = message.NewValue;
}
}
}
} }
} }

View File

@@ -12,7 +12,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
public partial class LyricsRendererViewModel public partial class LyricsRendererViewModel
{ {
private readonly ValueTransition<float> _canvasYScrollTransition = new( private readonly ValueTransition<double> _canvasYScrollTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutSine easingType: EasingType.EaseInOutSine
@@ -30,68 +30,68 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to) interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
); );
private readonly ValueTransition<float> _immersiveBgOpacityTransition = new( private readonly ValueTransition<double> _immersiveBgOpacityTransition = new(
initialValue: 1f, initialValue: 1f,
durationSeconds: 0.3f durationSeconds: 0.3f
); );
private readonly ValueTransition<float> _titleXTransition = new( private readonly ValueTransition<double> _titleXTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _titleYTransition = new( private readonly ValueTransition<double> _titleYTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _lyricsXTransition = new( private readonly ValueTransition<double> _lyricsXTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _lyricsYTransition = new( private readonly ValueTransition<double> _lyricsYTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _lyricsOpacityTransition = new( private readonly ValueTransition<double> _lyricsOpacityTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f durationSeconds: 0.3f
); );
private readonly ValueTransition<float> _albumArtBgTransition = new( private readonly ValueTransition<double> _albumArtBgTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 1f durationSeconds: 1f
); );
private readonly ValueTransition<float> _albumArtOpacityTransition = new( private readonly ValueTransition<double> _albumArtOpacityTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 1f durationSeconds: 1f
); );
private readonly ValueTransition<float> _albumArtXTransition = new( private readonly ValueTransition<double> _albumArtXTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _albumArtYTransition = new( private readonly ValueTransition<double> _albumArtYTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 0.3f, durationSeconds: 0.3f,
easingType: EasingType.EaseInOutBack easingType: EasingType.EaseInOutBack
); );
private readonly ValueTransition<float> _songInfoOpacityTransition = new( private readonly ValueTransition<double> _songInfoOpacityTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 1f durationSeconds: 1f
); );
private readonly ValueTransition<float> _lyricsBgBrightnessTransition = new( private readonly ValueTransition<double> _lyricsBgBrightnessTransition = new(
initialValue: 0f, initialValue: 0f,
durationSeconds: 1f durationSeconds: 1f
); );

View File

@@ -37,6 +37,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isAlbumArtCornerRadiusChanged = true; private bool _isAlbumArtCornerRadiusChanged = true;
private bool _isAlbumArtBgOpacityChanged = false;
private bool _isAlbumArtBgBlurAmountChanged = false;
public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args) public void Update(ICanvasAnimatedControl control, CanvasAnimatedUpdateEventArgs args)
{ {
_elapsedTime = args.Timing.ElapsedTime; _elapsedTime = args.Timing.ElapsedTime;
@@ -59,8 +62,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isDisplayTypeChanged = _displayType != _displayTypeReceived; _isDisplayTypeChanged = _displayType != _displayTypeReceived;
_isPlayingLineChanged = _playingLineIndex != playingLineIndex; _isPlayingLineChanged = _playingLineIndex != playingLineIndex;
_canvasWidth = (float)control.Size.Width; _canvasWidth = control.Size.Width;
_canvasHeight = (float)control.Size.Height; _canvasHeight = control.Size.Height;
_displayType = _displayTypeReceived; _displayType = _displayTypeReceived;
_playingLineIndex = playingLineIndex; _playingLineIndex = playingLineIndex;
@@ -78,11 +81,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isDebugOverlayEnabledChanged = false; _isDebugOverlayEnabledChanged = false;
} }
if (_isDynamicCoverOverlayEnabled)
{ _rotateAngle += _coverRotateBaseSpeed * _settingsService.AppSettings.LyricsBackgroundSettings.CoverOverlaySpeed / 100.0;
_rotateAngle += _coverRotateSpeed; _rotateAngle %= Math.PI * 2;
_rotateAngle %= MathF.PI * 2;
}
if (_isCanvasWidthChanged) if (_isCanvasWidthChanged)
{ {
@@ -107,17 +108,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
switch (_lyricsLayoutOrientation) switch (_lyricsLayoutOrientation)
{ {
case LyricsLayoutOrientation.Horizontal: case LyricsLayoutOrientation.Horizontal:
_albumArtSize = MathF.Min((_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16, (_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2); _albumArtSize = Math.Min((_canvasHeight - _topMargin - _bottomMargin) * 8.5 / 16.0, (_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2.0);
_albumArtSize = MathF.Max(0, _albumArtSize); _albumArtSize = Math.Max(0, _albumArtSize);
_albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize * 1.05f - _titleTextFormat.FontSize - _artistTextFormat.FontSize) / 2, jumpTo); _albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize * 1.05 - _titleTextFormat.FontSize - _artistTextFormat.FontSize) / 2.0, jumpTo);
_titleYTransition.StartTransition(_albumArtYTransition.TargetValue + _albumArtSize * 1.05f, jumpTo); _titleYTransition.StartTransition(_albumArtYTransition.TargetValue + _albumArtSize * 1.05, jumpTo);
_lyricsYTransition.StartTransition(0, jumpTo); _lyricsYTransition.StartTransition(0, jumpTo);
switch (_displayType) switch (_displayType)
{ {
case LyricsDisplayType.AlbumArtOnly: case LyricsDisplayType.AlbumArtOnly:
_lyricsOpacityTransition.StartTransition(0f, jumpTo); _lyricsOpacityTransition.StartTransition(0f, jumpTo);
_albumArtOpacityTransition.StartTransition(1f, jumpTo); _albumArtOpacityTransition.StartTransition(1f, jumpTo);
_albumArtXTransition.StartTransition(_canvasWidth / 2 - _albumArtSize / 2, jumpTo); _albumArtXTransition.StartTransition(_canvasWidth / 2.0 - _albumArtSize / 2.0, jumpTo);
_titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo); _titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo);
break; break;
case LyricsDisplayType.LyricsOnly: case LyricsDisplayType.LyricsOnly:
@@ -128,8 +129,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
case LyricsDisplayType.SplitView: case LyricsDisplayType.SplitView:
_lyricsOpacityTransition.StartTransition(1f, jumpTo); _lyricsOpacityTransition.StartTransition(1f, jumpTo);
_albumArtOpacityTransition.StartTransition(1f, jumpTo); _albumArtOpacityTransition.StartTransition(1f, jumpTo);
_lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo); _lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2.0 + _leftMargin + _middleMargin, jumpTo);
_albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo); _albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2.0 - _albumArtSize) / 2.0, jumpTo);
_titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo); _titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo);
break; break;
default: default:
@@ -140,13 +141,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_albumArtSize = 64; _albumArtSize = 64;
_lyricsXTransition.StartTransition(_leftMargin, jumpTo); _lyricsXTransition.StartTransition(_leftMargin, jumpTo);
_albumArtXTransition.StartTransition(_leftMargin, jumpTo); _albumArtXTransition.StartTransition(_leftMargin, jumpTo);
_titleXTransition.StartTransition(_leftMargin + _albumArtSize * 1.2f, jumpTo); _titleXTransition.StartTransition(_leftMargin + _albumArtSize * 1.2, jumpTo);
switch (_displayType) switch (_displayType)
{ {
case LyricsDisplayType.AlbumArtOnly: case LyricsDisplayType.AlbumArtOnly:
_lyricsOpacityTransition.StartTransition(0f, jumpTo); _lyricsOpacityTransition.StartTransition(0f, jumpTo);
_albumArtOpacityTransition.StartTransition(1f, jumpTo); _albumArtOpacityTransition.StartTransition(1f, jumpTo);
_albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize) / 2, jumpTo); _albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize) / 2.0, jumpTo);
_titleYTransition.StartTransition(_albumArtYTransition.TargetValue, jumpTo); _titleYTransition.StartTransition(_albumArtYTransition.TargetValue, jumpTo);
break; break;
case LyricsDisplayType.LyricsOnly: case LyricsDisplayType.LyricsOnly:
@@ -223,13 +224,24 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
UpdateCoverAcrylicOverlay(control); UpdateCoverAcrylicOverlay(control);
UpdateAlbumArtBgEffect(control); UpdateAlbumArtBgEffect(control);
_isCoverAcrylicEffectAmountChanged = false; _isCoverAcrylicEffectAmountChanged = false;
} }
if (_isAlbumArtBgOpacityChanged)
{
UpdateAlbumArtBgEffect(control);
_isAlbumArtBgOpacityChanged = false;
}
if (_isAlbumArtBgBlurAmountChanged)
{
UpdateAlbumArtBgEffect(control);
_isAlbumArtBgBlurAmountChanged = false;
}
_albumArtChanged = false; _albumArtChanged = false;
if (_isCanvasWidthChanged || _lyricsXTransition.IsTransitioning) if (_isCanvasHeightChanged || _isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
{ {
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin; _maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
_maxLyricsWidth = Math.Max(_maxLyricsWidth, 0); _maxLyricsWidth = Math.Max(_maxLyricsWidth, 0);
@@ -239,13 +251,16 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (_isLayoutChanged) if (_isLayoutChanged)
{ {
ReLayout(control); ReLayout(control);
UpdateCanvasYScrollOffset(control, true, false);
} }
else
if (_isLayoutChanged || _isPlayingLineChanged)
{ {
UpdateCanvasYScrollOffset(control, false, true); UpdateCanvasTargetYScrollOffset();
_canvasYScrollTransition.StartTransition(_canvasTargetYScrollOffset, _isLayoutChanged);
} }
UpdateVisibleLinesBoundary();
UpdateLinesProps(control); UpdateLinesProps(control);
_isLayoutChanged = false; _isLayoutChanged = false;
@@ -264,6 +279,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_albumArtBgTransition.Update(_elapsedTime); _albumArtBgTransition.Update(_elapsedTime);
_lyricsBgBrightnessTransition.Update(_elapsedTime); _lyricsBgBrightnessTransition.Update(_elapsedTime);
_songInfoOpacityTransition.Update(_elapsedTime); _songInfoOpacityTransition.Update(_elapsedTime);
_canvasYScrollTransition.Update(_elapsedTime);
} }
private void ReLayout(ICanvasAnimatedControl control) private void ReLayout(ICanvasAnimatedControl control)
@@ -273,18 +289,28 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (_isDockMode) if (_isDockMode)
{ {
_lyricsTextFormat.FontSize = _lyricsDockFontSize; _lyricsStyleSettings = _settingsService.AppSettings.DockLyricsStyleSettings;
_lyricsEffectSettings = _settingsService.AppSettings.DockLyricsEffectSettings;
} }
else if (_isDesktopMode) else if (_isDesktopMode)
{ {
_lyricsTextFormat.FontSize = _lyricsDesktopFontSize; _lyricsStyleSettings = _settingsService.AppSettings.DesktopLyricsStyleSettings;
_lyricsEffectSettings = _settingsService.AppSettings.DesktopLyricsEffectSettings;
} }
else else
{ {
_lyricsTextFormat.FontSize = _lyricsStandardFontSize; _lyricsStyleSettings = _settingsService.AppSettings.StandardLyricsStyleSettings;
_lyricsEffectSettings = _settingsService.AppSettings.StandardLyricsEffectSettings;
} }
float y = 0; _lyricsTextFormat.FontSize = _lyricsStyleSettings.LyricsFontSize;
_lyricsTextFormat.FontWeight = _lyricsStyleSettings.LyricsFontWeight.ToFontWeight();
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = _lyricsStyleSettings.LyricsFontFamily;
_canvasYScrollTransition.SetDuration(_lyricsEffectSettings.LyricsScrollDuration / 1000.0);
_canvasYScrollTransition.SetEasingType(_lyricsEffectSettings.LyricsScrollEasingType);
double y = 0;
// Init Positions // Init Positions
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++) for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
@@ -296,26 +322,27 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
continue; continue;
} }
line.Position = new Vector2(0, y); line.RecreatePlaceholder(control);
line.UpdateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType);
line.UpdateCenterPosition(_maxLyricsWidth, _isDockMode ? TextAlignmentType.Center : _lyricsAlignmentType);
//line.UpdateTextGeometry(); line.Position = new Vector2(0, (float)y);
//line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor); line.RecreateTextLayout(control, _lyricsTextFormat, _maxLyricsWidth, _canvasHeight, _lyricsStyleSettings.LyricsAlignmentType);
line.UpdateCenterPosition(_maxLyricsWidth, _lyricsStyleSettings.LyricsAlignmentType);
if (line.CanvasTextLayout == null) line.RecreateTextGeometry();
{ line.RecreateFontEffect(control, _strokeFontColor, _lyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor, _fgFontColor);
continue; line.RecreateBackgroundEffect(_lyricsOpacityTransition.Value);
} line.RecreateCurrentLineMask(control);
line.RecreateForegroundBlurEffect(control, _lyricsEffectSettings.LyricsGlowEffectScope, _lyricsGlowEffectAmount);
line.RecreateForegroundHighlightEffect(control, _lyricsEffectSettings.LyricsHighlightScope);
y += y +=
(float)line.CanvasTextLayout.LayoutBounds.Height (double)line.CanvasTextLayout!.LayoutBounds.Height
/ line.CanvasTextLayout.LineCount / line.CanvasTextLayout.LineCount
* (line.CanvasTextLayout.LineCount + _lyricsLineSpacingFactor); * (line.CanvasTextLayout.LineCount + _lyricsStyleSettings.LyricsLineSpacingFactor);
} }
} }
private void UpdateCanvasYScrollOffset(ICanvasAnimatedControl control, bool forceScroll, bool withAnimation) private void UpdateCanvasTargetYScrollOffset()
{ {
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries(); var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
@@ -323,30 +350,29 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
// Set _scrollOffsetY // Set _scrollOffsetY
if ((!_isPlayingLineChanged && forceScroll) || _isPlayingLineChanged) LyricsLine? currentPlayingLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
{
LyricsLine? currentPlayingLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
if (currentPlayingLine == null) return; if (currentPlayingLine == null) return;
var playingTextLayout = currentPlayingLine?.CanvasTextLayout; var playingTextLayout = currentPlayingLine?.CanvasTextLayout;
if (playingTextLayout == null) return; if (playingTextLayout == null) return;
float? targetYScrollOffset = (float?)(-currentPlayingLine!.Position.Y + _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2); double? targetYScrollOffset = -currentPlayingLine!.Position.Y + _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[0].Position.Y - playingTextLayout.LayoutBounds.Height / 2.0;
if (!targetYScrollOffset.HasValue) return; if (!targetYScrollOffset.HasValue) return;
_canvasYScrollTransition.StartTransition(targetYScrollOffset.Value, !withAnimation); _canvasTargetYScrollOffset = targetYScrollOffset.Value;
} }
_canvasYScrollTransition.Update(_elapsedTime); private void UpdateVisibleLinesBoundary()
{
var (startLineIndex, endLineIndex) = GetMaxLyricsLineIndexBoundaries();
// Update visible line indices
var lines = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines; var lines = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines;
if (lines == null || lines.Count == 0) return; if (lines == null || lines.Count == 0) return;
float offset = _canvasYScrollTransition.Value + _canvasHeight / 2; double offset = _canvasYScrollTransition.Value + _canvasHeight / 2;
int startVisibleLineIndex = FindFirstVisibleLine(lines, offset); int startVisibleLineIndex = FindFirstVisibleLine(lines, offset);
int endVisibleLineIndex = FindLastVisibleLine(lines, offset, _canvasHeight); int endVisibleLineIndex = FindLastVisibleLine(lines, offset, _canvasHeight);
@@ -361,7 +387,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_endVisibleLineIndex = endVisibleLineIndex; _endVisibleLineIndex = endVisibleLineIndex;
} }
private int FindFirstVisibleLine(IList<LyricsLine> lines, float offset) private int FindFirstVisibleLine(IList<LyricsLine> lines, double offset)
{ {
int left = 0, right = lines.Count - 1, result = -1; int left = 0, right = lines.Count - 1, result = -1;
while (left <= right) while (left <= right)
@@ -370,7 +396,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
var line = lines[mid]; var line = lines[mid];
var layout = line.CanvasTextLayout; var layout = line.CanvasTextLayout;
if (layout == null) break; if (layout == null) break;
float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height; double value = offset + line.Position.Y + (double)layout.LayoutBounds.Height;
if (value >= 0) if (value >= 0)
{ {
result = mid; result = mid;
@@ -384,7 +410,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
return result; return result;
} }
private int FindLastVisibleLine(IList<LyricsLine> lines, float offset, float canvasHeight) private int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double canvasHeight)
{ {
int left = 0, right = lines.Count - 1, result = -1; int left = 0, right = lines.Count - 1, result = -1;
while (left <= right) while (left <= right)
@@ -393,7 +419,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
var line = lines[mid]; var line = lines[mid];
var layout = line.CanvasTextLayout; var layout = line.CanvasTextLayout;
if (layout == null) break; if (layout == null) break;
float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height; double value = offset + line.Position.Y + (double)layout.LayoutBounds.Height;
if (value >= canvasHeight) if (value >= canvasHeight)
{ {
result = mid; result = mid;
@@ -415,10 +441,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
} }
else else
{ {
ThemeTypeSent = _lyricsBgTheme; ThemeTypeSent = _settingsService.AppSettings.LyricsBackgroundSettings.LyricsBackgroundTheme;
} }
float brightness; double brightness;
Color grayedEnvironmentalColor = Colors.Transparent; Color grayedEnvironmentalColor = Colors.Transparent;
bool isLight = ThemeTypeSent switch bool isLight = ThemeTypeSent switch
@@ -462,7 +488,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
} }
} }
switch (_lyricsBgFontColorType) switch (_lyricsStyleSettings.LyricsBgFontColorType)
{ {
case LyricsFontColorType.AdaptiveGrayed: case LyricsFontColorType.AdaptiveGrayed:
_bgFontColor = _adaptiveGrayedFontColor; _bgFontColor = _adaptiveGrayedFontColor;
@@ -471,13 +497,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_bgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor; _bgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
break; break;
case LyricsFontColorType.Custom: case LyricsFontColorType.Custom:
_bgFontColor = _customBgFontColor ?? _adaptiveGrayedFontColor; _bgFontColor = _lyricsStyleSettings.LyricsCustomBgFontColor;
break; break;
default: default:
break; break;
} }
switch (_lyricsFgFontColorType) switch (_lyricsStyleSettings.LyricsFgFontColorType)
{ {
case LyricsFontColorType.AdaptiveGrayed: case LyricsFontColorType.AdaptiveGrayed:
_fgFontColor = _adaptiveGrayedFontColor; _fgFontColor = _adaptiveGrayedFontColor;
@@ -486,13 +512,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_fgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor; _fgFontColor = _adaptiveColoredFontColor ?? _adaptiveGrayedFontColor;
break; break;
case LyricsFontColorType.Custom: case LyricsFontColorType.Custom:
_fgFontColor = _customFgFontColor ?? _adaptiveGrayedFontColor; _fgFontColor = _lyricsStyleSettings.LyricsCustomFgFontColor;
break; break;
default: default:
break; break;
} }
switch (_lyricsStrokeFontColorType) switch (_lyricsStyleSettings.LyricsStrokeFontColorType)
{ {
case LyricsFontColorType.AdaptiveGrayed: case LyricsFontColorType.AdaptiveGrayed:
_strokeFontColor = grayedEnvironmentalColor.WithBrightness(0.7); _strokeFontColor = grayedEnvironmentalColor.WithBrightness(0.7);
@@ -501,7 +527,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_strokeFontColor = _environmentalColor.WithBrightness(0.7); _strokeFontColor = _environmentalColor.WithBrightness(0.7);
break; break;
case LyricsFontColorType.Custom: case LyricsFontColorType.Custom:
_strokeFontColor = _customStrokeFontColor ?? _environmentalColor; _strokeFontColor = _lyricsStyleSettings.LyricsCustomStrokeFontColor;
break; break;
default: default:
break; break;
@@ -518,32 +544,67 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
if (currentPlayingLine == null) return; if (currentPlayingLine == null) return;
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++) for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex + 1; i++)
{ {
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i); var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
if (line == null) continue; if (line == null) continue;
line.UpdateTextGeometry(); line.UpdateBackgroundEffect(_lyricsOpacityTransition.Value);
line.UpdateFontEffect(control, _isDesktopMode, _strokeFontColor, _lyricsFontStrokeWidth, _bgFontColor);
if (_isLayoutChanged || _isVisibleLinesBoundaryChanged || _isPlayingLineChanged) if (i == _playingLineIndex)
{ {
float distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y); GetLinePlayingProgress(
float distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1); _playingLineIndex,
out int charStartIndex,
out int charLength,
out double charProgress
);
line.AngleTransition.StartTransition(_isFanLyricsEnabled line.RecreateCurrentCharMask(control, charStartIndex, charLength, charProgress);
? (float)Math.PI line.RecreateLineStartToCurrentCharMask(control, charStartIndex, charLength, charProgress);
* (30f / 180f)
line.UpdateForegroundBlurEffect(control, _lyricsEffectSettings.LyricsGlowEffectScope, _lyricsGlowEffectAmount);
line.UpdateForegroundHighlightEffect(control, _lyricsEffectSettings.LyricsHighlightScope);
}
if (_isLayoutChanged || _isPlayingLineChanged)
{
int lineCountDelta = i - _playingLineIndex;
int absoluteLineCountDelta = Math.Abs(lineCountDelta);
double distanceFromPlayingLine = Math.Abs(line.Position.Y - currentPlayingLine.Position.Y);
double distanceFactor = Math.Clamp(distanceFromPlayingLine / (_canvasHeight / 2), 0, 1);
line.AngleTransition.StartTransition(_lyricsEffectSettings.IsFanLyricsEnabled
? Math.PI
* (30.0 / 180.0)
* distanceFactor * distanceFactor
* (i > _playingLineIndex ? 1 : -1) * (i > _playingLineIndex ? 1 : -1)
: 0 : 0
); );
line.BlurAmountTransition.StartTransition(_lyricsBlurAmount * distanceFactor); line.BlurAmountTransition.StartTransition(_lyricsEffectSettings.LyricsBlurAmount * distanceFactor);
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale)); line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
line.OpacityTransition.StartTransition(_defaultOpacity - distanceFactor * _defaultOpacity * (1 - _lyricsVerticalEdgeOpacity / 100f)); line.OpacityTransition.StartTransition(_lyricsStyleSettings.LyricsBgFontOpacity / 100.0 - distanceFactor * _lyricsStyleSettings.LyricsBgFontOpacity / 100.0 * (1 - _lyricsEffectSettings.LyricsVerticalEdgeOpacity / 100.0));
line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f); line.HighlightOpacityTransition.StartTransition(i == _playingLineIndex ? 1f : 0f);
double yScrollDuration;
if (lineCountDelta < 0)
{
yScrollDuration = _canvasYScrollTransition.DurationSeconds + distanceFactor * (_lyricsEffectSettings.LyricsScrollTopDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
}
else if (lineCountDelta == 0)
{
yScrollDuration = _canvasYScrollTransition.DurationSeconds;
}
else
{
yScrollDuration = _canvasYScrollTransition.DurationSeconds + distanceFactor * (_lyricsEffectSettings.LyricsScrollBottomDuration / 1000.0 - _canvasYScrollTransition.DurationSeconds);
}
line.YOffsetTransition.SetEasingType(_canvasYScrollTransition.EasingType ?? EasingType.Linear);
line.YOffsetTransition.SetDuration(yScrollDuration);
line.YOffsetTransition.StartTransition(_canvasTargetYScrollOffset, _isLayoutChanged);
} }
line.AngleTransition.Update(_elapsedTime); line.AngleTransition.Update(_elapsedTime);
@@ -551,12 +612,13 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
line.BlurAmountTransition.Update(_elapsedTime); line.BlurAmountTransition.Update(_elapsedTime);
line.OpacityTransition.Update(_elapsedTime); line.OpacityTransition.Update(_elapsedTime);
line.HighlightOpacityTransition.Update(_elapsedTime); line.HighlightOpacityTransition.Update(_elapsedTime);
line.YOffsetTransition.Update(_elapsedTime);
} }
} }
private void UpdateImmersiveBackgroundOpacity() private void UpdateImmersiveBackgroundOpacity()
{ {
float targetOpacity; double targetOpacity;
if (_isDesktopMode) if (_isDesktopMode)
{ {
if (_isLyricsWindowLocked) if (_isLyricsWindowLocked)
@@ -584,7 +646,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void UpdateCoverAcrylicOverlay(ICanvasAnimatedControl control) private void UpdateCoverAcrylicOverlay(ICanvasAnimatedControl control)
{ {
if (_coverAcrylicEffectAmount > 0) if (_settingsService.AppSettings.LyricsBackgroundSettings.CoverAcrylicEffectAmount > 0)
{ {
var ret = ImageHelper.GenerateNoiseBGRA((int)_canvasWidth, (int)_canvasHeight); var ret = ImageHelper.GenerateNoiseBGRA((int)_canvasWidth, (int)_canvasHeight);
_coverAcrylicNoiseCanvasBitmap?.Dispose(); _coverAcrylicNoiseCanvasBitmap?.Dispose();
@@ -599,25 +661,20 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
} }
} }
private MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo()
{
return _mediaSourceProvidersInfo.Where(x => x.Provider == SongInfo?.SourceAppUserModelId)?.FirstOrDefault();
}
private void UpdateTimelineSyncThreshold() private void UpdateTimelineSyncThreshold()
{ {
_timelineSyncThreshold = GetCurrentMediaSourceProviderInfo()?.TimelineSyncThreshold ?? 0; _timelineSyncThreshold = _mediaSessionsService.GetCurrentMediaSourceProviderInfo()?.TimelineSyncThreshold ?? 0;
} }
private void UpdatePositionOffset() private void UpdatePositionOffset()
{ {
var current = GetCurrentMediaSourceProviderInfo(); var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_positionOffset = TimeSpan.FromMilliseconds(current?.PositionOffset ?? 0); _positionOffset = TimeSpan.FromMilliseconds(current?.PositionOffset ?? 0);
} }
private void UpdateIsLastFMTrackEnabled() private void UpdateIsLastFMTrackEnabled()
{ {
var current = GetCurrentMediaSourceProviderInfo(); var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_isLastFMTrackEnabled = current?.IsLastFMTrackEnabled ?? false; _isLastFMTrackEnabled = current?.IsLastFMTrackEnabled ?? false;
} }
} }

View File

@@ -4,11 +4,13 @@ using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events; using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services; using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService; using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService; using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LyricsSearchService; using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.MediaSessionsService; using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService; using BetterLyrics.WinUI3.Services.TranslateService;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.WinUI; using CommunityToolkit.WinUI;
@@ -31,6 +33,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
public partial class LyricsRendererViewModel : BaseViewModel public partial class LyricsRendererViewModel : BaseViewModel
{ {
private LyricsStyleSettings _lyricsStyleSettings;
private LyricsEffectSettings _lyricsEffectSettings;
private bool _isLastFMTrackEnabled = false; private bool _isLastFMTrackEnabled = false;
private bool _isLastFMTracked = false; private bool _isLastFMTracked = false;
private TimeSpan _totalPlayingTime = TimeSpan.Zero; private TimeSpan _totalPlayingTime = TimeSpan.Zero;
@@ -49,6 +54,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private int _drawFrameCount = 0; private int _drawFrameCount = 0;
private int _displayedDrawFrameCount = 0; private int _displayedDrawFrameCount = 0;
private Queue<SoftwareBitmap?> _cachedAlbumArtSwBitmaps = [];
private SoftwareBitmap? _lastAlbumArtSwBitmap = null; private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
private SoftwareBitmap? _albumArtSwBitmap = null; private SoftwareBitmap? _albumArtSwBitmap = null;
@@ -57,8 +64,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private CanvasBitmap? _coverAcrylicNoiseCanvasBitmap = null; private CanvasBitmap? _coverAcrylicNoiseCanvasBitmap = null;
private float _albumArtSize = 0f; private double _albumArtSize = 0f;
private int _albumArtCornerRadius = 0;
private string? _lastSongTitle; private string? _lastSongTitle;
private string? _songTitle; private string? _songTitle;
@@ -66,17 +72,16 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private string? _lastSongArtist; private string? _lastSongArtist;
private string? _songArtist; private string? _songArtist;
private float _canvasWidth = 0f; private double _canvasWidth = 0f;
private float _canvasHeight = 0f; private double _canvasHeight = 0f;
private float _defaultOpacity; private readonly double _defaultScale = 0.75f;
private readonly float _highlightedOpacity = 1.0f; private readonly double _highlightedScale = 1.0f;
private readonly float _defaultScale = 0.75f; private readonly double _coverRotateBaseSpeed = 0.003f;
private readonly float _highlightedScale = 1.0f; private double _rotateAngle = 0f;
private readonly float _coverRotateSpeed = 0.003f; private double _canvasTargetYScrollOffset = 0;
private float _rotateAngle = 0f;
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedRecipients] [NotifyPropertyChangedRecipients]
@@ -86,28 +91,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
[NotifyPropertyChangedRecipients] [NotifyPropertyChangedRecipients]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null; public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
private TextAlignmentType _lyricsAlignmentType; private readonly double _lyricsGlowEffectAmount = 8f;
private readonly float _lyricsGlowEffectAmount = 8f; private double _maxLyricsWidth = 0f;
private int _lyricsBlurAmount;
private int _lyricsVerticalEdgeOpacity;
private ElementTheme _lyricsBgTheme;
private LineRenderingType _lyricsGlowEffectScope;
private LineRenderingType _lyricsHighlightScope;
private int _lyricsFontStrokeWidth;
private int _lyricsStandardFontSize;
private int _lyricsDockFontSize;
private int _lyricsDesktopFontSize;
private float _lyricsLineSpacingFactor;
private LyricsFontColorType _lyricsBgFontColorType;
private LyricsFontColorType _lyricsFgFontColorType;
private LyricsFontColorType _lyricsStrokeFontColorType;
private float _maxLyricsWidth = 0f;
private readonly ISettingsService _settingsService;
private readonly ILyricsSearchService _lyrcsSearchService; private readonly ILyricsSearchService _lyrcsSearchService;
private readonly ILibWatcherService _libWatcherService; private readonly ILibWatcherService _libWatcherService;
private readonly IMediaSessionsService _mediaSessionsService; private readonly IMediaSessionsService _mediaSessionsService;
@@ -115,13 +103,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private readonly ILastFMService _lastFMService; private readonly ILastFMService _lastFMService;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly float _leftMargin = 36f; private readonly double _leftMargin = 36f;
private readonly float _middleMargin = 36f; private readonly double _middleMargin = 36f;
private readonly float _rightMargin = 36f; private readonly double _rightMargin = 36f;
private readonly float _topMargin = 36f; private readonly double _topMargin = 36f;
private readonly float _bottomMargin = 36f; private readonly double _bottomMargin = 36f;
private DockPlacement _dockPlacement;
private Color _adaptiveGrayedFontColor = Colors.Transparent; private Color _adaptiveGrayedFontColor = Colors.Transparent;
private Color? _adaptiveColoredFontColor = null; private Color? _adaptiveColoredFontColor = null;
@@ -137,10 +123,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private Color _fgFontColor; private Color _fgFontColor;
private Color _strokeFontColor; private Color _strokeFontColor;
private Color? _customBgFontColor;
private Color? _customFgFontColor;
private Color? _customStrokeFontColor;
private int _playingLineIndex = -1; private int _playingLineIndex = -1;
private int _startVisibleLineIndex = -1; private int _startVisibleLineIndex = -1;
@@ -149,36 +131,25 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isDebugOverlayEnabled = false; private bool _isDebugOverlayEnabled = false;
private bool _isDesktopMode = false; private bool _isDesktopMode = false;
private bool _isDockMode = false; private bool _isDockMode = false;
private bool _isFanLyricsEnabled = false;
private bool _isPlaying = true; private bool _isPlaying = true;
private bool _isLyricsWindowLocked = false; private bool _isLyricsWindowLocked = false;
private bool _isMouseWithinWindow = false; private bool _isMouseWithinWindow = false;
private bool _isDynamicCoverOverlayEnabled;
private bool _isLyricsGlowEffectEnabled;
private bool _isLyricsFloatAnimationEnabled;
private bool _isLayoutChanged = true; private bool _isLayoutChanged = true;
private int _langIndex = 0; private int _langIndex = 0;
private List<LyricsData> _lyricsDataArr = []; private List<LyricsData> _lyricsDataArr = [];
private bool _isTranslationEnabled;
private bool _showTranslationOnly;
private int _targetLanguageIndex;
private bool _isLibreTranslateEnabled;
private string _lyricsTranslationSeparator;
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
private int _timelineSyncThreshold = 0; private int _timelineSyncThreshold = 0;
private CanvasTextFormat _lyricsTextFormat = new() private CanvasTextFormat _lyricsTextFormat = new()
{ {
HorizontalAlignment = CanvasHorizontalAlignment.Left, HorizontalAlignment = CanvasHorizontalAlignment.Left,
VerticalAlignment = CanvasVerticalAlignment.Top, VerticalAlignment = CanvasVerticalAlignment.Top,
FontSize = 12,
}; };
private CanvasTextFormat _titleTextFormat = new() private CanvasTextFormat _titleTextFormat = new()
{ {
@@ -212,11 +183,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private LyricsLayoutOrientation _lyricsLayoutOrientation; private LyricsLayoutOrientation _lyricsLayoutOrientation;
private int _albumArtBgBlurAmount;
private int _albumArtBgOpacity;
private int _coverAcrylicEffectAmount;
[ObservableProperty] [ObservableProperty]
public partial bool IsTranslating { get; set; } = false; public partial bool IsTranslating { get; set; } = false;
@@ -250,7 +216,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
return GetMaxLyricsLineIndexBoundaries().Item2; return GetMaxLyricsLineIndexBoundaries().Item2;
} }
private void GetLinePlayingProgress(int lineIndex, out int charStartIndex, out int charLength, out float charProgress) private void GetLinePlayingProgress(int lineIndex, out int charStartIndex, out int charLength, out double charProgress)
{ {
charStartIndex = 0; charStartIndex = 0;
charLength = 0; charLength = 0;
@@ -265,7 +231,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
else if (nextLine != null) lineEndMs = nextLine.StartMs; else if (nextLine != null) lineEndMs = nextLine.StartMs;
else lineEndMs = _songDurationMs; else lineEndMs = _songDurationMs;
float now = (float)TotalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds; double now = (double)TotalTime.TotalMilliseconds + (double)_positionOffset.TotalMilliseconds;
// 1. 还没到本句 // 1. 还没到本句
if (now < line.StartMs) if (now < line.StartMs)
@@ -325,11 +291,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
int textLength = line.OriginalText.Length; int textLength = line.OriginalText.Length;
if (textLength == 0) return; if (textLength == 0) return;
float lineProgress = (now - line.StartMs) / (lineEndMs - line.StartMs); double lineProgress = (now - line.StartMs) / (lineEndMs - line.StartMs);
lineProgress = Math.Clamp(lineProgress, 0f, 1f); lineProgress = Math.Clamp(lineProgress, 0f, 1f);
// 计算当前高亮到第几个字 // 计算当前高亮到第几个字
float charFloatIndex = lineProgress * textLength; double charFloatIndex = lineProgress * textLength;
int charIndex = (int)charFloatIndex; int charIndex = (int)charFloatIndex;
charStartIndex = Math.Clamp(charIndex, 0, textLength - 1); charStartIndex = Math.Clamp(charIndex, 0, textLength - 1);
charLength = 1; charLength = 1;
@@ -369,7 +335,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private void PlaybackService_TimelineChanged(object? sender, TimelineChangedEventArgs e) private void PlaybackService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
{ {
if (Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold) var diff = Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds);
if (diff >= _timelineSyncThreshold)
{ {
TotalTime = e.Position; TotalTime = e.Position;
if (TotalTime.TotalSeconds <= 1) if (TotalTime.TotalSeconds <= 1)
@@ -378,6 +345,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLastFMTracked = false; _isLastFMTracked = false;
} }
} }
// 大跨度,刷新布局,避免歌词不显示
if (diff >= _timelineSyncThreshold + 5000)
{
_isLayoutChanged = true;
}
} }
private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e) private void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
@@ -418,14 +390,24 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{ {
if (e.AlbumArtSwBitmap != _albumArtSwBitmap) if (e.AlbumArtSwBitmap != _albumArtSwBitmap)
{ {
//_lastAlbumArtSwBitmap?.Dispose(); _cachedAlbumArtSwBitmaps.Append(_albumArtSwBitmap);
_lastAlbumArtSwBitmap = null;
_lastAlbumArtSwBitmap = _albumArtSwBitmap; _lastAlbumArtSwBitmap = _albumArtSwBitmap;
//_albumArtSwBitmap?.Dispose(); if (_cachedAlbumArtSwBitmaps.Count > 2)
_albumArtSwBitmap = null; {
_cachedAlbumArtSwBitmaps.Dequeue()?.Dispose();
}
_cachedAlbumArtSwBitmaps.Append(e.AlbumArtSwBitmap);
_albumArtSwBitmap = e.AlbumArtSwBitmap; _albumArtSwBitmap = e.AlbumArtSwBitmap;
if (_cachedAlbumArtSwBitmaps.Count > 2)
{
_cachedAlbumArtSwBitmaps.Dequeue()?.Dispose();
}
_albumArtChanged = true; _albumArtChanged = true;
_albumArtLightAccentColor = e.AlbumArtLightAccentColor ?? Colors.Transparent; _albumArtLightAccentColor = e.AlbumArtLightAccentColor ?? Colors.Transparent;
@@ -433,6 +415,10 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateColorConfig(); UpdateColorConfig();
} }
else
{
e.AlbumArtSwBitmap?.Dispose();
}
} }
private void UpdateTranslations() private void UpdateTranslations()
@@ -442,7 +428,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLayoutChanged = true; _isLayoutChanged = true;
IsTranslating = true; IsTranslating = true;
if (_isTranslationEnabled) if (_settingsService.AppSettings.TranslationSettings.IsTranslationEnabled)
{ {
_ = _refreshLyricsRunner.RunAsync(async token => _ = _refreshLyricsRunner.RunAsync(async token =>
{ {
@@ -480,19 +466,19 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr); int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
if (found >= 0) if (found >= 0)
{ {
if (_showTranslationOnly) if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
{ {
_lyricsDataArr[found].SetDisplayedTextInOriginalText(); _lyricsDataArr[found].SetDisplayedTextInOriginalText();
_langIndex = found; _langIndex = found;
} }
else else
{ {
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _lyricsTranslationSeparator, 50); _lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], _lyricsStyleSettings.LyricsTranslationSeparator, 50);
_langIndex = 0; _langIndex = 0;
} }
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider(); TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
} }
else if (_isLibreTranslateEnabled) else if (_settingsService.AppSettings.TranslationSettings.IsLibreTranslateEnabled)
{ {
string translated = string.Empty; string translated = string.Empty;
try try
@@ -500,7 +486,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token); translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
if (translated == string.Empty) return; if (translated == string.Empty) return;
if (_showTranslationOnly) if (_settingsService.AppSettings.TranslationSettings.ShowTranslationOnly)
{ {
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated); _lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
_lyricsDataArr[^1].SetDisplayedTextInOriginalText(); _lyricsDataArr[^1].SetDisplayedTextInOriginalText();
@@ -508,7 +494,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
} }
else else
{ {
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _lyricsTranslationSeparator); _lyricsDataArr[0].SetDisplayedTextAlongWith(translated, _lyricsStyleSettings.LyricsTranslationSeparator);
_langIndex = 0; _langIndex = 0;
} }
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate; TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;

View File

@@ -3,11 +3,11 @@
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService; using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel; using BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel;
using BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel;
using BetterLyrics.WinUI3.Views; using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
@@ -15,13 +15,8 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI; using CommunityToolkit.WinUI;
using Microsoft.UI;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Vanara.PInvoke; using Vanara.PInvoke;
using Windows.System; using Windows.System;
using Windows.UI; using Windows.UI;
@@ -38,7 +33,9 @@ namespace BetterLyrics.WinUI3
IRecipient<PropertyChangedMessage<ElementTheme>>, IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<DockPlacement>> IRecipient<PropertyChangedMessage<DockPlacement>>
{ {
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>(); private readonly IMediaSessionsService _mediaSessionsService;
private readonly ISettingsService _settingsService;
private ForegroundWindowWatcher? _windowWatcher = null; private ForegroundWindowWatcher? _windowWatcher = null;
private bool _ignoreFullscreenWindow; private bool _ignoreFullscreenWindow;
private bool _hideWindowWhenNotPlaying; private bool _hideWindowWhenNotPlaying;
@@ -47,15 +44,18 @@ namespace BetterLyrics.WinUI3
private int _dockWindowHeight; private int _dockWindowHeight;
private string _dockMonitorDeviceName; private string _dockMonitorDeviceName;
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService) public LyricsWindowViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService)
{ {
_dockMonitorDeviceName = _settingsService.DockMonitorDeviceName; _settingsService = settingsService;
_ignoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow; _mediaSessionsService = mediaSessionsService;
_hideWindowWhenNotPlaying = _settingsService.HideWindowWhenNotPlaying;
IsImmersiveMode = _settingsService.IsImmersiveMode; _dockMonitorDeviceName = _settingsService.AppSettings.DockModeSettings.DockMonitorDeviceName;
_dockPlacement = _settingsService.DockPlacement; _ignoreFullscreenWindow = _settingsService.AppSettings.GeneralSettings.IgnoreFullscreenWindow;
_dockWindowHeight = _settingsService.DockWindowHeight; _hideWindowWhenNotPlaying = _settingsService.AppSettings.GeneralSettings.HideWindowWhenNotPlaying;
OnIsImmersiveModeChanged(_settingsService.IsImmersiveMode); IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
_dockPlacement = _settingsService.AppSettings.DockModeSettings.DockPlacement;
_dockWindowHeight = _settingsService.AppSettings.DockModeSettings.DockWindowHeight;
OnIsImmersiveModeChanged(_settingsService.AppSettings.GeneralSettings.IsImmersiveMode);
_mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged; _mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
} }
@@ -86,7 +86,7 @@ namespace BetterLyrics.WinUI3
public partial bool IsImmersiveMode { get; set; } public partial bool IsImmersiveMode { get; set; }
[ObservableProperty] [ObservableProperty]
public partial float TopCommandGridOpacity { get; set; } public partial double TopCommandGridOpacity { get; set; }
[ObservableProperty] [ObservableProperty]
public partial ElementTheme ThemeType { get; set; } = ElementTheme.Default; public partial ElementTheme ThemeType { get; set; } = ElementTheme.Default;
@@ -153,13 +153,13 @@ namespace BetterLyrics.WinUI3
} }
} }
} }
else if (message.Sender is SettingsPageViewModel) else if (message.Sender is GeneralSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.IgnoreFullscreenWindow)) if (message.PropertyName == nameof(GeneralSettings.IgnoreFullscreenWindow))
{ {
_ignoreFullscreenWindow = message.NewValue; _ignoreFullscreenWindow = message.NewValue;
} }
else if (message.PropertyName == nameof(SettingsPageViewModel.HideWindowWhenNotPlaying)) else if (message.PropertyName == nameof(GeneralSettings.HideWindowWhenNotPlaying))
{ {
_hideWindowWhenNotPlaying = message.NewValue; _hideWindowWhenNotPlaying = message.NewValue;
UpdateDockWindow(); UpdateDockWindow();
@@ -180,19 +180,19 @@ namespace BetterLyrics.WinUI3
public void Receive(PropertyChangedMessage<int> message) public void Receive(PropertyChangedMessage<int> message)
{ {
if (message.Sender is SettingsPageViewModel) if (message.Sender is DockModeSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.DockWindowHeight)) if (message.PropertyName == nameof(DockModeSettings.DockWindowHeight))
{ {
_dockWindowHeight = message.NewValue; _dockWindowHeight = message.NewValue;
UpdateDockWindow(); UpdateDockWindow();
} }
else if (message.Sender is SettingsPageViewModel) }
else if (message.Sender is DesktopModeSettings)
{
if (message.PropertyName == nameof(DesktopModeSettings.LockHotKeyIndex))
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.LockHotKeyIndex)) UpdateLockHotKey(message.NewValue);
{
UpdateLockHotKey(message.NewValue);
}
} }
} }
} }
@@ -251,12 +251,12 @@ namespace BetterLyrics.WinUI3
public void UpdateAccentColor(nint hwnd) public void UpdateAccentColor(nint hwnd)
{ {
WindowPixelSampleMode mode = IsDesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode(); WindowPixelSampleMode mode = IsDesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode();
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, _settingsService.DockMonitorDeviceName, mode).ToColor(); ActivatedWindowAccentColor = ColorHelper.GetAccentColor(hwnd, _settingsService.AppSettings.DockModeSettings.DockMonitorDeviceName, mode).ToColor();
} }
public void InitLockHotKey() public void InitLockHotKey()
{ {
UpdateLockHotKey(_settingsService.LockHotKeyIndex); UpdateLockHotKey(_settingsService.AppSettings.DesktopModeSettings.LockHotKeyIndex);
} }
[RelayCommand] [RelayCommand]
@@ -269,7 +269,7 @@ namespace BetterLyrics.WinUI3
{ {
DesktopModeHelper.SetClickThrough(window, false); DesktopModeHelper.SetClickThrough(window, false);
IsLyricsWindowLocked = false; IsLyricsWindowLocked = false;
IsImmersiveMode = _settingsService.IsImmersiveMode; IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
} }
else else
{ {
@@ -327,14 +327,14 @@ namespace BetterLyrics.WinUI3
[RelayCommand] [RelayCommand]
private void OnImmersiveToggleButtonEnabledChanged() private void OnImmersiveToggleButtonEnabledChanged()
{ {
_settingsService.IsImmersiveMode = IsImmersiveMode; _settingsService.AppSettings.GeneralSettings.IsImmersiveMode = IsImmersiveMode;
} }
public void Receive(PropertyChangedMessage<DockPlacement> message) public void Receive(PropertyChangedMessage<DockPlacement> message)
{ {
if (message.Sender is SettingsPageViewModel) if (message.Sender is DockModeSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.DockPlacement)) if (message.PropertyName == nameof(DockModeSettings.DockPlacement))
{ {
_dockPlacement = message.NewValue; _dockPlacement = message.NewValue;
UpdateDockWindow(); UpdateDockWindow();
@@ -344,9 +344,9 @@ namespace BetterLyrics.WinUI3
public void Receive(PropertyChangedMessage<string> message) public void Receive(PropertyChangedMessage<string> message)
{ {
if (message.Sender is SettingsPageViewModel) if (message.Sender is DockModeSettings)
{ {
if (message.PropertyName == nameof(SettingsPageViewModel.SelectedDockMonitorDeviceName)) if (message.PropertyName == nameof(DockModeSettings.DockMonitorDeviceName))
{ {
_dockMonitorDeviceName = message.NewValue; _dockMonitorDeviceName = message.NewValue;
UpdateDockWindow(); UpdateDockWindow();

View File

@@ -0,0 +1,84 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class MediaSettingsControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public MediaSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
[RelayCommand]
private async Task SelectAndAddFolderAsync(UIElement sender)
{
var window = WindowHelper.GetWindowByWindowType<SettingsWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FolderPicker();
picker.FileTypeFilter.Add("*");
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
AddFolderAsync(folder.Path);
}
}
private void AddFolderAsync(string path)
{
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
if (AppSettings.LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo"), InfoBarSeverity.Warning);
}
else if (AppSettings.LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
{
// 添加的文件夹是现有文件夹的子文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo"), InfoBarSeverity.Warning);
}
else if (AppSettings.LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
)
{
// 添加的文件夹是现有文件夹的父文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo"), InfoBarSeverity.Warning);
}
else
{
AppSettings.LocalMediaFolders.Add(new LocalMediaFolder(path, true));
}
}
public void RemoveFolderAsync(LocalMediaFolder folder)
{
AppSettings.LocalMediaFolders.Remove(folder);
}
}
}

View File

@@ -1,7 +1,9 @@
using ATL; using ATL;
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models; using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services; using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LibWatcherService; using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
@@ -25,10 +27,11 @@ using Windows.Media.Playback;
namespace BetterLyrics.WinUI3.ViewModels namespace BetterLyrics.WinUI3.ViewModels
{ {
public partial class MusicGalleryViewModel : BaseViewModel, public partial class MusicGalleryViewModel : BaseViewModel
IRecipient<PropertyChangedMessage<ObservableCollection<LocalMediaFolder>>>
{ {
private readonly ILibWatcherService _libWatcherService; private readonly ILibWatcherService _libWatcherService;
private readonly ISettingsService _settingsService;
private readonly MediaPlayer _mediaPlayer = new(); private readonly MediaPlayer _mediaPlayer = new();
private readonly MediaTimelineController _timelineController = new(); private readonly MediaTimelineController _timelineController = new();
private readonly SystemMediaTransportControls _smtc; private readonly SystemMediaTransportControls _smtc;
@@ -85,13 +88,18 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty] [ObservableProperty]
public partial string SongSearchQuery { get; set; } = string.Empty; public partial string SongSearchQuery { get; set; } = string.Empty;
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) : base(settingsService) public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService)
{ {
_settingsService = settingsService;
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty)); SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty));
RefreshSongs(); RefreshSongs();
PlaybackOrder = _settingsService.PlaybackOrder; _settingsService.AppSettings.LocalMediaFolders.CollectionChanged += LocalMediaFolders_CollectionChanged;
_settingsService.AppSettings.LocalMediaFolders.ItemPropertyChanged += LocalMediaFolders_ItemPropertyChanged;
PlaybackOrder = _settingsService.AppSettings.MusicGallerySettings.PlaybackOrder;
_mediaPlayer.MediaOpened += MediaPlayer_MediaOpened; _mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded; _mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
@@ -110,6 +118,16 @@ namespace BetterLyrics.WinUI3.ViewModels
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged; _libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
} }
private void LocalMediaFolders_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
{
RefreshSongs();
}
private void LocalMediaFolders_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RefreshSongs();
}
private void MediaPlayer_MediaEnded(MediaPlayer sender, object args) private void MediaPlayer_MediaEnded(MediaPlayer sender, object args)
{ {
PlayNextTrack(); PlayNextTrack();
@@ -242,7 +260,7 @@ namespace BetterLyrics.WinUI3.ViewModels
Task.Run(() => Task.Run(() =>
{ {
foreach (var folder in _settingsService.LocalMediaFolders) foreach (var folder in _settingsService.AppSettings.LocalMediaFolders)
{ {
if (Directory.Exists(folder.Path) && folder.IsEnabled) if (Directory.Exists(folder.Path) && folder.IsEnabled)
{ {
@@ -264,7 +282,7 @@ namespace BetterLyrics.WinUI3.ViewModels
IsDataLoading = false; IsDataLoading = false;
}); });
}); });
}, TimeSpan.FromMilliseconds(100)); }, Constants.Time.DebounceTimeout);
} }
public void ApplyPlaylist() public void ApplyPlaylist()
@@ -405,18 +423,7 @@ namespace BetterLyrics.WinUI3.ViewModels
partial void OnPlaybackOrderChanged(PlaybackOrder value) partial void OnPlaybackOrderChanged(PlaybackOrder value)
{ {
_settingsService.PlaybackOrder = value; _settingsService.AppSettings.MusicGallerySettings.PlaybackOrder = value;
}
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> message)
{
if (message.Sender is SettingsPageViewModel.SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.SettingsPageViewModel.LocalMediaFolders))
{
RefreshSongs();
}
}
} }
} }
} }

View File

@@ -0,0 +1,206 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Hqub.Lastfm.Entities;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class PlaybackSettingsControlViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<LyricsSearchProvider?>>,
IRecipient<PropertyChangedMessage<TranslationSearchProvider?>>
{
private readonly IMediaSessionsService _mediaSessionsService;
private readonly ITranslateService _libreTranslateService;
private readonly ILastFMService _lastFMService;
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
public partial MediaSourceProviderInfo? SelectedMediaSourceProvider { get; set; }
[ObservableProperty]
public partial bool IsLastFMAuthenticated { get; set; }
[ObservableProperty]
public partial User? LastFMUser { get; set; }
[ObservableProperty]
public partial bool IsLibreTranslateServerTesting { get; set; } = false;
[ObservableProperty]
public partial bool IsLXMusicServerTesting { get; set; } = false;
[ObservableProperty]
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
[ObservableProperty]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
public PlaybackSettingsControlViewModel(
ISettingsService settingsService,
IMediaSessionsService mediaSessionsService,
ITranslateService libreTranslateService,
ILastFMService lastFMService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
_libreTranslateService = libreTranslateService;
_lastFMService = lastFMService;
_lastFMService.UserChanged += LastFMService_UserChanged;
_lastFMService.IsAuthenticatedChanged += LastFMService_IsAuthenticatedChanged;
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
AppSettings = _settingsService.AppSettings;
AppSettings.MediaSourceProvidersInfo.CollectionChanged += MediaSourceProvidersInfo_CollectionChanged;
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
LastFMUser = _lastFMService.User;
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
}
private void MediaSourceProvidersInfo_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
}
private void LastFMService_IsAuthenticatedChanged(object? sender, Events.LastFMIsAuthenticatedChangedEventArgs e)
{
IsLastFMAuthenticated = e.IsAuthenticated;
}
private void LastFMService_UserChanged(object? sender, Events.LastFMUserChangedEventArgs e)
{
LastFMUser = e.User;
}
private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
var current = AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.SourceAppUserModelId)?.FirstOrDefault();
if (_mediaSessionsService.Position.TotalSeconds <= 1 && current?.ResetPositionOffsetOnSongChanged == true)
{
current.PositionOffset = 0;
}
}
private void MediaSessionsService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
{
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
}
[RelayCommand]
private void LibreTranslateServerTest()
{
IsLibreTranslateServerTesting = true;
Task.Run(async () =>
{
try
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
});
}
catch (Exception)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
});
}
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
IsLibreTranslateServerTesting = false;
});
});
}
[RelayCommand]
private async Task LastFMAuthAsync()
{
await _lastFMService.AuthAsync();
}
[RelayCommand]
private async Task LastFMUnAuthAsync()
{
await _lastFMService.UnAuthAsync();
}
[RelayCommand]
private async Task LastFMRefreshAsync()
{
await _lastFMService.RefreshAsync();
}
[RelayCommand]
private void LXMusicServerTest()
{
IsLXMusicServerTesting = true;
Task.Run(async () =>
{
bool testResult = await NetHelper.CheckConnectivity($"{AppSettings.GeneralSettings.LXMusicServer}/status");
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
if (testResult)
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
}
IsLXMusicServerTesting = false;
});
});
}
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
{
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
{
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.LyricsSearchProvider))
{
LyricsSearchProvider = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<TranslationSearchProvider?> message)
{
if (message.Sender is LyricsRendererViewModel.LyricsRendererViewModel)
{
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsRendererViewModel.TranslationSearchProvider))
{
TranslationSearchProvider = message.NewValue;
}
}
}
}
}

View File

@@ -0,0 +1,115 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class SettingsPageViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
public string Version { get; set; } = MetadataHelper.AppVersion;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsDebugOverlayEnabled { get; set; } = false;
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; } = "App";
public SettingsPageViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
[RelayCommand]
private async Task LaunchProjectGitHubPageAsync()
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GithubUrl));
}
[RelayCommand]
private static async Task OpenCacheFolderAsync()
{
await Windows.System.Launcher.LaunchFolderPathAsync(Helper.PathHelper.CacheFolder);
}
[RelayCommand]
private static void RestartApp()
{
Helper.WindowHelper.RestartApp();
}
[RelayCommand]
private async Task ImportSettingsAsync()
{
var window = WindowHelper.GetWindowByWindowType<SettingsWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add(".json");
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var file = await picker.PickSingleFileAsync();
var succeed = _settingsService.ImportSettings(file.Path);
if (succeed)
{
WindowHelper.RestartApp();
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("ImportSettingsFailed") ?? "");
}
}
[RelayCommand]
private async Task ExportSettingsAsync()
{
var window = WindowHelper.GetWindowByWindowType<SettingsWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FolderPicker();
picker.FileTypeFilter.Add("*");
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
_settingsService.ExportSettings(folder.Path);
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("ExportSettingsSuccess") ?? "", InfoBarSeverity.Success);
}
}
}
}

View File

@@ -1,132 +0,0 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel
{
public partial class SettingsPageViewModel
{
public SettingsPageViewModel(
ISettingsService settingsService,
ILibWatcherService libWatcherService,
IMediaSessionsService mediaSessionsService,
ITranslateService libreTranslateService,
ILastFMService lastFMService) : base(settingsService)
{
_libWatcherService = libWatcherService;
_mediaSessionsService = mediaSessionsService;
_libreTranslateService = libreTranslateService;
// LastFM
_lastFMService = lastFMService;
_lastFMService.UserChanged += LastFMService_UserChanged;
_lastFMService.IsAuthenticatedChanged += LastFMService_IsAuthenticatedChanged;
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
LastFMUser = _lastFMService.User;
IsLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled;
LibreTranslateServer = _settingsService.LibreTranslateServer;
SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
LocalMediaFolders = [.. _settingsService.LocalMediaFolders];
AlbumArtSearchProvidersInfo = [.. _settingsService.AlbumArtSearchProvidersInfo];
Language = _settingsService.Language;
CoverImageRadius = _settingsService.CoverImageRadius;
AutoStartWindowType = _settingsService.AutoStartWindowType;
AutoLockOnDesktopMode = _settingsService.AutoLockOnDesktopMode;
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
CoverAcrylicEffectAmount = _settingsService.CoverAcrylicEffectAmount;
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
SongInfoAlignmentType = _settingsService.SongInfoAlignmentType;
LyricsFontWeight = _settingsService.LyricsFontWeight;
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
// Font size
LyricsStandardFontSize = _settingsService.LyricsStandardFontSize;
LyricsDockFontSize = _settingsService.LyricsDockFontSize;
LyricsDesktopFontSize = _settingsService.LyricsDesktopFontSize;
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
LyricsHighlightScope = _settingsService.LyricsHighlightScope;
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
LyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
LyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
LyricsStrokeFontColorType = _settingsService.LyricsStrokeFontColorType;
LyricsCustomBgFontColor = _settingsService.LyricsCustomBgFontColor;
LyricsCustomFgFontColor = _settingsService.LyricsCustomFgFontColor;
LyricsCustomStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme;
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
SelectedMediaSourceProvider = MediaSourceProvidersInfo.FirstOrDefault();
IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
LyricsScrollEasingType = _settingsService.LyricsScrollEasingType;
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
IsLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled;
LockHotKeyIndex = _settingsService.LockHotKeyIndex;
LXMusicServer = _settingsService.LXMusicServer;
DockPlacement = _settingsService.DockPlacement;
LyricsBgFontOpacity = _settingsService.LyricsBgFontOpacity;
HideWindowWhenNotPlaying = _settingsService.HideWindowWhenNotPlaying;
DockWindowHeight = _settingsService.DockWindowHeight;
SystemFontNames = [.. FontHelper.SystemFontFamilies];
SelectedFontFamilyIndex = _settingsService.SelectedFontFamilyIndex;
LyricsFontFamily = _settingsService.LyricsFontFamily;
IsDragEverywhereEnabled = _settingsService.IsDragEverywhereEnabled;
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
SelectedDockMonitorDeviceName = _settingsService.DockMonitorDeviceName;
LyricsTranslationSeparator = _settingsService.LyricsTranslationSeparator;
_mediaSessionsService.MediaSourceProvidersInfoChanged += MediaSessionsService_SessionIdsChanged;
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
}
private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
var current = MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.SourceAppUserModelId)?.FirstOrDefault();
if (_mediaSessionsService.Position.TotalSeconds <= 1 && current?.ResetPositionOffsetOnSongChanged == true)
{
current.PositionOffset = 0;
}
}
private void LastFMService_IsAuthenticatedChanged(object? sender, Events.LastFMIsAuthenticatedChangedEventArgs e)
{
IsLastFMAuthenticated = e.IsAuthenticated;
}
private void LastFMService_UserChanged(object? sender, Events.LastFMUserChangedEventArgs e)
{
LastFMUser = e.User;
}
}
}

View File

@@ -1,227 +0,0 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Views;
using Microsoft.UI.Xaml;
using Windows.Globalization;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel
{
public partial class SettingsPageViewModel
{
partial void OnDockPlacementChanged(DockPlacement value)
{
_settingsService.DockPlacement = value;
}
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
{
_settingsService.LyricsScrollEasingType = value;
}
partial void OnLyricsScrollDurationChanged(int value)
{
_settingsService.LyricsScrollDuration = value;
}
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
{
_settingsService.LyricsBackgroundTheme = value;
}
partial void OnLyricsFontStrokeWidthChanged(int value)
{
_settingsService.LyricsFontStrokeWidth = value;
}
partial void OnIgnoreFullscreenWindowChanged(bool value)
{
_settingsService.IgnoreFullscreenWindow = value;
}
partial void OnSelectedTargetLanguageIndexChanged(int value)
{
_settingsService.SelectedTargetLanguageIndex = value;
}
partial void OnLibreTranslateServerChanged(string value)
{
_settingsService.LibreTranslateServer = value;
}
partial void OnLXMusicServerChanged(string value)
{
_settingsService.LXMusicServer = value;
}
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
{
_settingsService.AutoStartWindowType = value;
}
partial void OnAutoLockOnDesktopModeChanged(bool value)
{
_settingsService.AutoLockOnDesktopMode = value;
}
partial void OnCoverImageRadiusChanged(int value)
{
_settingsService.CoverImageRadius = value;
}
partial void OnCoverOverlayBlurAmountChanged(int value)
{
_settingsService.CoverOverlayBlurAmount = value;
}
partial void OnCoverAcrylicEffectAmountChanged(int value)
{
_settingsService.CoverAcrylicEffectAmount = value;
}
partial void OnCoverOverlayOpacityChanged(int value)
{
_settingsService.CoverOverlayOpacity = value;
}
partial void OnIsDynamicCoverOverlayEnabledChanged(bool value)
{
_settingsService.IsDynamicCoverOverlayEnabled = value;
}
partial void OnLanguageChanged(Enums.Language value)
{
switch (value)
{
case Enums.Language.FollowSystem:
ApplicationLanguages.PrimaryLanguageOverride = "";
break;
case Enums.Language.English:
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
break;
case Enums.Language.SimplifiedChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";
break;
case Enums.Language.TraditionalChinese:
ApplicationLanguages.PrimaryLanguageOverride = "zh-TW";
break;
case Enums.Language.Japanese:
ApplicationLanguages.PrimaryLanguageOverride = "ja-JP";
break;
case Enums.Language.Korean:
ApplicationLanguages.PrimaryLanguageOverride = "ko-KR";
break;
default:
break;
}
_settingsService.Language = Language;
}
partial void OnIsFanLyricsEnabledChanged(bool value)
{
_settingsService.IsFanLyricsEnabled = value;
}
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
{
_settingsService.IsLyricsGlowEffectEnabled = value;
}
partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value)
{
_settingsService.LyricsAlignmentType = value;
}
partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value)
{
_settingsService.SongInfoAlignmentType = value;
}
partial void OnLyricsBlurAmountChanged(int value)
{
_settingsService.LyricsBlurAmount = value;
}
partial void OnLyricsCustomBgFontColorChanged(Color value)
{
_settingsService.LyricsCustomBgFontColor = value;
}
partial void OnLyricsCustomFgFontColorChanged(Color value)
{
_settingsService.LyricsCustomFgFontColor = value;
}
partial void OnLyricsCustomStrokeFontColorChanged(Color value)
{
_settingsService.LyricsCustomStrokeFontColor = value;
}
partial void OnLyricsBgFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsBgFontColorType = value;
}
partial void OnLyricsFgFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsFgFontColorType = value;
}
partial void OnLyricsStrokeFontColorTypeChanged(LyricsFontColorType value)
{
_settingsService.LyricsStrokeFontColorType = value;
}
partial void OnLyricsStandardFontSizeChanged(int value)
{
_settingsService.LyricsStandardFontSize = value;
}
partial void OnLyricsDockFontSizeChanged(int value)
{
_settingsService.LyricsDockFontSize = value;
}
partial void OnLyricsDesktopFontSizeChanged(int value)
{
_settingsService.LyricsDesktopFontSize = value;
}
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
{
_settingsService.LyricsFontWeight = value;
}
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
{
_settingsService.LyricsGlowEffectScope = value;
}
partial void OnLyricsHighlightScopeChanged(LineRenderingType value)
{
_settingsService.LyricsHighlightScope = value;
}
partial void OnLyricsLineSpacingFactorChanged(float value)
{
_settingsService.LyricsLineSpacingFactor = value;
}
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
{
_settingsService.LyricsVerticalEdgeOpacity = value;
}
partial void OnIsLyricsFloatAnimationEnabledChanged(bool value)
{
_settingsService.IsLyricsFloatAnimationEnabled = value;
}
partial void OnLyricsBgFontOpacityChanged(int value)
{
_settingsService.LyricsBgFontOpacity = value;
}
partial void OnHideWindowWhenNotPlayingChanged(bool value)
{
_settingsService.HideWindowWhenNotPlaying = value;
}
partial void OnDockWindowHeightChanged(int value)
{
_settingsService.DockWindowHeight = value;
}
partial void OnSelectedFontFamilyIndexChanged(int value)
{
_settingsService.SelectedFontFamilyIndex = value;
LyricsFontFamily = SystemFontNames[value];
}
partial void OnLyricsFontFamilyChanged(string value)
{
_settingsService.LyricsFontFamily = value;
}
partial void OnIsDragEverywhereEnabledChanged(bool value)
{
_settingsService.IsDragEverywhereEnabled = value;
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (lyricsWindow != null)
{
lyricsWindow.UpdateTitleBarArea();
}
}
partial void OnIsLibreTranslateEnabledChanged(bool value)
{
_settingsService.IsLibreTranslateEnabled = value;
}
partial void OnSelectedDockMonitorDeviceNameChanged(string value)
{
_settingsService.DockMonitorDeviceName = value;
}
partial void OnLyricsTranslationSeparatorChanged(string value)
{
_settingsService.LyricsTranslationSeparator = value;
}
}
}

View File

@@ -1,248 +0,0 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using Hqub.Lastfm.Entities;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel
{
public partial class SettingsPageViewModel
{
public string Version { get; set; } = MetadataHelper.AppVersion;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLibreTranslateEnabled { get; set; }
[ObservableProperty]
public partial bool IsDragEverywhereEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial string LyricsFontFamily { get; set; }
[ObservableProperty]
public partial ObservableCollection<string> SystemFontNames { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int SelectedFontFamilyIndex { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial DockPlacement DockPlacement { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LockHotKeyIndex { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial ElementTheme LyricsBackgroundTheme { get; set; }
[ObservableProperty]
public partial AutoStartWindowType AutoStartWindowType { get; set; }
[ObservableProperty]
public partial bool AutoLockOnDesktopMode { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int CoverImageRadius { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int CoverOverlayBlurAmount { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int CoverOverlayOpacity { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsDebugOverlayEnabled { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLogEnabled { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsDynamicCoverOverlayEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int CoverAcrylicEffectAmount { get; set; }
[ObservableProperty]
public partial Enums.Language Language { get; set; }
[ObservableProperty]
public partial ObservableCollection<LocalMediaFolder> LocalMediaFolders { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial ObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
[ObservableProperty]
public partial ObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
[ObservableProperty]
public partial MediaSourceProviderInfo? SelectedMediaSourceProvider { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsFanLyricsEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLyricsGlowEffectEnabled { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial TextAlignmentType LyricsAlignmentType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial TextAlignmentType SongInfoAlignmentType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsBlurAmount { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial Color LyricsCustomBgFontColor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial Color LyricsCustomFgFontColor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial Color LyricsCustomStrokeFontColor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsBgFontOpacity { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LyricsFontColorType LyricsBgFontColorType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LyricsFontColorType LyricsFgFontColorType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsStandardFontSize { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsDockFontSize { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsDesktopFontSize { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LyricsFontWeight LyricsFontWeight { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial LineRenderingType LyricsHighlightScope { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial float LyricsLineSpacingFactor { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsVerticalEdgeOpacity { get; set; }
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; } = "App";
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLyricsFloatAnimationEnabled { get; set; }
[ObservableProperty]
public partial string LibreTranslateServer { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int SelectedTargetLanguageIndex { get; set; } = 0;
[ObservableProperty]
public partial bool IsLibreTranslateServerTesting { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsFontStrokeWidth { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IgnoreFullscreenWindow { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial EasingType LyricsScrollEasingType { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int LyricsScrollDuration { get; set; }
[ObservableProperty]
public partial bool IsLXMusicServerTesting { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial string LXMusicServer { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool HideWindowWhenNotPlaying { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int DockWindowHeight { get; set; }
// Dock Monitor
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial string SelectedDockMonitorDeviceName { get; set; }
[ObservableProperty]
public partial ObservableCollection<string> MonitorDeviceNames { get; set; }
// LastFM
[ObservableProperty]
public partial bool IsLastFMAuthenticated { get; set; }
[ObservableProperty]
public partial User? LastFMUser { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial string LyricsTranslationSeparator { get; set; }
}
}

View File

@@ -1,270 +0,0 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using WinRT.Interop;
namespace BetterLyrics.WinUI3.ViewModels.SettingsPageViewModel
{
public partial class SettingsPageViewModel : BaseViewModel
{
private readonly ILibWatcherService _libWatcherService;
private readonly IMediaSessionsService _mediaSessionsService;
private readonly ITranslateService _libreTranslateService;
private readonly ILastFMService _lastFMService;
private void MediaSessionsService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
{
MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo];
}
public void OnLyricsSearchProvidersReordered()
{
_settingsService.MediaSourceProvidersInfo = [.. MediaSourceProvidersInfo];
Broadcast(
MediaSourceProvidersInfo,
MediaSourceProvidersInfo,
nameof(MediaSourceProvidersInfo)
);
}
public void OnAlbumArtSearchProvidersReordered()
{
_settingsService.AlbumArtSearchProvidersInfo = [.. AlbumArtSearchProvidersInfo];
Broadcast(
AlbumArtSearchProvidersInfo,
AlbumArtSearchProvidersInfo,
nameof(AlbumArtSearchProvidersInfo)
);
}
public void RemoveFolderAsync(LocalMediaFolder folder)
{
LocalMediaFolders.Remove(folder);
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
_libWatcherService.UpdateWatchers([.. LocalMediaFolders]);
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
}
public void ToggleLocalLyricsFolder()
{
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
}
public void ToggleAlbumArtSearchProvider(AlbumArtSearchProviderInfo providerInfo)
{
_settingsService.AlbumArtSearchProvidersInfo = [.. AlbumArtSearchProvidersInfo];
Broadcast(
AlbumArtSearchProvidersInfo,
AlbumArtSearchProvidersInfo,
nameof(AlbumArtSearchProvidersInfo)
);
}
public void BroadcastMediaSourceProvidersInfoChanged()
{
_dispatcherQueueTimer.Debounce(() =>
{
_settingsService.MediaSourceProvidersInfo = [.. MediaSourceProvidersInfo];
Broadcast(
MediaSourceProvidersInfo,
MediaSourceProvidersInfo,
nameof(MediaSourceProvidersInfo)
);
}, TimeSpan.FromMilliseconds(100));
}
private void AddFolderAsync(string path)
{
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
if (LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo"), InfoBarSeverity.Warning);
}
else if (LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
{
// 添加的文件夹是现有文件夹的子文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo"), InfoBarSeverity.Warning);
}
else if (LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
)
{
// 添加的文件夹是现有文件夹的父文件夹
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathIncludingOthersInfo"), InfoBarSeverity.Warning);
}
else
{
LocalMediaFolders.Add(new LocalMediaFolder(path, true));
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
_libWatcherService.UpdateWatchers([.. LocalMediaFolders]);
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
}
}
[RelayCommand]
private async Task LaunchProjectGitHubPageAsync()
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GithubUrl));
}
[RelayCommand]
private static async Task OpenCacheFolderAsync()
{
await Windows.System.Launcher.LaunchFolderPathAsync(Helper.PathHelper.CacheFolder);
}
[RelayCommand]
private static void RestartApp()
{
Helper.WindowHelper.RestartApp();
}
[RelayCommand]
private async Task SelectAndAddFolderAsync(UIElement sender)
{
var window = Helper.WindowHelper.GetWindowByWindowType<SettingsWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FolderPicker();
picker.FileTypeFilter.Add("*");
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
AddFolderAsync(folder.Path);
}
}
[RelayCommand]
private void LibreTranslateServerTest()
{
IsLibreTranslateServerTesting = true;
Task.Run(async () =>
{
try
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
});
}
catch (Exception)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
});
}
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
IsLibreTranslateServerTesting = false;
});
});
}
[RelayCommand]
private void LXMusicServerTest()
{
IsLXMusicServerTesting = true;
Task.Run(async () =>
{
bool testResult = await NetHelper.CheckConnectivity($"{LXMusicServer}/status");
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
if (testResult)
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
}
else
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), InfoBarSeverity.Error);
}
IsLXMusicServerTesting = false;
});
});
}
[RelayCommand]
private void RefreshMonitorDeviceNames()
{
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
SelectedDockMonitorDeviceName = MonitorHelper.GetPrimaryMonitorDeviceName();
}
[RelayCommand]
private async Task LastFMAuthAsync()
{
await _lastFMService.AuthAsync();
}
[RelayCommand]
private async Task LastFMUnAuthAsync()
{
await _lastFMService.UnAuthAsync();
}
[RelayCommand]
private async Task LastFMRefreshAsync()
{
await _lastFMService.RefreshAsync();
}
public async Task<bool> ToggleAutoStartupAsync(bool target)
{
StartupTask startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);
if (target)
{
await startupTask.RequestEnableAsync();
}
else
{
startupTask.Disable();
}
return await DetectIsAutoStartupEnabledAsync();
}
public async Task<bool> DetectIsAutoStartupEnabledAsync()
{
bool result = false;
var startupTask = await StartupTask.GetAsync(Constants.App.AutoStartupTaskId);
switch (startupTask.State)
{
case StartupTaskState.Disabled:
case StartupTaskState.DisabledByUser:
case StartupTaskState.DisabledByPolicy:
result = false;
break;
case StartupTaskState.Enabled:
result = true;
break;
}
return result;
}
}
}

View File

@@ -2,7 +2,7 @@
namespace BetterLyrics.WinUI3.ViewModels namespace BetterLyrics.WinUI3.ViewModels
{ {
public partial class SettingsWindowViewModel(ISettingsService settingsService) : BaseWindowViewModel(settingsService) public partial class SettingsWindowViewModel : BaseWindowViewModel
{ {
} }
} }

View File

@@ -11,8 +11,10 @@ using WinUIEx;
namespace BetterLyrics.WinUI3.ViewModels namespace BetterLyrics.WinUI3.ViewModels
{ {
public partial class SystemTrayViewModel(ISettingsService settingsService) : BaseViewModel(settingsService), IRecipient<PropertyChangedMessage<bool>> public partial class SystemTrayViewModel(ISettingsService settingsService) : BaseViewModel, IRecipient<PropertyChangedMessage<bool>>
{ {
private readonly ISettingsService _settingsService = settingsService;
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedRecipients] [NotifyPropertyChangedRecipients]
public partial bool IsLyricsWindowLocked { get; set; } = false; public partial bool IsLyricsWindowLocked { get; set; } = false;

View File

@@ -220,37 +220,29 @@
<!-- Lyrics & Translation --> <!-- Lyrics & Translation -->
<Button <Button
Click="TranslationButton_Click" Click="PlaybackSettingsShortcutButton_Click"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8BD;}" Glyph=&#xEA69;}"
Style="{StaticResource GhostButtonStyle}"> Style="{StaticResource GhostButtonStyle}">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" /> <ToolTip x:Uid="LyricsPagePlaybackSourceButtonToolTip" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
<Button.ContextFlyout> <Button.ContextFlyout>
<Flyout x:Name="TranslationFlyout" ShouldConstrainToRootBounds="False"> <Flyout
<StackPanel Spacing="{StaticResource SettingsCardSpacing}"> x:Name="PlaybackSettingsFlyout"
<controls:SettingsExpander Placement="Right"
x:Uid="LyricsPageTranslationEnabled" ShouldConstrainToRootBounds="False">
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, <Flyout.FlyoutPresenterStyle>
Glyph=&#xE774;}" <Style TargetType="FlyoutPresenter">
IsExpanded="True"> <Setter Property="MinWidth" Value="800" />
<ToggleSwitch IsOn="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}" /> <Setter Property="Padding" Value="0" />
<controls:SettingsExpander.Items> <Setter Property="CornerRadius" Value="12" />
<controls:SettingsCard x:Uid="LyricsPageTranslationOnly" IsEnabled="{x:Bind ViewModel.IsTranslationEnabled, Mode=OneWay}"> <Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTranslationOnly, Mode=TwoWay}" /> <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
</controls:SettingsCard> <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
</controls:SettingsExpander.Items> </Style>
</controls:SettingsExpander> </Flyout.FlyoutPresenterStyle>
<StackPanel Orientation="Horizontal" Spacing="6"> <uc:PlaybackSettingsControl MaxWidth="800" MaxHeight="500" />
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
</StackPanel>
</StackPanel>
</Flyout> </Flyout>
</Button.ContextFlyout> </Button.ContextFlyout>
</Button> </Button>
@@ -352,13 +344,6 @@
</Grid.ContextFlyout> </Grid.ContextFlyout>
</Grid> </Grid>
<TeachingTip
x:Name="WelcomeTeachingTip"
x:Uid="MainPageWelcomeTeachingTip"
Closed="WelcomeTeachingTip_Closed"
IsOpen="{x:Bind ViewModel.IsWelcomeTeachingTipOpen, Mode=OneWay}"
Target="{x:Bind RootGrid}" />
<uc:SystemTray /> <uc:SystemTray />
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang // 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums; using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService; using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels; using BetterLyrics.WinUI3.ViewModels;
@@ -28,27 +29,22 @@ namespace BetterLyrics.WinUI3.Views
DataContext = Ioc.Default.GetRequiredService<LyricsPageViewModel>(); DataContext = Ioc.Default.GetRequiredService<LyricsPageViewModel>();
} }
private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args)
{
ViewModel.IsFirstRun = false;
}
private void LyricsOnlyRadioButton_Click(object sender, RoutedEventArgs e) private void LyricsOnlyRadioButton_Click(object sender, RoutedEventArgs e)
{ {
ViewModel.DisplayType = LyricsDisplayType.LyricsOnly; ViewModel.DisplayType = LyricsDisplayType.LyricsOnly;
_settingsService.DisplayType = ViewModel.DisplayType; _settingsService.AppSettings.GeneralSettings.DisplayType = ViewModel.DisplayType;
} }
private void AlbumArtOnlyRadioButton_Click(object sender, RoutedEventArgs e) private void AlbumArtOnlyRadioButton_Click(object sender, RoutedEventArgs e)
{ {
ViewModel.DisplayType = LyricsDisplayType.AlbumArtOnly; ViewModel.DisplayType = LyricsDisplayType.AlbumArtOnly;
_settingsService.DisplayType = ViewModel.DisplayType; _settingsService.AppSettings.GeneralSettings.DisplayType = ViewModel.DisplayType;
} }
private void SplitViewRadioButton_Click(object sender, RoutedEventArgs e) private void SplitViewRadioButton_Click(object sender, RoutedEventArgs e)
{ {
ViewModel.DisplayType = LyricsDisplayType.SplitView; ViewModel.DisplayType = LyricsDisplayType.SplitView;
_settingsService.DisplayType = ViewModel.DisplayType; _settingsService.AppSettings.GeneralSettings.DisplayType = ViewModel.DisplayType;
} }
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
@@ -74,9 +70,9 @@ namespace BetterLyrics.WinUI3.Views
DisplayTypeSwitchFlyout.ShowAt(BottomRightCommandStackPanel); DisplayTypeSwitchFlyout.ShowAt(BottomRightCommandStackPanel);
} }
private void TranslationButton_Click(object sender, RoutedEventArgs e) private void PlaybackSettingsShortcutButton_Click(object sender, RoutedEventArgs e)
{ {
TranslationFlyout.ShowAt(BottomRightCommandStackPanel); PlaybackSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
} }
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -4,6 +4,7 @@ using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper; using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.SettingsService; using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Microsoft.UI.Windowing; using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Input;
@@ -46,7 +47,7 @@ namespace BetterLyrics.WinUI3.Views
public void UpdateTitleBarArea() public void UpdateTitleBarArea()
{ {
if (_settingsService.IsDragEverywhereEnabled) if (_settingsService.AppSettings.GeneralSettings.IsDragEverywhereEnabled)
{ {
SetTitleBar(RootGrid); SetTitleBar(RootGrid);
} }
@@ -69,15 +70,11 @@ namespace BetterLyrics.WinUI3.Views
public void AutoSelectLyricsMode(AutoStartWindowType? type = null, bool? autoLook = null) public void AutoSelectLyricsMode(AutoStartWindowType? type = null, bool? autoLook = null)
{ {
type ??= _settingsService.AutoStartWindowType; type ??= _settingsService.AppSettings.GeneralSettings.AutoStartWindowType;
switch (type!) switch (type!)
{ {
case AutoStartWindowType.StandardMode: case AutoStartWindowType.StandardMode:
AppWindow.MoveAndResize(new Windows.Graphics.RectInt32( AppWindow.MoveAndResize(_settingsService.AppSettings.StandardModeSettings.WindowBounds.ToRectInt32());
_settingsService.StandardWindowLeft,
_settingsService.StandardWindowTop,
_settingsService.StandardWindowWidth,
_settingsService.StandardWindowHeight));
break; break;
case AutoStartWindowType.DockMode: case AutoStartWindowType.DockMode:
DockFlyoutItem.IsChecked = true; DockFlyoutItem.IsChecked = true;
@@ -86,7 +83,7 @@ namespace BetterLyrics.WinUI3.Views
case AutoStartWindowType.DesktopMode: case AutoStartWindowType.DesktopMode:
DesktopFlyoutItem.IsChecked = true; DesktopFlyoutItem.IsChecked = true;
ViewModel.ToggleDesktopModeCommand.Execute(null); ViewModel.ToggleDesktopModeCommand.Execute(null);
if (autoLook == null && _settingsService.AutoLockOnDesktopMode) if (autoLook == null && _settingsService.AppSettings.DesktopModeSettings.AutoLockOnDesktopMode)
{ {
ViewModel.ToggleLockWindowCommand.Execute(null); ViewModel.ToggleLockWindowCommand.Execute(null);
} }
@@ -118,24 +115,21 @@ namespace BetterLyrics.WinUI3.Views
} }
else else
{ {
if (ViewModel.IsDesktopMode) App.DispatcherQueueTimer?.Debounce(() =>
{
_settingsService.DesktopWindowLeft = rect.X;
_settingsService.DesktopWindowTop = rect.Y;
_settingsService.DesktopWindowWidth = size.Width;
_settingsService.DesktopWindowHeight = 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 else
{ {
_settingsService.StandardWindowLeft = rect.X; _settingsService.AppSettings.StandardModeSettings.WindowBounds = new Windows.Foundation.Rect(rect.X, rect.Y, size.Width, size.Height);
_settingsService.StandardWindowTop = rect.Y; }
_settingsService.StandardWindowWidth = size.Width; }, Constants.Time.DebounceTimeout);
_settingsService.StandardWindowHeight = size.Height;
}
} }
} }
} }
@@ -189,6 +183,7 @@ namespace BetterLyrics.WinUI3.Views
ViewModel.IsImmersiveMode = true; ViewModel.IsImmersiveMode = true;
break; break;
case AppWindowPresenterKind.FullScreen: case AppWindowPresenterKind.FullScreen:
AOTFlyoutItem.Visibility = AOTFlyoutItem.Visibility =
ClickThroughButton.Visibility = ClickThroughButton.Visibility =
DesktopFlyoutItem.Visibility = DesktopFlyoutItem.Visibility =
@@ -196,6 +191,7 @@ namespace BetterLyrics.WinUI3.Views
DockFlyoutItem.Visibility = DockFlyoutItem.Visibility =
Visibility.Collapsed; Visibility.Collapsed;
FullScreenFlyoutItem.IsChecked = true; FullScreenFlyoutItem.IsChecked = true;
ViewModel.IsImmersiveMode = true;
break; break;
case AppWindowPresenterKind.Overlapped: case AppWindowPresenterKind.Overlapped:
DockFlyoutItem.Visibility = Visibility.Visible; DockFlyoutItem.Visibility = Visibility.Visible;
@@ -242,7 +238,7 @@ namespace BetterLyrics.WinUI3.Views
ClickThroughButton.Visibility = Visibility.Collapsed; ClickThroughButton.Visibility = Visibility.Collapsed;
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop; AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
ViewModel.IsImmersiveMode = _settingsService.IsImmersiveMode; ViewModel.IsImmersiveMode = _settingsService.AppSettings.GeneralSettings.IsImmersiveMode;
} }
break; break;
default: default:

Some files were not shown because too many files have changed in this diff Show More