mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 19:08:33 +08:00
Compare commits
22 Commits
v1.1.159.0
...
v1.1.166.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e6ba725d2 | ||
|
|
ffb0c58a58 | ||
|
|
3b61f568d0 | ||
|
|
b925a10d69 | ||
|
|
720f4b311e | ||
|
|
6c03002051 | ||
|
|
9cebd56bd5 | ||
|
|
f0a4c1251d | ||
|
|
a8418d4234 | ||
|
|
53abc4526c | ||
|
|
4d3b982904 | ||
|
|
5faace562d | ||
|
|
290b7f38b4 | ||
|
|
6f02a1a46c | ||
|
|
4f74e48cfb | ||
|
|
06f08558cc | ||
|
|
a2b21ed3d5 | ||
|
|
4cb1ca0bb3 | ||
|
|
81ed341e47 | ||
|
|
a9f92f4cb7 | ||
|
|
62719ed513 | ||
|
|
c9bd7725d0 |
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.1.159.0" />
|
||||
Version="1.1.166.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
|
||||
@@ -109,11 +109,11 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<LyricsWindowSettingsControlViewModel>()
|
||||
.AddSingleton<LyricsWindowSwitchControlViewModel>()
|
||||
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<NowPlayingWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
.AddSingleton<SystemTrayViewModel>()
|
||||
.AddSingleton<SettingsPageViewModel>()
|
||||
.AddSingleton<LyricsPageViewModel>()
|
||||
.AddSingleton<NowPlayingPageViewModel>()
|
||||
.AddSingleton<MusicGalleryViewModel>()
|
||||
.AddSingleton<AboutControlViewModel>()
|
||||
.BuildServiceProvider()
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Assets\Wiki82.profile.xml" />
|
||||
<None Remove="Controls\AboutControl.xaml" />
|
||||
<None Remove="Controls\AlbumArtLayoutSettingsControl.xaml" />
|
||||
<None Remove="Controls\AlbumArtAreaEffectSettingsControl.xaml" />
|
||||
<None Remove="Controls\AppSettingsControl.xaml" />
|
||||
<None Remove="Controls\DemoWindowGrid.xaml" />
|
||||
<None Remove="Controls\Dragger.xaml" />
|
||||
<None Remove="Controls\ExtendedSlider.xaml" />
|
||||
<None Remove="Controls\FontFamilyAutoSuggestBox.xaml" />
|
||||
<None Remove="Controls\ImageSwitcher.xaml" />
|
||||
<None Remove="Controls\LyricsSearchControl.xaml" />
|
||||
<None Remove="Controls\LyricsStyleSettingsControl.xaml" />
|
||||
<None Remove="Controls\LyricsWindowSettingsControl.xaml" />
|
||||
@@ -38,6 +38,7 @@
|
||||
<None Remove="Controls\PropertyRow.xaml" />
|
||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Controls\WindowSettingsControl.xaml" />
|
||||
<None Remove="Views\LyricsSearchWindow.xaml" />
|
||||
<None Remove="Views\LyricsWindowSwitchWindow.xaml" />
|
||||
<None Remove="Views\MusicGalleryPage.xaml" />
|
||||
@@ -58,7 +59,6 @@
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||
@@ -294,7 +294,7 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\AlbumArtLayoutSettingsControl.xaml">
|
||||
<Page Update="Controls\AlbumArtAreaStyleSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
@@ -337,7 +337,22 @@
|
||||
<Folder Include="TemplateSelector\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\Dragger.xaml">
|
||||
<Page Update="Controls\WindowSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\AlbumArtAreaEffectSettingsControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\ImageSwitcher.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\ShadowImage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
public const string Edge = "MSEdge";
|
||||
public const string BetterLyrics = "37412.BetterLyrics_rd1g0rsrrtxw8!App";
|
||||
public const string BetterLyricsDebug = "37412.BetterLyrics_c8mj3v9sysxb4!App";
|
||||
public const string SaltPlayerForWindows = "Sakawish.SaltPlayerforWindows_q65q631pyh094!SaltPlayerforWindows";
|
||||
public const string SaltPlayerForWindowsMS = "Sakawish.SaltPlayerforWindows_q65q631pyh094!SaltPlayerforWindows";
|
||||
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows.exe";
|
||||
public const string MoeKoeMusic = "cn.MoeKoe.Music";
|
||||
public const string MoeKoeMusicAlternative = "electron.app.MoeKoe Music";
|
||||
public const string Listen1 = "com.listen1.listen1";
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
public const string Edge = "Microsoft Edge";
|
||||
public const string BetterLyrics = "BetterLyrics";
|
||||
public const string BetterLyricsDebug = "BetterLyrics (Debug)";
|
||||
public const string SaltPlayerForWindows = "Salt Player for Windows";
|
||||
public const string SaltPlayerForWindowsMS = "Salt Player for Windows (Microsoft Store)";
|
||||
public const string SaltPlayerForWindowsSteam = "Salt Player for Windows (Steam)";
|
||||
public const string MoeKoeMusic = "MoeKoe Music";
|
||||
public const string Listen1 = "Listen 1";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaEffectSettingsControl"
|
||||
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:dev="using:DevWinUI"
|
||||
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="SettingsPageAlbumEffect" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageImageSwitchType" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind AlbumArtAreaEffectSettings.ImageSwitchType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageCrossfade" />
|
||||
<ComboBoxItem x:Uid="SettingsPageSlide" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,37 @@
|
||||
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.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 AlbumArtAreaEffectSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty AlbumArtAreaEffectSettingsProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettings), typeof(AlbumArtAreaEffectSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public AlbumArtAreaEffectSettings AlbumArtAreaEffectSettings
|
||||
{
|
||||
get => (AlbumArtAreaEffectSettings)GetValue(AlbumArtAreaEffectSettingsProperty);
|
||||
set => SetValue(AlbumArtAreaEffectSettingsProperty, value);
|
||||
}
|
||||
|
||||
public AlbumArtAreaEffectSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtLayoutSettingsControl"
|
||||
x:Class="BetterLyrics.WinUI3.Controls.AlbumArtAreaStyleSettingsControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
@@ -16,8 +16,9 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageAlbumStyle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<!-- 整体对齐 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlignment" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind AlbumArtLayoutSettings.SongInfoAlignmentType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLeft" />
|
||||
@@ -28,6 +29,27 @@
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAlbumArt" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<!-- 专辑高度 -->
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAlbumArtHeight"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
|
||||
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Maximum="512"
|
||||
Minimum="0"
|
||||
Unit="px"
|
||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 专辑圆角 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlbumRadius" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="12"
|
||||
@@ -37,6 +59,7 @@
|
||||
Value="{x:Bind AlbumArtLayoutSettings.CoverImageRadius, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 专辑阴影 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageAlbumShadowAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="12"
|
||||
@@ -7,18 +7,18 @@ using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class AlbumArtLayoutSettingsControl : UserControl
|
||||
public sealed partial class AlbumArtAreaStyleSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty AlbumArtLayoutSettingsProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettings), typeof(AlbumArtLayoutSettingsControl), new PropertyMetadata(default));
|
||||
DependencyProperty.Register(nameof(AlbumArtLayoutSettings), typeof(AlbumArtAreaStyleSettings), typeof(AlbumArtAreaStyleSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public AlbumArtLayoutSettings AlbumArtLayoutSettings
|
||||
public AlbumArtAreaStyleSettings AlbumArtLayoutSettings
|
||||
{
|
||||
get => (AlbumArtLayoutSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
||||
get => (AlbumArtAreaStyleSettings)GetValue(AlbumArtLayoutSettingsProperty);
|
||||
set => SetValue(AlbumArtLayoutSettingsProperty, value);
|
||||
}
|
||||
|
||||
public AlbumArtLayoutSettingsControl()
|
||||
public AlbumArtAreaStyleSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.Dragger"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid
|
||||
Background="Transparent"
|
||||
PointerCanceled="Grid_PointerReleased"
|
||||
PointerEntered="Grid_PointerEntered"
|
||||
PointerExited="Grid_PointerExited"
|
||||
PointerMoved="Grid_PointerMoved"
|
||||
PointerPressed="Grid_PointerPressed"
|
||||
PointerReleased="Grid_PointerReleased">
|
||||
|
||||
<Border x:Name="HitArea" Background="{ThemeResource SystemControlForegroundBaseMediumLowBrush}">
|
||||
<Grid
|
||||
x:Name="HandlePill"
|
||||
Background="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
CornerRadius="4" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,135 +0,0 @@
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public class DragDeltaEventArgs : EventArgs
|
||||
{
|
||||
public double HorizontalChange { get; }
|
||||
public double VerticalChange { get; }
|
||||
|
||||
public DragDeltaEventArgs(double hChange, double vChange)
|
||||
{
|
||||
HorizontalChange = hChange;
|
||||
VerticalChange = vChange;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class Dragger : UserControl
|
||||
{
|
||||
public event EventHandler DragStarted;
|
||||
public event EventHandler<DragDeltaEventArgs> DragDelta;
|
||||
public event EventHandler DragCompleted;
|
||||
|
||||
private bool _isDragging = false;
|
||||
private Point _lastPoint;
|
||||
|
||||
public static readonly DependencyProperty OrientationProperty =
|
||||
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(Dragger),
|
||||
new PropertyMetadata(Orientation.Vertical, OnOrientationChanged));
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => (Orientation)GetValue(OrientationProperty);
|
||||
set => SetValue(OrientationProperty, value);
|
||||
}
|
||||
|
||||
private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var control = (Dragger)d;
|
||||
control.UpdateVisuals();
|
||||
}
|
||||
|
||||
public Dragger()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.Loaded += (s, e) => UpdateVisuals();
|
||||
}
|
||||
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
if (Orientation == Orientation.Vertical)
|
||||
{
|
||||
this.ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast);
|
||||
|
||||
this.Width = 16;
|
||||
this.Height = double.NaN; // Auto
|
||||
|
||||
if (HitArea != null && HandlePill != null)
|
||||
{
|
||||
HitArea.Width = 16;
|
||||
HitArea.Height = double.NaN;
|
||||
|
||||
HandlePill.Width = 8;
|
||||
HandlePill.Height = 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth);
|
||||
|
||||
this.Height = 16;
|
||||
this.Width = double.NaN; // Auto
|
||||
|
||||
if (HitArea != null && HandlePill != null)
|
||||
{
|
||||
HitArea.Height = 16;
|
||||
HitArea.Width = double.NaN;
|
||||
|
||||
HandlePill.Height = 8;
|
||||
HandlePill.Width = 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void Grid_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void Grid_PointerPressed(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var element = sender as UIElement;
|
||||
if (element.CapturePointer(e.Pointer))
|
||||
{
|
||||
_isDragging = true;
|
||||
_lastPoint = e.GetCurrentPoint(this.XamlRoot.Content).Position;
|
||||
DragStarted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
var currentPoint = e.GetCurrentPoint(this.XamlRoot.Content).Position;
|
||||
double dx = currentPoint.X - _lastPoint.X;
|
||||
double dy = currentPoint.Y - _lastPoint.Y;
|
||||
|
||||
if (dx != 0 || dy != 0)
|
||||
{
|
||||
DragDelta?.Invoke(this, new DragDeltaEventArgs(dx, dy));
|
||||
_lastPoint = currentPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Grid_PointerReleased(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
var element = sender as UIElement;
|
||||
_isDragging = false;
|
||||
element.ReleasePointerCapture(e.Pointer);
|
||||
DragCompleted?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,25 +6,54 @@
|
||||
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:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock
|
||||
x:Name="SelectedLocalizedFontFamilyTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True" />
|
||||
<AutoSuggestBox
|
||||
x:Name="AutoSuggestBox"
|
||||
Width="150"
|
||||
MinWidth="180"
|
||||
GotFocus="AutoSuggestBox_GotFocus"
|
||||
LostFocus="AutoSuggestBox_LostFocus"
|
||||
SuggestionChosen="AutoSuggestBox_SuggestionChosen"
|
||||
Text="{x:Bind SelectedFontFamily, Mode=OneWay}"
|
||||
TextChanged="AutoSuggestBox_TextChanged" />
|
||||
TextChanged="AutoSuggestBox_TextChanged">
|
||||
<!--<AutoSuggestBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:ExtendedFontFamily">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{x:Bind LocalizedFontFamily}" TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{StaticResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind FontFamily}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</AutoSuggestBox.ItemTemplate>-->
|
||||
</AutoSuggestBox>
|
||||
<Button
|
||||
Click="Button_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="SettingsPageRefreshDropdown" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="SettingsPageCollapseDropdown" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -11,11 +18,15 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class FontFamilyAutoSuggestBox : UserControl
|
||||
{
|
||||
private List<string> SystemFontNames { get; set; } = [.. FontHelper.SystemFontFamilies];
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
//private List<ExtendedFontFamily> FontFamilies { get; set; } = [];
|
||||
private List<string> FontFamilies { get; set; } = [];
|
||||
|
||||
public FontFamilyAutoSuggestBox()
|
||||
{
|
||||
InitializeComponent();
|
||||
RefreshFontFamilies();
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectedFontFamilyProperty =
|
||||
@@ -27,20 +38,47 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
set => SetValue(SelectedFontFamilyProperty, value);
|
||||
}
|
||||
|
||||
private void RefreshFontFamilies()
|
||||
{
|
||||
//Task.Run(() =>
|
||||
//{
|
||||
// var fontFamilies = FontHelper.SystemFontFamilies.Select(x => new ExtendedFontFamily()
|
||||
// {
|
||||
// FontFamily = x,
|
||||
// LocalizedFontFamily = FontHelper.GetLocalizedFontFamilyName(x, _settingsService.AppSettings.GeneralSettings.LanguageCode)
|
||||
// }).OrderBy(x => x.LocalizedFontFamily).ToList();
|
||||
// DispatcherQueue.TryEnqueue(() =>
|
||||
// {
|
||||
// FontFamilies = fontFamilies;
|
||||
// });
|
||||
//});
|
||||
FontFamilies = FontHelper.SystemFontFamilies.OrderBy(x => x).ToList();
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
|
||||
{
|
||||
SelectedFontFamily = args.SelectedItem.ToString() ?? "";
|
||||
if (args.SelectedItem is ExtendedFontFamily extendedFontFamily)
|
||||
{
|
||||
SelectedFontFamily = extendedFontFamily.FontFamily;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedFontFamily = args.SelectedItem.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAutoSuggestBoxItemsSource()
|
||||
private void UpdateAutoSuggestBoxItemsSource(string? query = null)
|
||||
{
|
||||
query ??= AutoSuggestBox.Text;
|
||||
|
||||
//var suitableItems = new List<ExtendedFontFamily>();
|
||||
var suitableItems = new List<string>();
|
||||
var splitText = AutoSuggestBox.Text.ToLower().Split(" ");
|
||||
foreach (var fontFamily in SystemFontNames)
|
||||
var splitText = query.ToLower().Split(" ");
|
||||
foreach (var fontFamily in FontFamilies)
|
||||
{
|
||||
var found = splitText.All((key) =>
|
||||
bool found = splitText.All((key) =>
|
||||
{
|
||||
//return fontFamily.FontFamily.ToLower().Contains(key) || fontFamily.LocalizedFontFamily.ToLower().Contains(key);
|
||||
return fontFamily.ToLower().Contains(key);
|
||||
});
|
||||
if (found)
|
||||
@@ -50,9 +88,15 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
if (suitableItems.Count == 0)
|
||||
{
|
||||
//suitableItems.Add(new ExtendedFontFamily()
|
||||
//{
|
||||
// FontFamily = "",
|
||||
// LocalizedFontFamily = "N/A"
|
||||
//});
|
||||
suitableItems.Add("N/A");
|
||||
}
|
||||
AutoSuggestBox.ItemsSource = suitableItems.Order();
|
||||
//AutoSuggestBox.ItemsSource = suitableItems.OrderBy(x => x.LocalizedFontFamily);
|
||||
AutoSuggestBox.ItemsSource = suitableItems.OrderBy(x => x);
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
@@ -63,16 +107,18 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
UpdateAutoSuggestBoxItemsSource();
|
||||
}
|
||||
|
||||
SelectedLocalizedFontFamilyTextBlock.Text = FontHelper.GetLocalizedFontFamilyName(SelectedFontFamily, _settingsService.AppSettings.GeneralSettings.LanguageCode);
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SystemFontNames = [.. FontHelper.SystemFontFamilies];
|
||||
RefreshFontFamilies();
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UpdateAutoSuggestBoxItemsSource();
|
||||
UpdateAutoSuggestBoxItemsSource("");
|
||||
}
|
||||
|
||||
private void AutoSuggestBox_LostFocus(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.ImageSwitcher"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||
</Grid.OpacityTransition>
|
||||
<local:ShadowImage
|
||||
x:Name="LastAlbumArtImage"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||
<local:ShadowImage
|
||||
x:Name="AlbumArtImage"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadiusAmount="{x:Bind CornerRadiusAmount, Mode=OneWay}"
|
||||
ShadowAmount="{x:Bind ShadowAmount, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,145 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
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 System.Threading.Tasks;
|
||||
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 ImageSwitcher : UserControl
|
||||
{
|
||||
public int CornerRadiusAmount
|
||||
{
|
||||
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||
|
||||
public int ShadowAmount
|
||||
{
|
||||
get { return (int)GetValue(ShadowAmountProperty); }
|
||||
set { SetValue(ShadowAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShadowAmountProperty =
|
||||
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ImageSwitcher), new PropertyMetadata(0));
|
||||
|
||||
public ImageSource? Source
|
||||
{
|
||||
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty =
|
||||
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ImageSwitcher), new PropertyMetadata(null, OnDependencyPropertyChanged));
|
||||
|
||||
public ImageSwitchType SwitchType
|
||||
{
|
||||
get { return (ImageSwitchType)GetValue(SwitchTypeProperty); }
|
||||
set { SetValue(SwitchTypeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SwitchTypeProperty =
|
||||
DependencyProperty.Register(nameof(SwitchType), typeof(ImageSwitchType), typeof(ImageSwitcher), new PropertyMetadata(ImageSwitchType.Crossfade));
|
||||
|
||||
public ImageSwitcher()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void UpdateSource()
|
||||
{
|
||||
switch (SwitchType)
|
||||
{
|
||||
case ImageSwitchType.Crossfade:
|
||||
UpdateSourceCrossfade();
|
||||
break;
|
||||
case ImageSwitchType.Slide:
|
||||
UpdateSourceSlide();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSourceCrossfade()
|
||||
{
|
||||
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||
// ʹ<><CAB9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
LastAlbumArtImage.TranslationTransition = null;
|
||||
LastAlbumArtImage.OpacityTransition = null;
|
||||
LastAlbumArtImage.Translation = new();
|
||||
LastAlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
|
||||
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
AlbumArtImage.TranslationTransition = null;
|
||||
AlbumArtImage.OpacityTransition = null;
|
||||
AlbumArtImage.Translation = new();
|
||||
AlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||
AlbumArtImage.Source = Source;
|
||||
|
||||
// <20><><EFBFBD>浭<EFBFBD><E6B5AD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
LastAlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.Opacity = 1;
|
||||
}
|
||||
|
||||
private void UpdateSourceSlide()
|
||||
{
|
||||
// Ϊ<><CEAA><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC><EFBFBD>þ<EFBFBD>Դ
|
||||
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||
// ʹ<><CAB9><EFBFBD><EFBFBD>λ
|
||||
LastAlbumArtImage.TranslationTransition = null;
|
||||
LastAlbumArtImage.OpacityTransition = null;
|
||||
LastAlbumArtImage.Translation = new();
|
||||
LastAlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||
LastAlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
|
||||
// ʹǰ<CAB9><C7B0>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD>
|
||||
AlbumArtImage.TranslationTransition = null;
|
||||
AlbumArtImage.OpacityTransition = null;
|
||||
AlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||
AlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.TranslationTransition = new Vector3Transition { Duration = Constants.Time.AnimationDuration };
|
||||
AlbumArtImage.OpacityTransition = new ScalarTransition { Duration = Constants.Time.AnimationDuration };
|
||||
// ֮<><D6AE>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||||
AlbumArtImage.Source = Source;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>
|
||||
LastAlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.Opacity = 1;
|
||||
LastAlbumArtImage.Translation = new(-(float)ActualWidth, 0, 0);
|
||||
AlbumArtImage.Translation = new();
|
||||
}
|
||||
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ImageSwitcher imageSwitcher)
|
||||
{
|
||||
if (e.Property == SourceProperty)
|
||||
{
|
||||
imageSwitcher.UpdateSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public static class InstantTip
|
||||
{
|
||||
public static readonly DependencyProperty ContentProperty =
|
||||
DependencyProperty.RegisterAttached("Content", typeof(object), typeof(InstantTip), new PropertyMetadata(null, OnContentChanged));
|
||||
|
||||
public static void SetContent(DependencyObject element, object value) => element.SetValue(ContentProperty, value);
|
||||
public static object GetContent(DependencyObject element) => element.GetValue(ContentProperty);
|
||||
|
||||
private static Dictionary<int, Popup> _activePopups = [];
|
||||
|
||||
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is FrameworkElement element)
|
||||
{
|
||||
element.PointerEntered -= Element_PointerEntered;
|
||||
element.PointerExited -= Element_PointerExited;
|
||||
element.Unloaded -= Element_Unloaded;
|
||||
|
||||
if (e.NewValue != null)
|
||||
{
|
||||
element.PointerEntered += Element_PointerEntered;
|
||||
element.PointerExited += Element_PointerExited;
|
||||
element.Unloaded += Element_Unloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
int hashCode = element.GetHashCode();
|
||||
|
||||
if (_activePopups.ContainsKey(hashCode))
|
||||
{
|
||||
_activePopups.TryGetValue(hashCode, out var popup);
|
||||
if (popup != null)
|
||||
{
|
||||
popup.IsOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var rawContent = GetContent(element);
|
||||
|
||||
if (rawContent == null) return;
|
||||
|
||||
// 创建可视卡片容器 (Visual Card)
|
||||
var visualCard = new Grid
|
||||
{
|
||||
Background = (Brush)Application.Current.Resources["AcrylicBackgroundFillColorDefaultBrush"],
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Shadow = new ThemeShadow(),
|
||||
Translation = new Vector3(0, 0, 32),
|
||||
Padding = new Thickness(8, 4, 8, 4)
|
||||
};
|
||||
|
||||
var popupContent = new Grid
|
||||
{
|
||||
IsHitTestVisible = false,
|
||||
Opacity = 0,
|
||||
Padding = new Thickness(16)
|
||||
};
|
||||
popupContent.Children.Add(visualCard);
|
||||
|
||||
var popup = new Popup
|
||||
{
|
||||
Child = popupContent,
|
||||
IsHitTestVisible = false,
|
||||
ShouldConstrainToRootBounds = false,
|
||||
XamlRoot = element.XamlRoot,
|
||||
};
|
||||
|
||||
object finalContent = rawContent;
|
||||
|
||||
if (rawContent is ToolTip toolTipWrapper)
|
||||
{
|
||||
finalContent = toolTipWrapper.Content;
|
||||
}
|
||||
|
||||
if (finalContent is string text)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 12,
|
||||
MaxWidth = 320,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]
|
||||
};
|
||||
visualCard.Children.Add(textBlock);
|
||||
}
|
||||
else if (finalContent != null)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = finalContent.ToString(),
|
||||
FontSize = 12,
|
||||
MaxWidth = 320,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]
|
||||
};
|
||||
visualCard.Children.Add(textBlock);
|
||||
}
|
||||
|
||||
var transform = element.TransformToVisual(null);
|
||||
var point = transform.TransformPoint(new Point(0, element.ActualHeight));
|
||||
|
||||
popup.VerticalOffset = point.Y;
|
||||
popup.HorizontalOffset = point.X - popupContent.Padding.Left;
|
||||
|
||||
popup.IsOpen = true;
|
||||
|
||||
App.Current.Resources.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
popupContent.OpacityTransition = new ScalarTransition();
|
||||
popupContent.Opacity = 1;
|
||||
});
|
||||
|
||||
_activePopups.Add(hashCode, popup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var element = sender as FrameworkElement;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
int hashCode = element.GetHashCode();
|
||||
|
||||
if (!_activePopups.ContainsKey(hashCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activePopups.TryGetValue(hashCode, out var popup);
|
||||
if (popup != null)
|
||||
{
|
||||
if (popup.Child is Grid popupContent)
|
||||
{
|
||||
popupContent.Opacity = 0;
|
||||
}
|
||||
popup.IsOpen = false;
|
||||
_activePopups.Remove(hashCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.PointerEntered -= Element_PointerEntered;
|
||||
element.PointerExited -= Element_PointerExited;
|
||||
element.Unloaded -= Element_Unloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageBackgroundOverlay" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageTheme" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox x:Name="ThemeComboBox" SelectedIndex="{x:Bind LyricsBackgroundSettings.LyricsBackgroundTheme, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.NowPlayingCanvas"
|
||||
x:Class="BetterLyrics.WinUI3.Controls.LyricsCanvas"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Logic;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
@@ -25,7 +26,7 @@ using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class NowPlayingCanvas : UserControl,
|
||||
public sealed partial class LyricsCanvas : UserControl,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>,
|
||||
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
||||
@@ -34,7 +35,9 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
IRecipient<PropertyChangedMessage<double>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
|
||||
IRecipient<PropertyChangedMessage<string>>
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
@@ -49,7 +52,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
private readonly SpectrumRenderer _spectrumRenderer = new();
|
||||
|
||||
private readonly LyricsSynchronizer _synchronizer = new();
|
||||
private readonly LyricsLayoutManager _layoutManager = new();
|
||||
private readonly LyricsAnimator _animator = new();
|
||||
|
||||
private readonly SpectrumAnalyzer _spectrumAnalyzer = new();
|
||||
@@ -88,6 +90,11 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
durationSeconds: 0.3f,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
private readonly ValueTransition<double> _mouseYScrollTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
|
||||
private TimeSpan _songPosition; // 当前歌曲时刻
|
||||
private TimeSpan _totalPlayedTime; // 当前歌曲播放总时长(包括来来回回重复播放的时间)
|
||||
@@ -99,14 +106,25 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
private double _renderLyricsHeight = 0;
|
||||
private double _renderLyricsOpacity = 0;
|
||||
|
||||
private Point _mousePosition = new(0, 0);
|
||||
private int _mouseHoverLineIndex = -1;
|
||||
private bool _isMouseInLyricsArea = false;
|
||||
private bool _isMousePressing = false;
|
||||
private bool _isMouseScrolling = false;
|
||||
|
||||
private LyricsData? _lyricsData;
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
private bool _isMouseScrollingChanged = false;
|
||||
|
||||
private int _playingLineIndex;
|
||||
private (int Start, int End) _visibleRange;
|
||||
private double _canvasTargetScrollOffset;
|
||||
|
||||
public TimeSpan SongPosition => _songPosition;
|
||||
public double CurrentCanvasYScroll => _canvasYScrollTransition.Value;
|
||||
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines);
|
||||
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
|
||||
|
||||
// 歌词区域起始横 X 坐标
|
||||
public double LyricsStartX
|
||||
@@ -116,7 +134,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsStartXProperty =
|
||||
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
// 歌词区域起始 Y 坐标
|
||||
public double LyricsStartY
|
||||
@@ -126,7 +144,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsStartYProperty =
|
||||
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
// 歌词区域最大宽度
|
||||
public double LyricsWidth
|
||||
@@ -136,7 +154,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsWidthProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
// 歌词区域最大高度
|
||||
public double LyricsHeight
|
||||
@@ -146,7 +164,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsHeightProperty =
|
||||
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
// 歌词区域不透明度
|
||||
public double LyricsOpacity
|
||||
@@ -156,9 +174,60 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LyricsOpacityProperty =
|
||||
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
public NowPlayingCanvas()
|
||||
/// <summary>
|
||||
/// 用户操控鼠标已滚动的距离(从 0 开始算)
|
||||
/// </summary>
|
||||
public double MouseScrollOffset
|
||||
{
|
||||
get { return (double)GetValue(MouseScrollOffsetProperty); }
|
||||
set { SetValue(MouseScrollOffsetProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MouseScrollOffsetProperty =
|
||||
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 用户鼠标当前的位置(相对于歌词区域左上角)
|
||||
/// </summary>
|
||||
public Point MousePosition
|
||||
{
|
||||
get { return (Point)GetValue(MousePositionProperty); }
|
||||
set { SetValue(MousePositionProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MousePositionProperty =
|
||||
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnLayoutPropChanged));
|
||||
|
||||
public bool IsMouseInLyricsArea
|
||||
{
|
||||
get { return (bool)GetValue(IsMouseInLyricsAreaProperty); }
|
||||
set { SetValue(IsMouseInLyricsAreaProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMouseInLyricsAreaProperty =
|
||||
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
|
||||
public bool IsMousePressing
|
||||
{
|
||||
get { return (bool)GetValue(IsMousePressingProperty); }
|
||||
set { SetValue(IsMousePressingProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMousePressingProperty =
|
||||
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
|
||||
public bool IsMouseScrolling
|
||||
{
|
||||
get { return (bool)GetValue(IsMouseScrollingProperty); }
|
||||
set { SetValue(IsMouseScrollingProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsMouseScrollingProperty =
|
||||
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
|
||||
|
||||
public LyricsCanvas()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
@@ -171,34 +240,64 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<bool>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TextAlignmentType>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<SongInfo?>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsFontWeight>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this);
|
||||
}
|
||||
|
||||
private static void OnLayoutPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is NowPlayingCanvas canvas)
|
||||
if (d is LyricsCanvas canvas)
|
||||
{
|
||||
if (e.Property == LyricsStartXProperty)
|
||||
{
|
||||
canvas._renderLyricsStartX = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == LyricsStartYProperty)
|
||||
{
|
||||
canvas._renderLyricsStartY = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == LyricsWidthProperty)
|
||||
{
|
||||
canvas._renderLyricsWidth = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == LyricsHeightProperty)
|
||||
{
|
||||
canvas._renderLyricsHeight = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == LyricsOpacityProperty)
|
||||
{
|
||||
canvas._renderLyricsOpacity = Convert.ToDouble(e.NewValue);
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
else if (e.Property == MouseScrollOffsetProperty)
|
||||
{
|
||||
canvas._mouseYScrollTransition.StartTransition(Convert.ToDouble(e.NewValue));
|
||||
}
|
||||
else if (e.Property == MousePositionProperty)
|
||||
{
|
||||
canvas._mousePosition = (Point)e.NewValue;
|
||||
}
|
||||
else if (e.Property == IsMouseInLyricsAreaProperty)
|
||||
{
|
||||
canvas._isMouseInLyricsArea = (bool)e.NewValue;
|
||||
}
|
||||
else if (e.Property == IsMousePressingProperty)
|
||||
{
|
||||
canvas._isMousePressing = (bool)e.NewValue;
|
||||
}
|
||||
else if (e.Property == IsMouseScrollingProperty)
|
||||
{
|
||||
var value = (bool)e.NewValue;
|
||||
if (canvas._isMouseScrolling != value)
|
||||
{
|
||||
canvas._isMouseScrollingChanged = true;
|
||||
}
|
||||
canvas._isMouseScrolling = value;
|
||||
}
|
||||
|
||||
canvas._isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +313,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
var lyricsStyle = status.LyricsStyleSettings;
|
||||
var lyricsEffect = status.LyricsEffectSettings;
|
||||
|
||||
var lyricsData = _lyricsData;
|
||||
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
|
||||
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
|
||||
|
||||
@@ -259,25 +357,29 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
ds: args.DrawingSession,
|
||||
lyricsData: _lyricsData,
|
||||
playingLineIndex: _playingLineIndex,
|
||||
mouseHoverLineIndex: _mouseHoverLineIndex,
|
||||
isMousePressing: _isMousePressing,
|
||||
startVisibleIndex: _visibleRange.Start,
|
||||
endVisibleIndex: _visibleRange.End,
|
||||
lyricsX: _renderLyricsStartX,
|
||||
lyricsY: _renderLyricsStartY,
|
||||
lyricsWidth: _renderLyricsWidth,
|
||||
lyricsHeight: _renderLyricsHeight,
|
||||
userScrollOffset: _mouseYScrollTransition.Value,
|
||||
lyricsOpacity: _renderLyricsOpacity,
|
||||
playingLineTopOffsetFactor: lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||
windowStatus: status,
|
||||
strokeColor: lyricsThemeColors.StrokeFontColor,
|
||||
bgColor: lyricsThemeColors.BgFontColor,
|
||||
fgColor: lyricsThemeColors.FgFontColor,
|
||||
getPlaybackState: (lineIndex) =>
|
||||
{
|
||||
if (lyricsData == null) return new LinePlaybackState();
|
||||
if (_lyricsData == null) return new LinePlaybackState();
|
||||
|
||||
var line = lyricsData.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
var line = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
if (line == null) return new LinePlaybackState();
|
||||
|
||||
var nextLine = lyricsData.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
var nextLine = _lyricsData.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
|
||||
return _synchronizer.GetLinePlayingProgress(
|
||||
fixedSongPositionMs,
|
||||
@@ -307,15 +409,17 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
#if DEBUG
|
||||
args.DrawingSession.DrawText(
|
||||
$"[DEBUG]\n" +
|
||||
$"Lyrics start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||
$"Lyrics size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
|
||||
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
|
||||
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_lyricsData?.LyricsLines)}\n" +
|
||||
$"Playing line (idx): {_playingLineIndex}\n" +
|
||||
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
|
||||
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
|
||||
$"Total line count: {GetMaxLyricsLineIndexBoundaries().Item2 + 1}\n" +
|
||||
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines).End + 1}\n" +
|
||||
$"Played: {TimeSpan.FromMilliseconds(fixedSongPositionMs)} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
|
||||
$"Y offset: {_canvasYScrollTransition.Value}",
|
||||
new Vector2(10, 40), Colors.Red);
|
||||
$"Y offset: {_canvasYScrollTransition.Value}\n" +
|
||||
$"User scroll offset: {_mouseYScrollTransition.Value}",
|
||||
new Vector2(0, 0), Colors.Red);
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -323,6 +427,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args)
|
||||
{
|
||||
var lyricsBg = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings;
|
||||
var lyricsStyle = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
|
||||
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
|
||||
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
|
||||
|
||||
@@ -352,7 +457,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
if (isPlayingLineChanged || _isLayoutChanged)
|
||||
{
|
||||
var targetScroll = _layoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
|
||||
var targetScroll = LyricsLayoutManager.CalculateTargetScrollOffset(_lyricsData, _playingLineIndex);
|
||||
if (targetScroll.HasValue) _canvasTargetScrollOffset = targetScroll.Value;
|
||||
|
||||
_canvasYScrollTransition.SetEasingType(lyricsEffect.LyricsScrollEasingType);
|
||||
@@ -363,30 +468,50 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
#endregion
|
||||
|
||||
_visibleRange = _layoutManager.CalculateVisibleRange(
|
||||
_mouseYScrollTransition.Update(elapsedTime);
|
||||
|
||||
_mouseHoverLineIndex = LyricsLayoutManager.FindMouseHoverLineIndex(
|
||||
_lyricsData?.LyricsLines,
|
||||
_canvasYScrollTransition.Value, // µ±Ç°¹ö¶¯Î»ÖÃ
|
||||
_isMouseInLyricsArea,
|
||||
_mousePosition,
|
||||
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value,
|
||||
_renderLyricsStartY,
|
||||
_renderLyricsHeight,
|
||||
sender.Size.Height
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||
);
|
||||
|
||||
_animator.UpdateVisibleLines(
|
||||
_visibleRange = LyricsLayoutManager.CalculateVisibleRange(
|
||||
_lyricsData?.LyricsLines,
|
||||
_canvasYScrollTransition.Value + _mouseYScrollTransition.Value, // 当前滚动位置
|
||||
_renderLyricsStartY,
|
||||
_renderLyricsHeight,
|
||||
sender.Size.Height,
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0
|
||||
);
|
||||
|
||||
var maxRange = LyricsLayoutManager.CalculateMaxRange(_lyricsData?.LyricsLines);
|
||||
|
||||
_animator.UpdateLines(
|
||||
_lyricsData,
|
||||
_visibleRange.Start,
|
||||
_visibleRange.End,
|
||||
_isMouseScrolling ? maxRange.Start : _visibleRange.Start,
|
||||
_isMouseScrolling ? maxRange.End : _visibleRange.End,
|
||||
_playingLineIndex,
|
||||
sender.Size.Height,
|
||||
_canvasTargetScrollOffset,
|
||||
lyricsStyle.PlayingLineTopOffset / 100.0,
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings,
|
||||
_canvasYScrollTransition,
|
||||
albumArtThemeColors.BgFontColor,
|
||||
albumArtThemeColors.FgFontColor,
|
||||
elapsedTime,
|
||||
_isMouseScrolling,
|
||||
_isLayoutChanged,
|
||||
isPlayingLineChanged
|
||||
isPlayingLineChanged,
|
||||
_isMouseScrollingChanged
|
||||
);
|
||||
|
||||
_isMouseScrollingChanged = false;
|
||||
|
||||
_lyricsRenderer.CalculateLyrics3DMatrix(
|
||||
lyricsEffect: lyricsEffect,
|
||||
lyricsX: _renderLyricsStartX,
|
||||
@@ -466,9 +591,9 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private void TriggerRelayout()
|
||||
{
|
||||
if (_layoutManager == null || _lyricsData == null || !_isLayoutChanged) return;
|
||||
if (_lyricsData == null || !_isLayoutChanged) return;
|
||||
|
||||
_layoutManager.MeasureAndArrange(
|
||||
LyricsLayoutManager.MeasureAndArrange(
|
||||
resourceCreator: Canvas,
|
||||
lyricsData: _lyricsData,
|
||||
status: _liveStatesService.LiveStates.LyricsWindowStatus,
|
||||
@@ -512,18 +637,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
_isLastFMTracked = false;
|
||||
}
|
||||
|
||||
private Tuple<int, int> GetMaxLyricsLineIndexBoundaries()
|
||||
{
|
||||
if (_mediaSessionsService.CurrentSongInfo == null
|
||||
|| _lyricsData == null
|
||||
|| _lyricsData.LyricsLines.Count == 0)
|
||||
{
|
||||
return new Tuple<int, int>(-1, -1);
|
||||
}
|
||||
|
||||
return new Tuple<int, int>(0, _lyricsData.LyricsLines.Count - 1);
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<AlbumArtThemeColors> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
@@ -618,6 +731,10 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.PlayingLineTopOffset))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsEffectSettings)
|
||||
{
|
||||
@@ -667,6 +784,10 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsEffectSettings.IsLyricsBlurEffectEnabled))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsStyleSettings)
|
||||
{
|
||||
@@ -699,5 +820,30 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontWeight))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is LyricsStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCJKFontFamily))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsWesternFontFamily))
|
||||
{
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@
|
||||
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
|
||||
Text="Effect" />
|
||||
|
||||
<!-- 模糊效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsBlurEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsBlurEffectEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<!-- 辉光效果 -->
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
|
||||
@@ -29,6 +29,26 @@
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsCenterTopOffset" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
Default="50"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Unit="%"
|
||||
Value="{x:Bind LyricsStyleSettings.PlayingLineTopOffset, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
||||
Default="0.5"
|
||||
Frequency="0.1"
|
||||
Maximum="2"
|
||||
Minimum="0"
|
||||
Unit="x"
|
||||
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageCJK">
|
||||
@@ -207,17 +227,6 @@
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageLyricsLineSpacingFactor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<local:ExtendedSlider
|
||||
x:Uid="SettingsPageLyricsLineSpacingFactorSlider"
|
||||
Default="0.5"
|
||||
Frequency="0.1"
|
||||
Maximum="2"
|
||||
Minimum="0"
|
||||
Unit="x"
|
||||
Value="{x:Bind LyricsStyleSettings.LyricsLineSpacingFactor, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
<Pivot SelectionChanged="Pivot_SelectionChanged">
|
||||
|
||||
<PivotItem Tag="General">
|
||||
<PivotItem Tag="Window">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="AppSettingsControlGeneral"
|
||||
@@ -189,6 +189,15 @@
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="AlbumArtEffect">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
x:Uid="SettingsPageAlbumEffect"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}" />
|
||||
</PivotItem.Header>
|
||||
</PivotItem>
|
||||
|
||||
<PivotItem Tag="LyricsStyle">
|
||||
<PivotItem.Header>
|
||||
<TextBlock
|
||||
@@ -229,155 +238,8 @@
|
||||
</controls:SwitchPresenter.ContentTransitions>
|
||||
|
||||
<!-- Window -->
|
||||
<controls:Case Value="General">
|
||||
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel
|
||||
Margin="0,6,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<TextBox Text="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWorkArea"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<uc:ExtendedSlider
|
||||
Default="64"
|
||||
Maximum="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
||||
Minimum="64"
|
||||
Unit="px"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAdaptEnvColor"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard
|
||||
x:Uid="SettingsPageEnvColorSample"
|
||||
Header="Environment color sample mode"
|
||||
IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWindowBounds"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard Header="X">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowX, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard Header="Y">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowY, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWidth">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageHeight">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAOT"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<controls:Case Value="Window">
|
||||
<uc:WindowSettingsControl LyricsWindowStatus="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Layout -->
|
||||
@@ -386,7 +248,7 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<TextBlock x:Uid="SettingsPageLayout" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsExpander x:Uid="SettingsPageDisplayTypeSwitcher" IsExpanded="True">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
@@ -411,7 +273,12 @@
|
||||
|
||||
<!-- Album art area style -->
|
||||
<controls:Case Value="AlbumArtStyle">
|
||||
<uc:AlbumArtLayoutSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
||||
<uc:AlbumArtAreaStyleSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Album art area effect -->
|
||||
<controls:Case Value="AlbumArtEffect">
|
||||
<uc:AlbumArtAreaEffectSettingsControl AlbumArtAreaEffectSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtAreaEffectSettings, Mode=OneWay}" />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Lyrics style -->
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
StorageFile? file;
|
||||
if (this.Parent is FlyoutPresenter)
|
||||
{
|
||||
file = await PickerHelper.PickSaveFileAsync<LyricsWindow>(fileTypeChoices);
|
||||
file = await PickerHelper.PickSaveFileAsync<NowPlayingWindow>(fileTypeChoices);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -132,7 +132,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
StorageFile? file;
|
||||
if (this.Parent is FlyoutPresenter)
|
||||
{
|
||||
file = await PickerHelper.PickSingleFileAsync<LyricsWindow>(fileTypeFilter);
|
||||
file = await PickerHelper.PickSingleFileAsync<NowPlayingWindow>(fileTypeFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.ShadowImage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:const="using:BetterLyrics.WinUI3.Constants"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</UserControl.OpacityTransition>
|
||||
|
||||
<Grid Margin="-32" Padding="32">
|
||||
<Grid
|
||||
x:Name="ShadowCastGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
SizeChanged="ShadowCastGrid_SizeChanged">
|
||||
<Image Source="{x:Bind Source, Mode=OneWay}" Stretch="Uniform" />
|
||||
</Grid>
|
||||
<Border
|
||||
x:Name="ShadowRect"
|
||||
Loaded="ShadowRect_Loaded"
|
||||
SizeChanged="ShadowRect_SizeChanged"
|
||||
Translation="0,0,0">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow x:Name="Shadow" />
|
||||
</Border.Shadow>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,103 @@
|
||||
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 ShadowImage : UserControl
|
||||
{
|
||||
public int CornerRadiusAmount
|
||||
{
|
||||
get { return (int)GetValue(CornerRadiusAmountProperty); }
|
||||
set { SetValue(CornerRadiusAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusAmountProperty =
|
||||
DependencyProperty.Register(nameof(CornerRadiusAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||
|
||||
public int ShadowAmount
|
||||
{
|
||||
get { return (int)GetValue(ShadowAmountProperty); }
|
||||
set { SetValue(ShadowAmountProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ShadowAmountProperty =
|
||||
DependencyProperty.Register(nameof(ShadowAmount), typeof(int), typeof(ShadowImage), new PropertyMetadata(0, OnDependencyPropertyChanged));
|
||||
|
||||
public ImageSource? Source
|
||||
{
|
||||
get { return (ImageSource?)GetValue(SourceProperty); }
|
||||
set { SetValue(SourceProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SourceProperty =
|
||||
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ShadowImage), new PropertyMetadata(null));
|
||||
|
||||
public ShadowImage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ShadowImage shadowImage)
|
||||
{
|
||||
if (e.Property == CornerRadiusAmountProperty)
|
||||
{
|
||||
shadowImage.UpdateShadowCastGridCornerRadius();
|
||||
shadowImage.UpdateShadowRectCornerRadius();
|
||||
}
|
||||
else if (e.Property == ShadowAmountProperty)
|
||||
{
|
||||
shadowImage.UpdateShadowRectShadow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShadowRectShadow()
|
||||
{
|
||||
ShadowRect.Translation = new(0, 0, ShadowAmount);
|
||||
}
|
||||
|
||||
private void UpdateShadowCastGridCornerRadius()
|
||||
{
|
||||
var minSize = Math.Min(ShadowCastGrid.ActualHeight, ShadowCastGrid.ActualWidth);
|
||||
ShadowCastGrid.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
|
||||
}
|
||||
|
||||
private void UpdateShadowRectCornerRadius()
|
||||
{
|
||||
var minSize = Math.Min(ShadowRect.ActualHeight, ShadowRect.ActualWidth);
|
||||
ShadowRect.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
|
||||
}
|
||||
|
||||
private void ShadowCastGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateShadowCastGridCornerRadius();
|
||||
}
|
||||
|
||||
private void ShadowRect_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
UpdateShadowRectCornerRadius();
|
||||
}
|
||||
|
||||
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Shadow.Receivers.Add(ShadowCastGrid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,11 @@
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="SettingsPageClear" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button
|
||||
Margin="3,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
@@ -31,7 +35,11 @@
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="SettingsPageCheckShortcut" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.WindowSettingsControl"
|
||||
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:dev="using:DevWinUI"
|
||||
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="AppSettingsControlGeneral" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageConfigName" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel
|
||||
Margin="0,6,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<TextBox Text="{x:Bind LyricsWindowStatus.Name, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, FontSize=12, Glyph=}" Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWorkArea"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWorkAreaHeight" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Default="64"
|
||||
Maximum="{x:Bind LyricsWindowStatus.MonitorBounds.Height, Mode=OneWay}"
|
||||
Minimum="64"
|
||||
Unit="px"
|
||||
Value="{x:Bind LyricsWindowStatus.DockHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockPlacement" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDockMonitor" IsEnabled="{x:Bind LyricsWindowStatus.IsWorkArea, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<ComboBox ItemsSource="{x:Bind MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind LyricsWindowStatus.MonitorDeviceName, Mode=TwoWay}" />
|
||||
<Button
|
||||
Click="RefreshMonitorButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAdaptEnvColor"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard
|
||||
x:Uid="SettingsPageEnvColorSample"
|
||||
Header="Environment color sample mode"
|
||||
IsEnabled="{x:Bind LyricsWindowStatus.IsAdaptToEnvironment, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.EnvironmentSampleMode, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleBelow" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleAbove" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleInner" />
|
||||
<ComboBoxItem x:Uid="SettingsPageEnvColorSampleEdge" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageWindowBounds"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard Header="X">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind LyricsWindowStatus.WindowX, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard Header="Y">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind LyricsWindowStatus.WindowY, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageWidth">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageHeight">
|
||||
<NumberBox
|
||||
SmallChange="10"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsExpander
|
||||
x:Uid="SettingsPageAOT"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceAlwaysOnTop" IsEnabled="{x:Bind LyricsWindowStatus.IsAlwaysOnTop, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsAlwaysOnTopPolling, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.AutoShowOrHideWindow, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageShowInSwitchers" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaWhole" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,56 @@
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.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.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 WindowSettingsControl : UserControl
|
||||
{
|
||||
public static readonly DependencyProperty LyricsWindowStatusProperty =
|
||||
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(AlbumArtAreaEffectSettings), typeof(WindowSettingsControl), new PropertyMetadata(default));
|
||||
|
||||
public LyricsWindowStatus LyricsWindowStatus
|
||||
{
|
||||
get => (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty);
|
||||
set => SetValue(LyricsWindowStatusProperty, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<string> MonitorDeviceNames { get; set; } = [];
|
||||
|
||||
public WindowSettingsControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
}
|
||||
|
||||
private void RefreshMonitorDeviceNames()
|
||||
{
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
LyricsWindowStatus.MonitorDeviceName = MonitorDeviceNames.FirstOrDefault() ?? "";
|
||||
}
|
||||
|
||||
private void RefreshMonitorButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshMonitorDeviceNames();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum ImageSwitchType
|
||||
{
|
||||
Crossfade,
|
||||
Slide
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,9 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
extension(Point point)
|
||||
{
|
||||
public PointInt32 ToPointInt32() => new((int)point.X, (int)point.Y);
|
||||
|
||||
public Point AddX(double deltaX) => new(point.X + deltaX, point.Y);
|
||||
public Point AddY(double deltaY) => new(point.X, point.Y + deltaY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,14 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
if (track.Path is string path)
|
||||
{
|
||||
return TagLib.File.Create(path).Tag.Lyrics;
|
||||
try
|
||||
{
|
||||
return TagLib.File.Create(path).Tag.Lyrics;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,39 @@
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class FontHelper
|
||||
{
|
||||
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
|
||||
|
||||
public static string GetLocalizedFontFamilyName(string sourceName, string langCode)
|
||||
{
|
||||
if (langCode == "")
|
||||
{
|
||||
langCode = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
foreach (var font in Fonts.SystemFontFamilies)
|
||||
{
|
||||
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out string englishFamilyName) && englishFamilyName == sourceName)
|
||||
{
|
||||
if (font.FamilyNames.ContainsKey(XmlLanguage.GetLanguage(langCode)))
|
||||
{
|
||||
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage(langCode), out string localizedFamilyName))
|
||||
{
|
||||
return localizedFamilyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sourceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
PlayerID.Edge => PlayerName.Edge,
|
||||
PlayerID.BetterLyrics => PlayerName.BetterLyrics,
|
||||
PlayerID.BetterLyricsDebug => PlayerName.BetterLyricsDebug,
|
||||
PlayerID.SaltPlayerForWindows => PlayerName.SaltPlayerForWindows,
|
||||
PlayerID.SaltPlayerForWindowsMS => PlayerName.SaltPlayerForWindowsMS,
|
||||
PlayerID.SaltPlayerForWindowsSteam => PlayerName.SaltPlayerForWindowsSteam,
|
||||
PlayerID.MoeKoeMusic => PlayerName.MoeKoeMusic,
|
||||
PlayerID.MoeKoeMusicAlternative => PlayerName.MoeKoeMusic,
|
||||
PlayerID.Listen1 => PlayerName.Listen1,
|
||||
@@ -75,7 +76,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
PlayerID.Edge => PathHelper.EdgeLogoPath,
|
||||
PlayerID.BetterLyrics => PathHelper.LogoPath,
|
||||
PlayerID.BetterLyricsDebug => PathHelper.LogoPath,
|
||||
PlayerID.SaltPlayerForWindows => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||
PlayerID.SaltPlayerForWindowsMS => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||
PlayerID.SaltPlayerForWindowsSteam => PathHelper.SaltPlayerForWindowsLogoPath,
|
||||
PlayerID.MoeKoeMusic => PathHelper.MoeKoeMusicLogoPath,
|
||||
PlayerID.MoeKoeMusicAlternative => PathHelper.MoeKoeMusicLogoPath,
|
||||
PlayerID.Listen1 => PathHelper.Listen1LogoPath,
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
public static void CloseWindow<T>()
|
||||
{
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
EnsureDockModeReleased();
|
||||
}
|
||||
@@ -102,10 +102,10 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
if (window == null)
|
||||
{
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
window = new LyricsWindow();
|
||||
((LyricsWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
window = new NowPlayingWindow();
|
||||
((NowPlayingWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
}
|
||||
else if (typeof(T) == typeof(SettingsWindow))
|
||||
{
|
||||
@@ -133,7 +133,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
castedWindow.Restore();
|
||||
castedWindow.Activate();
|
||||
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
_liveStatesService.InitLyricsWindowStatus();
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
_defaultWindowStyle.Add(hwnd, castedWindow.GetWindowStyle());
|
||||
_defaultExtendedWindowStyle.Add(hwnd, castedWindow.GetExtendedWindowStyle());
|
||||
|
||||
var lyricsWindow = (LyricsWindow)window;
|
||||
var lyricsWindow = (NowPlayingWindow)window;
|
||||
lyricsWindow.ViewModel.InitShortcuts();
|
||||
lyricsWindow.ViewModel.InitFgWindowWatcher();
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
private static void EnsureDockModeReleased()
|
||||
{
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
SetIsWorkArea<NowPlayingWindow>(false);
|
||||
}
|
||||
|
||||
private static void TrackWindow(object window)
|
||||
@@ -301,9 +301,9 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
public static void SetTitleBarArea<T>(TitleBarArea titleBarArea)
|
||||
{
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
if (typeof(T) == typeof(NowPlayingWindow))
|
||||
{
|
||||
LyricsWindow? lyricsWindow = GetWindow<LyricsWindow>();
|
||||
NowPlayingWindow? lyricsWindow = GetWindow<NowPlayingWindow>();
|
||||
lyricsWindow?.SetTitleBarArea(titleBarArea);
|
||||
}
|
||||
else
|
||||
@@ -404,7 +404,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
|
||||
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
|
||||
{
|
||||
var window = GetWindow<LyricsWindow>();
|
||||
var window = GetWindow<NowPlayingWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
|
||||
@@ -412,23 +412,23 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
SetIsWorkArea<LyricsWindow>(false);
|
||||
SetIsWorkArea<NowPlayingWindow>(false);
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
}
|
||||
HideWindow<LyricsWindow>();
|
||||
HideWindow<NowPlayingWindow>();
|
||||
}
|
||||
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
|
||||
{
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
SetIsWorkArea<LyricsWindow>(true);
|
||||
SetIsWorkArea<NowPlayingWindow>(true);
|
||||
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
}
|
||||
OpenOrShowWindow<LyricsWindow>();
|
||||
OpenOrShowWindow<NowPlayingWindow>();
|
||||
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
MoveAndResize<LyricsWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
MoveAndResize<NowPlayingWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
}
|
||||
}, Constants.Time.DebounceTimeout);
|
||||
|
||||
@@ -12,38 +12,50 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
private readonly double _defaultScale = 0.75f;
|
||||
private readonly double _highlightedScale = 1.0f;
|
||||
|
||||
public void UpdateVisibleLines(
|
||||
public void UpdateLines(
|
||||
LyricsData? lyricsData,
|
||||
int startVisibleIndex,
|
||||
int endVisibleIndex,
|
||||
int startIndex,
|
||||
int endIndex,
|
||||
int playingLineIndex,
|
||||
double canvasHeight,
|
||||
double targetYScrollOffset,
|
||||
double playingLineTopOffsetFactor,
|
||||
LyricsEffectSettings lyricsEffect,
|
||||
ValueTransition<double> canvasYScrollTransition,
|
||||
Color bgColor,
|
||||
Color fgColor,
|
||||
TimeSpan elapsedTime,
|
||||
bool isMouseScrolling,
|
||||
bool isLayoutChanged,
|
||||
bool isPlayingLineChanged)
|
||||
bool isPlayingLineChanged,
|
||||
bool isMouseScrollingChanged
|
||||
)
|
||||
{
|
||||
if (lyricsData == null) return;
|
||||
|
||||
var currentPlayingLine = lyricsData.LyricsLines.ElementAtOrDefault(playingLineIndex);
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
// 循环更新可见行
|
||||
for (int i = startVisibleIndex; i <= endVisibleIndex + 1; i++)
|
||||
for (int i = startIndex; i <= endIndex + 1; i++)
|
||||
{
|
||||
var line = lyricsData.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
|
||||
if (isLayoutChanged || isPlayingLineChanged)
|
||||
if (isLayoutChanged || isPlayingLineChanged || isMouseScrollingChanged)
|
||||
{
|
||||
int lineCountDelta = i - playingLineIndex;
|
||||
int absLineCountDelta = Math.Abs(lineCountDelta);
|
||||
double distanceFromPlayingLine = Math.Abs(line.OriginalPosition.Y - currentPlayingLine.OriginalPosition.Y);
|
||||
double distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight / 2), 0, 1);
|
||||
|
||||
double distanceFactor = 0;
|
||||
if (lineCountDelta < 0)
|
||||
{
|
||||
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * playingLineTopOffsetFactor), 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceFactor = Math.Clamp(distanceFromPlayingLine / (canvasHeight * (1 - playingLineTopOffsetFactor)), 0, 1);
|
||||
}
|
||||
|
||||
double yScrollDuration;
|
||||
double yScrollDelay;
|
||||
@@ -70,19 +82,27 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
line.BlurAmountTransition.SetDuration(yScrollDuration);
|
||||
line.BlurAmountTransition.SetDelay(yScrollDelay);
|
||||
line.BlurAmountTransition.StartTransition(5 * distanceFactor);
|
||||
line.BlurAmountTransition.StartTransition(isMouseScrolling ? 0 : (lyricsEffect.IsLyricsBlurEffectEnabled ? (5 * distanceFactor) : 0));
|
||||
|
||||
line.ScaleTransition.SetDuration(yScrollDuration);
|
||||
line.ScaleTransition.SetDelay(yScrollDelay);
|
||||
line.ScaleTransition.StartTransition(_highlightedScale - distanceFactor * (_highlightedScale - _defaultScale));
|
||||
|
||||
line.UnplayingOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.UnplayingOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.UnplayingOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.3 : (1 - distanceFactor) * 0.3);
|
||||
line.PhoneticOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.PhoneticOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.PhoneticOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
|
||||
line.PlayingOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.PlayingOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.PlayingOpacityTransition.StartTransition(absLineCountDelta == 0 ? 1 : (1 - distanceFactor) * 0.3);
|
||||
line.PlayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.PlayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.PlayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 1 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
|
||||
line.UnplayedOriginalOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.UnplayedOriginalOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.UnplayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.3 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
|
||||
line.TranslatedOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.TranslatedOpacityTransition.SetDelay(yScrollDelay);
|
||||
line.TranslatedOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
|
||||
|
||||
line.ColorTransition.SetDuration(yScrollDuration);
|
||||
line.ColorTransition.SetDelay(yScrollDelay);
|
||||
@@ -105,8 +125,10 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
line.AngleTransition.Update(elapsedTime);
|
||||
line.ScaleTransition.Update(elapsedTime);
|
||||
line.BlurAmountTransition.Update(elapsedTime);
|
||||
line.PlayingOpacityTransition.Update(elapsedTime);
|
||||
line.UnplayingOpacityTransition.Update(elapsedTime);
|
||||
line.PhoneticOpacityTransition.Update(elapsedTime);
|
||||
line.PlayedOriginalOpacityTransition.Update(elapsedTime);
|
||||
line.UnplayedOriginalOpacityTransition.Update(elapsedTime);
|
||||
line.TranslatedOpacityTransition.Update(elapsedTime);
|
||||
line.YOffsetTransition.Update(elapsedTime);
|
||||
line.ColorTransition.Update(elapsedTime);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Logic
|
||||
{
|
||||
@@ -21,7 +23,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
/// <param name="canvasHeight"></param>
|
||||
/// <param name="lyricsWidth"></param>
|
||||
/// <param name="lyricsHeight"></param>
|
||||
public void MeasureAndArrange(
|
||||
public static void MeasureAndArrange(
|
||||
ICanvasAnimatedControl resourceCreator,
|
||||
LyricsData? lyricsData,
|
||||
LyricsWindowStatus status,
|
||||
@@ -56,6 +58,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
// 排版
|
||||
double currentY = 0;
|
||||
double actualWidth = 0;
|
||||
|
||||
foreach (var line in lyricsData.LyricsLines)
|
||||
{
|
||||
@@ -73,16 +76,17 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
line.RecreateTextGeometry();
|
||||
|
||||
// 顶部坐标
|
||||
line.TopPosition = new Vector2(0, (float)currentY);
|
||||
|
||||
// 左上角坐标
|
||||
line.TopLeftPosition = new Vector2(0, (float)currentY);
|
||||
// 注音层
|
||||
line.PhoneticPosition = line.TopPosition;
|
||||
line.PhoneticPosition = line.TopLeftPosition;
|
||||
if (line.PhoneticCanvasTextLayout != null)
|
||||
{
|
||||
currentY += line.PhoneticCanvasTextLayout.LayoutBounds.Height;
|
||||
// 间距
|
||||
currentY += (line.PhoneticCanvasTextLayout.LayoutBounds.Height / line.PhoneticCanvasTextLayout.LineCount) * 0.1;
|
||||
|
||||
actualWidth = Math.Max(actualWidth, line.PhoneticCanvasTextLayout.LayoutBounds.Width);
|
||||
}
|
||||
|
||||
// 原文层
|
||||
@@ -90,6 +94,8 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
if (line.OriginalCanvasTextLayout != null)
|
||||
{
|
||||
currentY += line.OriginalCanvasTextLayout.LayoutBounds.Height;
|
||||
|
||||
actualWidth = Math.Max(actualWidth, line.OriginalCanvasTextLayout.LayoutBounds.Width);
|
||||
}
|
||||
|
||||
// 翻译层
|
||||
@@ -102,10 +108,12 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
if (line.TranslatedCanvasTextLayout != null)
|
||||
{
|
||||
currentY += line.TranslatedCanvasTextLayout.LayoutBounds.Height;
|
||||
|
||||
actualWidth = Math.Max(actualWidth, line.TranslatedCanvasTextLayout.LayoutBounds.Width);
|
||||
}
|
||||
|
||||
// 底部坐标
|
||||
line.BottomPosition = new Vector2(0, (float)currentY);
|
||||
// 右下角坐标
|
||||
line.BottomRightPosition = new Vector2(0 + (float)actualWidth, (float)currentY);
|
||||
|
||||
// 行间距
|
||||
if (line.OriginalCanvasTextLayout != null)
|
||||
@@ -121,7 +129,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
/// <summary>
|
||||
/// 计算为了让当前歌词行的竖直几何中心点对齐到 0(原点),画布应该移动的距离(从画布最初始状态计算的值)
|
||||
/// </summary>
|
||||
public double? CalculateTargetScrollOffset(
|
||||
public static double? CalculateTargetScrollOffset(
|
||||
LyricsData? lyricsData,
|
||||
int playingLineIndex)
|
||||
{
|
||||
@@ -133,27 +141,26 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
|
||||
if (currentLine?.OriginalCanvasTextLayout == null || firstLine == null) return null;
|
||||
|
||||
return -currentLine.OriginalPosition.Y
|
||||
+ firstLine.OriginalPosition.Y
|
||||
- (currentLine.TranslatedPosition.Y
|
||||
+ (currentLine.TranslatedCanvasTextLayout?.LayoutBounds.Height ?? 0)
|
||||
- currentLine.PhoneticPosition.Y) / 2.0;
|
||||
return -currentLine.OriginalPosition.Y + firstLine.OriginalPosition.Y
|
||||
- (currentLine.BottomRightPosition.Y - currentLine.TopLeftPosition.Y) / 2.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前屏幕可见的行范围
|
||||
/// 返回值: (StartVisibleIndex, EndVisibleIndex)
|
||||
/// </summary>
|
||||
public (int Start, int End) CalculateVisibleRange(
|
||||
public static (int Start, int End) CalculateVisibleRange(
|
||||
IList<LyricsLine>? lines,
|
||||
double currentScrollOffset,
|
||||
double lyricsY,
|
||||
double lyricsHeight,
|
||||
double canvasHeight)
|
||||
double canvasHeight,
|
||||
double playingLineTopOffsetFactor
|
||||
)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight / 2;
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||
|
||||
int start = FindFirstVisibleLine(lines, offset, lyricsY);
|
||||
int end = FindLastVisibleLine(lines, offset, lyricsY, lyricsHeight, canvasHeight);
|
||||
@@ -167,7 +174,50 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
return (start, end);
|
||||
}
|
||||
|
||||
private int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
|
||||
public static (int Start, int End) CalculateMaxRange(IList<LyricsLine>? lines)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return (-1, -1);
|
||||
|
||||
return (0, lines.Count - 1);
|
||||
}
|
||||
|
||||
public static double CalculateActualHeight(IList<LyricsLine>? lines)
|
||||
{
|
||||
if (lines == null || lines.Count == 0) return 0;
|
||||
|
||||
return lines.Last().BottomRightPosition.Y;
|
||||
}
|
||||
|
||||
public static int FindMouseHoverLineIndex(
|
||||
IList<LyricsLine>? lines,
|
||||
bool isMouseInLyricsArea,
|
||||
Point mousePosition,
|
||||
double currentScrollOffset,
|
||||
double lyricsY,
|
||||
double lyricsHeight,
|
||||
double playingLineTopOffsetFactor
|
||||
)
|
||||
{
|
||||
if (!isMouseInLyricsArea) return -1;
|
||||
|
||||
if (lines == null || lines.Count == 0) return -1;
|
||||
|
||||
double offset = currentScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
{
|
||||
int mid = (left + right) / 2;
|
||||
var line = lines[mid];
|
||||
if (line.OriginalCanvasTextLayout == null) break;
|
||||
double value = offset + line.BottomRightPosition.Y;
|
||||
if (value >= mousePosition.Y) { result = mid; right = mid - 1; }
|
||||
else { left = mid + 1; }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int FindFirstVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
@@ -175,7 +225,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
int mid = (left + right) / 2;
|
||||
var line = lines[mid];
|
||||
if (line.OriginalCanvasTextLayout == null) break;
|
||||
double value = offset + line.BottomPosition.Y;
|
||||
double value = offset + line.BottomRightPosition.Y;
|
||||
// 理论上说应该使用下面这一行来精确计算视野内的首个可见行,但是考虑到动画视觉效果,还是注释掉了
|
||||
//if (value >= lyricsY) { result = mid; right = mid - 1; }
|
||||
if (value >= 0) { result = mid; right = mid - 1; }
|
||||
@@ -184,7 +234,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
return result;
|
||||
}
|
||||
|
||||
private int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
|
||||
private static int FindLastVisibleLine(IList<LyricsLine> lines, double offset, double lyricsY, double lyricsHeight, double canvasHeight)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
@@ -192,7 +242,7 @@ namespace BetterLyrics.WinUI3.Logic
|
||||
int mid = (left + right) / 2;
|
||||
var line = lines[mid];
|
||||
if (line.OriginalCanvasTextLayout == null) break;
|
||||
double value = offset + line.BottomPosition.Y;
|
||||
double value = offset + line.BottomRightPosition.Y;
|
||||
// 同理
|
||||
//if (value >= lyricsY + lyricsHeight) { result = mid; right = mid - 1; }
|
||||
if (value >= canvasHeight) { result = mid; right = mid - 1; }
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class ExtendedFontFamily
|
||||
{
|
||||
public string FontFamily { get; set; } = "";
|
||||
public string LocalizedFontFamily { get; set; } = "";
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,10 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public double AnimationDuration { get; set; } = 0.3;
|
||||
public ValueTransition<double> AngleTransition { get; set; }
|
||||
public ValueTransition<double> BlurAmountTransition { get; set; }
|
||||
public ValueTransition<double> UnplayingOpacityTransition { get; set; }
|
||||
public ValueTransition<double> PlayingOpacityTransition { get; set; }
|
||||
public ValueTransition<double> PhoneticOpacityTransition { get; set; }
|
||||
public ValueTransition<double> PlayedOriginalOpacityTransition { get; set; }
|
||||
public ValueTransition<double> UnplayedOriginalOpacityTransition { get; set; }
|
||||
public ValueTransition<double> TranslatedOpacityTransition { get; set; }
|
||||
public ValueTransition<double> ScaleTransition { get; set; }
|
||||
public ValueTransition<double> YOffsetTransition { get; set; }
|
||||
public ValueTransition<Color> ColorTransition { get; set; }
|
||||
@@ -44,7 +46,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
/// <summary>
|
||||
/// 顶部坐标(相对于坐标原点)
|
||||
/// </summary>
|
||||
public Vector2 TopPosition { get; set; }
|
||||
public Vector2 TopLeftPosition { get; set; }
|
||||
/// <summary>
|
||||
/// 中心坐标(相对于坐标原点)
|
||||
/// </summary>
|
||||
@@ -52,7 +54,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
/// <summary>
|
||||
/// 底部坐标(相对于坐标原点)
|
||||
/// </summary>
|
||||
public Vector2 BottomPosition { get; set; }
|
||||
public Vector2 BottomRightPosition { get; set; }
|
||||
|
||||
public List<LyricsSyllable> LyricsSyllables { get; set; } = [];
|
||||
|
||||
@@ -89,12 +91,22 @@ namespace BetterLyrics.WinUI3.Models
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
UnplayingOpacityTransition = new(
|
||||
PhoneticOpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
PlayingOpacityTransition = new(
|
||||
PlayedOriginalOpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
UnplayedOriginalOpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
);
|
||||
TranslatedOpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutSine
|
||||
@@ -123,7 +135,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
return;
|
||||
}
|
||||
|
||||
double centerY = (TopPosition.Y + BottomPosition.Y) / 2;
|
||||
double centerY = (TopLeftPosition.Y + BottomRightPosition.Y) / 2;
|
||||
|
||||
CenterPosition = type switch
|
||||
{
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty] public partial LyricsStyleSettings LyricsStyleSettings { get; set; } = new();
|
||||
[ObservableProperty] public partial LyricsEffectSettings LyricsEffectSettings { get; set; } = new(500, 500, 500, EasingType.EaseInOutQuad);
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsBackgroundSettings LyricsBackgroundSettings { get; set; } = new();
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtLayoutSettings AlbumArtLayoutSettings { get; set; } = new();
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtAreaStyleSettings AlbumArtLayoutSettings { get; set; } = new();
|
||||
[ObservableProperty] public partial AlbumArtAreaEffectSettings AlbumArtAreaEffectSettings { get; set; } = new();
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAdaptToEnvironment { get; set; } = false;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial WindowPixelSampleMode EnvironmentSampleMode { get; set; } = WindowPixelSampleMode.WindowEdge;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoShowOrHideWindow { get; set; } = false;
|
||||
@@ -65,7 +66,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
newValue.PropertyChanged += OldLyricsBackgroundSettings_PropertyChanged;
|
||||
}
|
||||
|
||||
partial void OnAlbumArtLayoutSettingsChanged(AlbumArtLayoutSettings oldValue, AlbumArtLayoutSettings newValue)
|
||||
partial void OnAlbumArtLayoutSettingsChanged(AlbumArtAreaStyleSettings oldValue, AlbumArtAreaStyleSettings newValue)
|
||||
{
|
||||
oldValue.PropertyChanged -= OldAlbumArtLayoutSettings_PropertyChanged;
|
||||
newValue.PropertyChanged += OldAlbumArtLayoutSettings_PropertyChanged;
|
||||
@@ -103,7 +104,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
public void UpdateMonitorNameAndBounds()
|
||||
{
|
||||
var lyricsWindow = WindowHook.GetWindow<LyricsWindow>();
|
||||
var lyricsWindow = WindowHook.GetWindow<NowPlayingWindow>();
|
||||
if (lyricsWindow == null) return;
|
||||
|
||||
var mointor = MonitorHook.GetMonitorInfoExFromWindow(lyricsWindow);
|
||||
@@ -184,10 +185,13 @@ namespace BetterLyrics.WinUI3.Models
|
||||
MonitorBounds = this.MonitorBounds,
|
||||
DemoMonitorBounds = this.DemoMonitorBounds,
|
||||
DockPlacement = this.DockPlacement,
|
||||
|
||||
LyricsStyleSettings = (LyricsStyleSettings)this.LyricsStyleSettings.Clone(),
|
||||
LyricsEffectSettings = (LyricsEffectSettings)this.LyricsEffectSettings.Clone(),
|
||||
LyricsBackgroundSettings = (LyricsBackgroundSettings)this.LyricsBackgroundSettings.Clone(),
|
||||
AlbumArtLayoutSettings = (AlbumArtLayoutSettings)this.AlbumArtLayoutSettings.Clone(),
|
||||
AlbumArtLayoutSettings = (AlbumArtAreaStyleSettings)this.AlbumArtLayoutSettings.Clone(),
|
||||
AlbumArtAreaEffectSettings = (AlbumArtAreaEffectSettings)this.AlbumArtAreaEffectSettings.Clone(),
|
||||
|
||||
IsAdaptToEnvironment = this.IsAdaptToEnvironment,
|
||||
EnvironmentSampleMode = this.EnvironmentSampleMode,
|
||||
AutoShowOrHideWindow = this.AutoShowOrHideWindow,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
public partial class AlbumArtAreaEffectSettings : ObservableRecipient, ICloneable
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ImageSwitchType ImageSwitchType { get; set; } = ImageSwitchType.Slide;
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new AlbumArtAreaEffectSettings()
|
||||
{
|
||||
ImageSwitchType = this.ImageSwitchType,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,12 @@ using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
public partial class AlbumArtLayoutSettings : ObservableRecipient, ICloneable
|
||||
public partial class AlbumArtAreaStyleSettings : ObservableRecipient, ICloneable
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType SongInfoAlignmentType { get; set; } = TextAlignmentType.Left;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAutoCoverImageHeight { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageHeight { get; set; } = 128;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageRadius { get; set; } = 12; // 12 % of the cover image size
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int CoverImageShadowAmount { get; set; } = 12;
|
||||
|
||||
@@ -17,15 +19,19 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowArtists { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowAlbum { get; set; } = false;
|
||||
|
||||
public AlbumArtLayoutSettings() { }
|
||||
public AlbumArtAreaStyleSettings() { }
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new AlbumArtLayoutSettings
|
||||
return new AlbumArtAreaStyleSettings
|
||||
{
|
||||
SongInfoAlignmentType = this.SongInfoAlignmentType,
|
||||
|
||||
IsAutoCoverImageHeight = this.IsAutoCoverImageHeight,
|
||||
CoverImageHeight = this.CoverImageHeight,
|
||||
CoverImageRadius = this.CoverImageRadius,
|
||||
CoverImageShadowAmount = this.CoverImageShadowAmount,
|
||||
|
||||
IsAutoSongInfoFontSize = this.IsAutoSongInfoFontSize,
|
||||
SongInfoFontSize = this.SongInfoFontSize,
|
||||
ShowTitle = this.ShowTitle,
|
||||
@@ -6,6 +6,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
public partial class LyricsEffectSettings : ObservableRecipient, ICloneable
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsBlurEffectEnabled { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectEnabled { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsScaleEffectEnabled { get; set; } = true;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
|
||||
@@ -38,6 +39,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
{
|
||||
return new LyricsEffectSettings(this.LyricsScrollTopDuration, this.LyricsScrollDuration, this.LyricsScrollBottomDuration, this.LyricsScrollEasingType)
|
||||
{
|
||||
IsLyricsBlurEffectEnabled = this.IsLyricsBlurEffectEnabled,
|
||||
IsLyricsGlowEffectEnabled = this.IsLyricsGlowEffectEnabled,
|
||||
IsLyricsScaleEffectEnabled = this.IsLyricsScaleEffectEnabled,
|
||||
IsLyricsFloatAnimationEnabled = this.IsLyricsFloatAnimationEnabled,
|
||||
|
||||
@@ -14,19 +14,27 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsFontSize { get; set; } = 12;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsFontSize { get; set; } = 24;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsFontSize { get; set; } = 12;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; } = TextAlignmentType.Left;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; } = 0;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomBgFontColor { get; set; } = Colors.White;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomFgFontColor { get; set; } = Colors.White;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomStrokeFontColor { get; set; } = Colors.White;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsBgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsFgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontWeight LyricsFontWeight { get; set; } = LyricsFontWeight.Bold;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayingLineTopOffset { get; set; } = 50; // 50 %
|
||||
|
||||
public LyricsStyleSettings() { }
|
||||
|
||||
public object Clone()
|
||||
@@ -49,6 +57,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
|
||||
LyricsLineSpacingFactor = this.LyricsLineSpacingFactor,
|
||||
LyricsCJKFontFamily = this.LyricsCJKFontFamily,
|
||||
LyricsWesternFontFamily = this.LyricsWesternFontFamily,
|
||||
|
||||
PlayingLineTopOffset = this.PlayingLineTopOffset,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,17 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
CanvasDrawingSession ds,
|
||||
LyricsData? lyricsData,
|
||||
int playingLineIndex,
|
||||
int mouseHoverLineIndex,
|
||||
bool isMousePressing,
|
||||
int startVisibleIndex,
|
||||
int endVisibleIndex,
|
||||
double lyricsX,
|
||||
double lyricsY,
|
||||
double lyricsWidth,
|
||||
double lyricsHeight,
|
||||
double userScrollOffset,
|
||||
double lyricsOpacity,
|
||||
double playingLineTopOffsetFactor,
|
||||
LyricsWindowStatus windowStatus,
|
||||
Color strokeColor,
|
||||
Color bgColor,
|
||||
@@ -51,12 +55,16 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
layerDs,
|
||||
lyricsData,
|
||||
playingLineIndex,
|
||||
mouseHoverLineIndex,
|
||||
isMousePressing,
|
||||
startVisibleIndex,
|
||||
endVisibleIndex,
|
||||
lyricsX,
|
||||
lyricsY,
|
||||
lyricsWidth,
|
||||
lyricsHeight,
|
||||
userScrollOffset,
|
||||
playingLineTopOffsetFactor,
|
||||
windowStatus,
|
||||
strokeColor,
|
||||
bgColor,
|
||||
@@ -78,12 +86,16 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
ds,
|
||||
lyricsData,
|
||||
playingLineIndex,
|
||||
mouseHoverLineIndex,
|
||||
isMousePressing,
|
||||
startVisibleIndex,
|
||||
endVisibleIndex,
|
||||
lyricsX,
|
||||
lyricsY,
|
||||
lyricsWidth,
|
||||
lyricsHeight,
|
||||
userScrollOffset,
|
||||
playingLineTopOffsetFactor,
|
||||
windowStatus,
|
||||
strokeColor,
|
||||
bgColor,
|
||||
@@ -98,12 +110,16 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
CanvasDrawingSession ds,
|
||||
LyricsData? lyricsData,
|
||||
int playingLineIndex,
|
||||
int mouseHoverLineIndex,
|
||||
bool isMousePressing,
|
||||
int startVisibleIndex,
|
||||
int endVisibleIndex,
|
||||
double lyricsX,
|
||||
double lyricsY,
|
||||
double lyricsWidth,
|
||||
double lyricsHeight,
|
||||
double userScrollOffset,
|
||||
double playingLineTopOffsetFactor,
|
||||
LyricsWindowStatus windowStatus,
|
||||
Color strokeColor,
|
||||
Color bgColor,
|
||||
@@ -128,7 +144,7 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
if (line.OriginalCanvasTextLayout == null) continue;
|
||||
if (line.OriginalCanvasTextLayout.LayoutBounds.Width <= 0) continue;
|
||||
|
||||
double yOffset = line.YOffsetTransition.Value + lyricsY + lyricsHeight / 2;
|
||||
double yOffset = line.YOffsetTransition.Value + userScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
|
||||
|
||||
var transform =
|
||||
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition) *
|
||||
@@ -149,6 +165,15 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
{
|
||||
_unplayingRenderer.Draw(ds, textOnlyLayer, line);
|
||||
}
|
||||
|
||||
if (i == mouseHoverLineIndex)
|
||||
{
|
||||
byte opacity = isMousePressing ? (byte)32 : (byte)16;
|
||||
double scale = isMousePressing ? 1.09 : 1.10;
|
||||
ds.FillRoundedRectangle(
|
||||
new Windows.Foundation.Rect(line.TopLeftPosition.ToPoint(), line.BottomRightPosition.ToPoint()).Scale(scale),
|
||||
8, 8, Color.FromArgb(opacity, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
{
|
||||
if (line.PhoneticCanvasTextLayout == null) return;
|
||||
|
||||
var opacity = line.UnplayingOpacityTransition.Value;
|
||||
var opacity = line.PhoneticOpacityTransition.Value;
|
||||
var blur = line.BlurAmountTransition.Value;
|
||||
var bounds = line.PhoneticCanvasTextLayout.LayoutBounds;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
{
|
||||
if (line.TranslatedCanvasTextLayout == null) return;
|
||||
|
||||
var opacity = line.UnplayingOpacityTransition.Value;
|
||||
var opacity = line.TranslatedOpacityTransition.Value;
|
||||
var blur = line.BlurAmountTransition.Value;
|
||||
var bounds = line.TranslatedCanvasTextLayout.LayoutBounds;
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
LyricsEffectSettings settings)
|
||||
{
|
||||
var blur = line.BlurAmountTransition.Value;
|
||||
var playingOpacity = line.PlayingOpacityTransition.Value;
|
||||
var unplayingOpacity = line.UnplayingOpacityTransition.Value;
|
||||
var playedOpacity = line.PlayedOriginalOpacityTransition.Value;
|
||||
var unplayedOpacity = line.UnplayedOriginalOpacityTransition.Value;
|
||||
|
||||
var subLineLayoutBounds = subLineRegion.LayoutBounds;
|
||||
Rect subLineRect = new(
|
||||
@@ -150,8 +150,8 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
float progressInRegion = (float)((curCharIndex - subLineRegion.CharacterIndex) / subLineRegion.CharacterCount);
|
||||
progressInRegion = Math.Clamp(progressInRegion, 0, 1 + fadeWidth);
|
||||
|
||||
var stop1 = fgColor.WithAlpha((byte)(255 * playingOpacity));
|
||||
var stop2 = bgColor.WithAlpha((byte)(255 * unplayingOpacity));
|
||||
var stop1 = fgColor.WithAlpha((byte)(255 * playedOpacity));
|
||||
var stop2 = bgColor.WithAlpha((byte)(255 * unplayedOpacity));
|
||||
|
||||
using (var gradientBrush = new CanvasLinearGradientBrush(resourceCreator,
|
||||
[
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
|
||||
if (line.PhoneticCanvasTextLayout != null)
|
||||
{
|
||||
var opacity = line.UnplayingOpacityTransition.Value;
|
||||
var opacity = line.PhoneticOpacityTransition.Value;
|
||||
DrawPart(ds, textOnlyLayer,
|
||||
line.PhoneticCanvasTextLayout,
|
||||
line.PhoneticPosition,
|
||||
@@ -29,13 +29,13 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
if (line.OriginalCanvasTextLayout != null)
|
||||
{
|
||||
double opacity;
|
||||
if (line.PlayingOpacityTransition.StartValue > line.UnplayingOpacityTransition.StartValue)
|
||||
if (line.PlayedOriginalOpacityTransition.StartValue > line.UnplayedOriginalOpacityTransition.StartValue)
|
||||
{
|
||||
opacity = line.PlayingOpacityTransition.Value;
|
||||
opacity = line.PlayedOriginalOpacityTransition.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
opacity = line.UnplayingOpacityTransition.Value;
|
||||
opacity = line.UnplayedOriginalOpacityTransition.Value;
|
||||
}
|
||||
DrawPart(ds, textOnlyLayer,
|
||||
line.OriginalCanvasTextLayout,
|
||||
@@ -46,7 +46,7 @@ namespace BetterLyrics.WinUI3.Renderer
|
||||
|
||||
if (line.TranslatedCanvasTextLayout != null)
|
||||
{
|
||||
var opacity = line.UnplayingOpacityTransition.Value;
|
||||
var opacity = line.TranslatedOpacityTransition.Value;
|
||||
DrawPart(ds, textOnlyLayer,
|
||||
line.TranslatedCanvasTextLayout,
|
||||
line.TranslatedPosition,
|
||||
|
||||
@@ -45,21 +45,21 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
|
||||
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
|
||||
|
||||
WindowHook.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
|
||||
WindowHook.SetIsWorkArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
WindowHook.UpdateWorkArea<LyricsWindow>();
|
||||
WindowHook.UpdateWorkArea<NowPlayingWindow>();
|
||||
}
|
||||
await Task.Delay(300);
|
||||
|
||||
WindowHook.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
|
||||
WindowHook.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
WindowHook.SetIsShowInSwitchers<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
|
||||
WindowHook.SetIsAlwaysOnTop<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
|
||||
WindowHook.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
WindowHook.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
WindowHook.SetIsClickThrough<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
WindowHook.SetIsBorderless<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
|
||||
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
WindowHook.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
WindowHook.SetTitleBarArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
|
||||
// 下述代码可以删除,但是为了避免给用户造成操作上的疑虑,暂时保留
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
@@ -67,7 +67,7 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
LiveStates.LyricsWindowStatus.WindowBounds = LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea();
|
||||
}
|
||||
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds);
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds);
|
||||
LiveStates.LyricsWindowStatus.WindowX = LiveStates.LyricsWindowStatus.WindowBounds.X;
|
||||
LiveStates.LyricsWindowStatus.WindowY = LiveStates.LyricsWindowStatus.WindowBounds.Y;
|
||||
LiveStates.LyricsWindowStatus.WindowWidth = LiveStates.LyricsWindowStatus.WindowBounds.Width;
|
||||
@@ -84,11 +84,11 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
{
|
||||
case nameof(LyricsWindowStatus.IsWorkArea):
|
||||
LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
WindowHook.SetIsWorkArea<LyricsWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
|
||||
WindowHook.SetIsWorkArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
|
||||
LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.DockHeight):
|
||||
@@ -98,37 +98,37 @@ namespace BetterLyrics.WinUI3.Services.LiveStatesService
|
||||
if (LiveStates.LyricsWindowStatus.IsWorkArea)
|
||||
{
|
||||
LiveStates.IsLyricsWindowStatusRefreshing = true;
|
||||
WindowHook.UpdateWorkArea<LyricsWindow>();
|
||||
WindowHook.UpdateWorkArea<NowPlayingWindow>();
|
||||
LiveStates.IsLyricsWindowStatusRefreshing = false;
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
|
||||
}
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.IsShownInSwitchers):
|
||||
WindowHook.SetIsShowInSwitchers<LyricsWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
|
||||
WindowHook.SetIsShowInSwitchers<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.IsAlwaysOnTop):
|
||||
WindowHook.SetIsAlwaysOnTop<LyricsWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
WindowHook.SetIsAlwaysOnTop<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.IsClickThrough):
|
||||
WindowHook.SetIsClickThrough<LyricsWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
WindowHook.SetIsClickThrough<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.IsBorderless):
|
||||
WindowHook.SetIsBorderless<LyricsWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
WindowHook.SetIsBorderless<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.WindowX):
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithX(LiveStates.LyricsWindowStatus.WindowX));
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithX(LiveStates.LyricsWindowStatus.WindowX));
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.WindowY):
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithY(LiveStates.LyricsWindowStatus.WindowY));
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithY(LiveStates.LyricsWindowStatus.WindowY));
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.WindowWidth):
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithWidth(LiveStates.LyricsWindowStatus.WindowWidth));
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithWidth(LiveStates.LyricsWindowStatus.WindowWidth));
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.WindowHeight):
|
||||
WindowHook.MoveAndResize<LyricsWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithHeight(LiveStates.LyricsWindowStatus.WindowHeight));
|
||||
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithHeight(LiveStates.LyricsWindowStatus.WindowHeight));
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.TitleBarArea):
|
||||
WindowHook.SetTitleBarArea<LyricsWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
WindowHook.SetTitleBarArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
|
||||
break;
|
||||
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
|
||||
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
Task PreviousAsync();
|
||||
Task NextAsync();
|
||||
Task ChangePosition(double seconds);
|
||||
Task ChangeLyricsLine(int index);
|
||||
|
||||
void UpdateLyrics();
|
||||
void UpdateTranslations();
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void UpdatePlayOrPauseSongShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
|
||||
{
|
||||
if (CurrentIsPlaying)
|
||||
{
|
||||
@@ -145,7 +145,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void UpdatePreviousSongShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.PreviousSong, _settingsService.AppSettings.GeneralSettings.PreviousSongShortcut, () =>
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.PreviousSong, _settingsService.AppSettings.GeneralSettings.PreviousSongShortcut, () =>
|
||||
{
|
||||
_ = PreviousAsync();
|
||||
});
|
||||
@@ -153,7 +153,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
private void UpdateNextSongShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.NextSong, _settingsService.AppSettings.GeneralSettings.NextSongShortcut, () =>
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.NextSong, _settingsService.AppSettings.GeneralSettings.NextSongShortcut, () =>
|
||||
{
|
||||
_ = NextAsync();
|
||||
});
|
||||
@@ -653,9 +653,17 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangeLyricsLine(int index)
|
||||
{
|
||||
if (CurrentLyricsData?.LyricsLines?.ElementAtOrDefault(index)?.StartMs is int startMs)
|
||||
{
|
||||
await ChangePosition(startMs / 1000.0);
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnCurrentIsPlayingChanged(bool value)
|
||||
{
|
||||
if (WindowHook.GetWindowHandle<LyricsWindow>() is IntPtr hwnd)
|
||||
if (WindowHook.GetWindowHandle<NowPlayingWindow>() is IntPtr hwnd)
|
||||
{
|
||||
TaskbarList.SetProgressState(hwnd, value ? TaskbarButtonProgressState.Normal : TaskbarButtonProgressState.Paused);
|
||||
}
|
||||
@@ -663,7 +671,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
partial void OnCurrentPositionChanged(TimeSpan value)
|
||||
{
|
||||
if (WindowHook.GetWindowHandle<LyricsWindow>() is IntPtr hwnd)
|
||||
if (WindowHook.GetWindowHandle<NowPlayingWindow>() is IntPtr hwnd)
|
||||
{
|
||||
TaskbarList.SetProgressValue(hwnd, (ulong)value.TotalSeconds, (ulong)(CurrentSongInfo?.Duration ?? value.TotalSeconds));
|
||||
}
|
||||
@@ -741,6 +749,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
{
|
||||
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
|
||||
{
|
||||
UpdateAlbumArtThemeColors();
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
@@ -770,9 +779,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
public void Receive(PropertyChangedMessage<Color> message)
|
||||
{
|
||||
if (message.Sender is LyricsWindowViewModel)
|
||||
if (message.Sender is NowPlayingWindowViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.BackdropAccentColor))
|
||||
if (message.PropertyName == nameof(NowPlayingWindowViewModel.BackdropAccentColor))
|
||||
{
|
||||
_envColor = message.NewValue;
|
||||
UpdateAlbumArtThemeColors();
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>Export successful</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>Unable to connect to LX Music server, please go to Settings - Advanced options to check if the link is entered correctly</value>
|
||||
<value>Unable to connect to LX Music Server, please go to Settings - Playback Source - LX Music - LX Music Server to check if the link is entered correctly</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>Fullscreen mode</value>
|
||||
@@ -384,10 +384,6 @@
|
||||
<data name="MainPageSplitView.Content" xml:space="preserve">
|
||||
<value>Split view</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>Click on the top-left button to enable immersive mode.
|
||||
If you encounter any problems, please go to the Settings page, About tab, and view the FAQ or contact the author for feedback</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||
<value>Welcome to BetterLyrics</value>
|
||||
</data>
|
||||
@@ -566,7 +562,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Acrylic Thin</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Description" xml:space="preserve">
|
||||
<value>At the same time, set the pure color layer opacity and the album art layer opacity in the lyrics background to 0 to get the correct effect</value>
|
||||
<value>Turning this on will override the theme settings in the lyric background settings</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Header" xml:space="preserve">
|
||||
<value>Adapt to environmental color</value>
|
||||
@@ -586,6 +582,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>Album art</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtHeight.Header" xml:space="preserve">
|
||||
<value>Album image height</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtLayer.Header" xml:space="preserve">
|
||||
<value>Album art layer</value>
|
||||
</data>
|
||||
@@ -595,6 +594,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageAlbumArtSize.Header" xml:space="preserve">
|
||||
<value>Album art size</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumEffect.Text" xml:space="preserve">
|
||||
<value>Album art area effect</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
|
||||
<value>Album art source</value>
|
||||
</data>
|
||||
@@ -658,9 +660,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
|
||||
<value>Lyrics background</value>
|
||||
</data>
|
||||
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
|
||||
<value>Blur amount</value>
|
||||
</data>
|
||||
<data name="SettingsPageBorderless.Header" xml:space="preserve">
|
||||
<value>Borderless window</value>
|
||||
</data>
|
||||
@@ -679,6 +678,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageCenter.Content" xml:space="preserve">
|
||||
<value>Center</value>
|
||||
</data>
|
||||
<data name="SettingsPageCheckShortcut.Content" xml:space="preserve">
|
||||
<value>Check shortcut</value>
|
||||
</data>
|
||||
<data name="SettingsPageChinese.Header" xml:space="preserve">
|
||||
<value>Chinese pronunciation</value>
|
||||
</data>
|
||||
@@ -691,6 +693,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageCJK.Header" xml:space="preserve">
|
||||
<value>CJK Unified Ideograph</value>
|
||||
</data>
|
||||
<data name="SettingsPageClear.Content" xml:space="preserve">
|
||||
<value>Clear</value>
|
||||
</data>
|
||||
<data name="SettingsPageClearCache.Content" xml:space="preserve">
|
||||
<value>Clear cache files</value>
|
||||
</data>
|
||||
@@ -700,6 +705,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
|
||||
<value>Click-through shortcut keys</value>
|
||||
</data>
|
||||
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
|
||||
<value>Collapse dropdown</value>
|
||||
</data>
|
||||
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||
<value>Compact</value>
|
||||
</data>
|
||||
@@ -715,6 +723,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageCreateFromTemplates.Content" xml:space="preserve">
|
||||
<value>Create from templates</value>
|
||||
</data>
|
||||
<data name="SettingsPageCrossfade.Content" xml:space="preserve">
|
||||
<value>Crossfade</value>
|
||||
</data>
|
||||
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
|
||||
<value>Current lyrics window status</value>
|
||||
</data>
|
||||
@@ -886,6 +897,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageHorizontalLayoutFactor.Text" xml:space="preserve">
|
||||
<value>Horizontal layout factor</value>
|
||||
</data>
|
||||
<data name="SettingsPageImageSwitchType.Header" xml:space="preserve">
|
||||
<value>Album art toggle animation</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>Import</value>
|
||||
</data>
|
||||
@@ -986,26 +1000,23 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Lyrics background</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>Non-current playback area</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>Font opacity (Non-current playback area)</value>
|
||||
<value>Not the current play line</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
|
||||
<value>Black</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
|
||||
<value>Blur amount</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Description" xml:space="preserve">
|
||||
<value>Enable blur for non-current rows</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
|
||||
<value>Adjusting this value will also increase the background blur intensity of the album image.</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>Significantly higher GPU usage when blur is enabled (> 0)</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Header" xml:space="preserve">
|
||||
<value>Blur Effect</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
|
||||
<value>Bold</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
|
||||
<value>Current line position</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
|
||||
<value>Lyrics area width factor</value>
|
||||
</data>
|
||||
@@ -1022,7 +1033,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Extra Light</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>Current playback area</value>
|
||||
<value>Current Play Row</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>Adaptive to lyrics background (Colored)</value>
|
||||
@@ -1090,15 +1101,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
|
||||
<value>Normal</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
|
||||
<value>Current char</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
|
||||
<value>Current line</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
|
||||
<value>Current line start to current char</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
|
||||
<value>Lyrics area height factor</value>
|
||||
</data>
|
||||
@@ -1300,6 +1302,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>Reference link</value>
|
||||
</data>
|
||||
<data name="SettingsPageRefreshDropdown.Content" xml:space="preserve">
|
||||
<value>Refresh dropdown</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>Original files and folders in this path will not be deleted when removing it from this app</value>
|
||||
</data>
|
||||
@@ -1393,6 +1398,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageShowTitle.Header" xml:space="preserve">
|
||||
<value>Show title</value>
|
||||
</data>
|
||||
<data name="SettingsPageSlide.Content" xml:space="preserve">
|
||||
<value>Slide</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>Current value: </value>
|
||||
</data>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>エクスポートが成功しました</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Serverに接続できない場合は、設定に移動してください。リンクが正しく入力されているかどうかを確認するための高度なオプションに移動してください</value>
|
||||
<value>LXミュージックサーバーに接続できません。[設定] - [再生ソース] - [LXミュージック] - [LXミュージックサーバー]に移動して、リンクが正しく入力されているかどうかを確認してください。</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>フルスクリーンモード</value>
|
||||
@@ -384,10 +384,6 @@
|
||||
<data name="MainPageSplitView.Content" xml:space="preserve">
|
||||
<value>分割ビュー</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>左上ボタンをクリックして、没入型モードを有効にします。
|
||||
問題が発生した場合は、[設定]ページ、[タブについて]にアクセスして、FAQを表示するか、フィードバックについて著者に連絡してください</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||
<value>BetterLyrics へようこそ</value>
|
||||
</data>
|
||||
@@ -566,7 +562,7 @@
|
||||
<value>アクリル薄い</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Description" xml:space="preserve">
|
||||
<value>同時に、歌詞の背景のピュアカラーレイヤーの不透明度とアルバムアートレイヤーの不透明度を0に設定して、正しい効果を得ます</value>
|
||||
<value>これをオンにすると、歌詞の背景設定のテーマ設定が上書きされます</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Header" xml:space="preserve">
|
||||
<value>環境の色に適応しましょう</value>
|
||||
@@ -586,6 +582,9 @@
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>アルバムアート</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtHeight.Header" xml:space="preserve">
|
||||
<value>アルバム画像の高さ</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtLayer.Header" xml:space="preserve">
|
||||
<value>アルバムアートレイヤー</value>
|
||||
</data>
|
||||
@@ -595,6 +594,9 @@
|
||||
<data name="SettingsPageAlbumArtSize.Header" xml:space="preserve">
|
||||
<value>アルバムアートサイズ</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumEffect.Text" xml:space="preserve">
|
||||
<value>アルバムリージョンアニメーション</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
|
||||
<value>アルバムアートソース</value>
|
||||
</data>
|
||||
@@ -658,9 +660,6 @@
|
||||
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
|
||||
<value>歌詞の背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
|
||||
<value>ぼやけの量</value>
|
||||
</data>
|
||||
<data name="SettingsPageBorderless.Header" xml:space="preserve">
|
||||
<value>ボーダーレスウィンドウ</value>
|
||||
</data>
|
||||
@@ -679,6 +678,9 @@
|
||||
<data name="SettingsPageCenter.Content" xml:space="preserve">
|
||||
<value>中心</value>
|
||||
</data>
|
||||
<data name="SettingsPageCheckShortcut.Content" xml:space="preserve">
|
||||
<value>ショートカットをチェック!</value>
|
||||
</data>
|
||||
<data name="SettingsPageChinese.Header" xml:space="preserve">
|
||||
<value>中国の発音</value>
|
||||
</data>
|
||||
@@ -691,6 +693,9 @@
|
||||
<data name="SettingsPageCJK.Header" xml:space="preserve">
|
||||
<value>CJK統一漢字</value>
|
||||
</data>
|
||||
<data name="SettingsPageClear.Content" xml:space="preserve">
|
||||
<value>消去</value>
|
||||
</data>
|
||||
<data name="SettingsPageClearCache.Content" xml:space="preserve">
|
||||
<value>キャッシュファイルをクリア</value>
|
||||
</data>
|
||||
@@ -700,6 +705,9 @@
|
||||
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
|
||||
<value>クリックスルーショートカットキー</value>
|
||||
</data>
|
||||
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
|
||||
<value>ドロップダウンを折りたたみます</value>
|
||||
</data>
|
||||
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||
<value>コンパクト</value>
|
||||
</data>
|
||||
@@ -715,6 +723,9 @@
|
||||
<data name="SettingsPageCreateFromTemplates.Content" xml:space="preserve">
|
||||
<value>テンプレートから作成</value>
|
||||
</data>
|
||||
<data name="SettingsPageCrossfade.Content" xml:space="preserve">
|
||||
<value>クロスフェード</value>
|
||||
</data>
|
||||
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
|
||||
<value>現在の歌詞ウィンドウステータス</value>
|
||||
</data>
|
||||
@@ -886,6 +897,9 @@
|
||||
<data name="SettingsPageHorizontalLayoutFactor.Text" xml:space="preserve">
|
||||
<value>水平レイアウト係数</value>
|
||||
</data>
|
||||
<data name="SettingsPageImageSwitchType.Header" xml:space="preserve">
|
||||
<value>アルバムダイアグラムアニメーションの切り替え</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>インポート</value>
|
||||
</data>
|
||||
@@ -986,26 +1000,23 @@
|
||||
<value>歌詞の背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>非電流再生エリア</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>フォントの不透明度(非電流再生エリア)</value>
|
||||
<value>現在のプレイラインではありません</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
|
||||
<value>黒</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
|
||||
<value>ぼやけの量</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Description" xml:space="preserve">
|
||||
<value>現在ではない行のぼかしを有効にします</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
|
||||
<value>この値を調整すると、アルバム画像のバックグラウンドブラー強度も増加します。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>ぼかしが有効になっている場合のGPU使用量が大幅に高くなります(> 0)</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Header" xml:space="preserve">
|
||||
<value>ぼかし効果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
|
||||
<value>大胆な</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
|
||||
<value>現在の行の位置</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
|
||||
<value>歌詞エリア幅係数</value>
|
||||
</data>
|
||||
@@ -1022,7 +1033,7 @@
|
||||
<value>余分な光</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>現在の再生エリア</value>
|
||||
<value>現在のプレイ行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>歌詞の背景に適応する(色付き)</value>
|
||||
@@ -1090,15 +1101,6 @@
|
||||
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
|
||||
<value>普通</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
|
||||
<value>現在の文字</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
|
||||
<value>現在の行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
|
||||
<value>現在のラインが現在の文字から始まります</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
|
||||
<value>歌詞エリアの高さ係数</value>
|
||||
</data>
|
||||
@@ -1300,6 +1302,9 @@
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>参照リンク</value>
|
||||
</data>
|
||||
<data name="SettingsPageRefreshDropdown.Content" xml:space="preserve">
|
||||
<value>ドロップダウンを更新してください</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>このパスの元のファイルとフォルダーは、このアプリから削除するときに削除されません</value>
|
||||
</data>
|
||||
@@ -1393,6 +1398,9 @@
|
||||
<data name="SettingsPageShowTitle.Header" xml:space="preserve">
|
||||
<value>タイトルを表示</value>
|
||||
</data>
|
||||
<data name="SettingsPageSlide.Content" xml:space="preserve">
|
||||
<value>スライド</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>現在の値: </value>
|
||||
</data>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>수출 성공</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Server에 연결할 수 없습니다. 설정으로 이동하십시오 - 고급 옵션이 링크가 올바르게 입력되었는지 확인하십시오.</value>
|
||||
<value>LX Music Server에 연결할 수 없습니다. 설정 - 재생 소스 - LX Music - LX Music Server로 이동하여 링크가 올바르게 입력되었는지 확인하십시오</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>전체화면 모드</value>
|
||||
@@ -384,10 +384,6 @@
|
||||
<data name="MainPageSplitView.Content" xml:space="preserve">
|
||||
<value>분할보기</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>몰입 형 모드를 활성화하려면 왼쪽 상단 버튼을 클릭하십시오.
|
||||
문제가 발생하면 설정 페이지, 탭 정보로 이동하여 FAQ를보고 저자에게 연락하여 피드백을 받으십시오.</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||
<value>Betterlyrics에 오신 것을 환영합니다</value>
|
||||
</data>
|
||||
@@ -566,7 +562,7 @@
|
||||
<value>아크릴 얇은</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Description" xml:space="preserve">
|
||||
<value>동시에 가사 배경의 순수 컬러 레이어 불투명도와 앨범 아트 레이어 불투명도를 0으로 설정하면 정확한 효과를 얻을 수 있습니다</value>
|
||||
<value>이 기능을 켜면 가사 배경 설정의 테마 설정이 재정의됩니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Header" xml:space="preserve">
|
||||
<value>환경 색상에 적응하세요</value>
|
||||
@@ -586,6 +582,9 @@
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>앨범 아트</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtHeight.Header" xml:space="preserve">
|
||||
<value>앨범 이미지 높이</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtLayer.Header" xml:space="preserve">
|
||||
<value>앨범 아트 레이어</value>
|
||||
</data>
|
||||
@@ -595,6 +594,9 @@
|
||||
<data name="SettingsPageAlbumArtSize.Header" xml:space="preserve">
|
||||
<value>앨범 아트 크기</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumEffect.Text" xml:space="preserve">
|
||||
<value>앨범 지역 애니메이션</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
|
||||
<value>앨범 아트 소스</value>
|
||||
</data>
|
||||
@@ -658,9 +660,6 @@
|
||||
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
|
||||
<value>가사 배경</value>
|
||||
</data>
|
||||
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
|
||||
<value>흐림 금액</value>
|
||||
</data>
|
||||
<data name="SettingsPageBorderless.Header" xml:space="preserve">
|
||||
<value>창문</value>
|
||||
</data>
|
||||
@@ -679,6 +678,9 @@
|
||||
<data name="SettingsPageCenter.Content" xml:space="preserve">
|
||||
<value>센터</value>
|
||||
</data>
|
||||
<data name="SettingsPageCheckShortcut.Content" xml:space="preserve">
|
||||
<value>단축키를 확인하세요!</value>
|
||||
</data>
|
||||
<data name="SettingsPageChinese.Header" xml:space="preserve">
|
||||
<value>화음</value>
|
||||
</data>
|
||||
@@ -691,6 +693,9 @@
|
||||
<data name="SettingsPageCJK.Header" xml:space="preserve">
|
||||
<value>한중일 통합 이데오그래프</value>
|
||||
</data>
|
||||
<data name="SettingsPageClear.Content" xml:space="preserve">
|
||||
<value>지우기</value>
|
||||
</data>
|
||||
<data name="SettingsPageClearCache.Content" xml:space="preserve">
|
||||
<value>캐시 파일을 지웁니다</value>
|
||||
</data>
|
||||
@@ -700,6 +705,9 @@
|
||||
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
|
||||
<value>바로 가기 키를 클릭하세요</value>
|
||||
</data>
|
||||
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
|
||||
<value>드롭다운 접기</value>
|
||||
</data>
|
||||
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||
<value>콤팩트</value>
|
||||
</data>
|
||||
@@ -715,6 +723,9 @@
|
||||
<data name="SettingsPageCreateFromTemplates.Content" xml:space="preserve">
|
||||
<value>템플릿에서 만들기</value>
|
||||
</data>
|
||||
<data name="SettingsPageCrossfade.Content" xml:space="preserve">
|
||||
<value>크로스 페이드</value>
|
||||
</data>
|
||||
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
|
||||
<value>현재 가사 창 상태</value>
|
||||
</data>
|
||||
@@ -886,6 +897,9 @@
|
||||
<data name="SettingsPageHorizontalLayoutFactor.Text" xml:space="preserve">
|
||||
<value>수평 레이아웃 요소</value>
|
||||
</data>
|
||||
<data name="SettingsPageImageSwitchType.Header" xml:space="preserve">
|
||||
<value>앨범 다이어그램 토글 애니메이션</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>가져오기</value>
|
||||
</data>
|
||||
@@ -986,26 +1000,23 @@
|
||||
<value>가사 배경</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>비 전류 재생 영역</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>글꼴 불투명 (비 전류 재생 영역)</value>
|
||||
<value>현재 플레이 라인이 아닙니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
|
||||
<value>검은색</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
|
||||
<value>흐림 금액</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Description" xml:space="preserve">
|
||||
<value>현재 행이 아닌 행에 블러 활성화</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
|
||||
<value>이 값을 조정하면 앨범 이미지의 배경 흐림 강도가 증가합니다.</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>Blur가 활성화 될 때 상당히 높은 GPU 사용량 (> 0)</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Header" xml:space="preserve">
|
||||
<value>흐림 효과</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
|
||||
<value>용감한</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
|
||||
<value>현재 행 위치</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
|
||||
<value>가사 영역 너비 계수</value>
|
||||
</data>
|
||||
@@ -1022,7 +1033,7 @@
|
||||
<value>여분의 빛</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>현재 재생 영역</value>
|
||||
<value>현재 플레이 행</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>가사 배경 (색상)에 적응</value>
|
||||
@@ -1090,15 +1101,6 @@
|
||||
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
|
||||
<value>정상</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
|
||||
<value>현재 숯</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
|
||||
<value>현재 라인</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
|
||||
<value>현재 라인은 현재 숯으로 시작합니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
|
||||
<value>가사 영역 높이 계수</value>
|
||||
</data>
|
||||
@@ -1300,6 +1302,9 @@
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>참고문헌</value>
|
||||
</data>
|
||||
<data name="SettingsPageRefreshDropdown.Content" xml:space="preserve">
|
||||
<value>새로 고침 드롭다운</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>이 경로의 원본 파일과 폴더는이 앱에서 제거 할 때 삭제되지 않습니다.</value>
|
||||
</data>
|
||||
@@ -1393,6 +1398,9 @@
|
||||
<data name="SettingsPageShowTitle.Header" xml:space="preserve">
|
||||
<value>제목 표시</value>
|
||||
</data>
|
||||
<data name="SettingsPageSlide.Content" xml:space="preserve">
|
||||
<value>살짝 밀기</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>현재 가치 : </value>
|
||||
</data>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>导出成功</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>无法连接到 LX 音乐服务器,请转到设置 - 高级选项以检查是否正确输入链接</value>
|
||||
<value>无法连接到 LX 音乐服务器,请转到设置 - 播放源 - LX Music - LX 音乐服务器以检查是否正确输入链接</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>全屏模式</value>
|
||||
@@ -384,10 +384,6 @@
|
||||
<data name="MainPageSplitView.Content" xml:space="preserve">
|
||||
<value>分屏视图</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>单击左上按钮以启用沉浸式模式。
|
||||
如果遇到任何问题,请转到“设置”页面,关于标签,查看常见问题解答或联系作者以获取反馈</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||
<value>欢迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
@@ -566,7 +562,7 @@
|
||||
<value>亚克力(薄层)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Description" xml:space="preserve">
|
||||
<value>同时,将歌词背景中的纯色图层不透明度和专辑美术图层不透明度设置为 0 ,以获得正确的效果</value>
|
||||
<value>开启该项将覆盖歌词背景设置中的主题设置</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Header" xml:space="preserve">
|
||||
<value>适应环境色彩</value>
|
||||
@@ -586,6 +582,9 @@
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtHeight.Header" xml:space="preserve">
|
||||
<value>专辑图片高度</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtLayer.Header" xml:space="preserve">
|
||||
<value>专辑艺术图层</value>
|
||||
</data>
|
||||
@@ -595,6 +594,9 @@
|
||||
<data name="SettingsPageAlbumArtSize.Header" xml:space="preserve">
|
||||
<value>专辑封面尺寸</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumEffect.Text" xml:space="preserve">
|
||||
<value>专辑区域动效</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
|
||||
<value>专辑封面源</value>
|
||||
</data>
|
||||
@@ -658,9 +660,6 @@
|
||||
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
|
||||
<value>歌词背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
|
||||
<value>模糊度</value>
|
||||
</data>
|
||||
<data name="SettingsPageBorderless.Header" xml:space="preserve">
|
||||
<value>无边框窗口</value>
|
||||
</data>
|
||||
@@ -679,6 +678,9 @@
|
||||
<data name="SettingsPageCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageCheckShortcut.Content" xml:space="preserve">
|
||||
<value>检查快捷方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageChinese.Header" xml:space="preserve">
|
||||
<value>中文注音</value>
|
||||
</data>
|
||||
@@ -691,6 +693,9 @@
|
||||
<data name="SettingsPageCJK.Header" xml:space="preserve">
|
||||
<value>中日韩统一表意文字</value>
|
||||
</data>
|
||||
<data name="SettingsPageClear.Content" xml:space="preserve">
|
||||
<value>清除</value>
|
||||
</data>
|
||||
<data name="SettingsPageClearCache.Content" xml:space="preserve">
|
||||
<value>清除缓存文件</value>
|
||||
</data>
|
||||
@@ -700,6 +705,9 @@
|
||||
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
|
||||
<value>点击穿透快捷键</value>
|
||||
</data>
|
||||
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
|
||||
<value>折叠下拉列表</value>
|
||||
</data>
|
||||
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||
<value>紧凑</value>
|
||||
</data>
|
||||
@@ -715,6 +723,9 @@
|
||||
<data name="SettingsPageCreateFromTemplates.Content" xml:space="preserve">
|
||||
<value>从模板创建</value>
|
||||
</data>
|
||||
<data name="SettingsPageCrossfade.Content" xml:space="preserve">
|
||||
<value>交叉淡入淡出</value>
|
||||
</data>
|
||||
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
|
||||
<value>当前歌词窗口状态</value>
|
||||
</data>
|
||||
@@ -886,6 +897,9 @@
|
||||
<data name="SettingsPageHorizontalLayoutFactor.Text" xml:space="preserve">
|
||||
<value>水平布局系数</value>
|
||||
</data>
|
||||
<data name="SettingsPageImageSwitchType.Header" xml:space="preserve">
|
||||
<value>专辑图切换动画</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>导入</value>
|
||||
</data>
|
||||
@@ -986,26 +1000,23 @@
|
||||
<value>歌词背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>非当前播放区域</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>字体不透明度(非当前播放区域)</value>
|
||||
<value>非当前播放行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
|
||||
<value>黑体</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
|
||||
<value>模糊度</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Description" xml:space="preserve">
|
||||
<value>为非当前行启用模糊效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
|
||||
<value>调整该数值将同步提高专辑图片背景模糊强度</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>启用模糊(> 0)时将显著提升 GPU 占用率</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Header" xml:space="preserve">
|
||||
<value>模糊效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
|
||||
<value>粗体</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
|
||||
<value>当前行位置</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
|
||||
<value>歌词区域宽度因子</value>
|
||||
</data>
|
||||
@@ -1022,7 +1033,7 @@
|
||||
<value>超细</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>当前播放区域</value>
|
||||
<value>当前播放行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>适应歌词背景(彩色)</value>
|
||||
@@ -1090,15 +1101,6 @@
|
||||
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
|
||||
<value>常规</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
|
||||
<value>当前字符</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
|
||||
<value>当前行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
|
||||
<value>当前歌词开始到当前字符</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
|
||||
<value>歌词区域高度因子</value>
|
||||
</data>
|
||||
@@ -1300,6 +1302,9 @@
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>参考链接</value>
|
||||
</data>
|
||||
<data name="SettingsPageRefreshDropdown.Content" xml:space="preserve">
|
||||
<value>刷新下拉列表</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>路径中的原始文件和文件夹不会被删除</value>
|
||||
</data>
|
||||
@@ -1393,6 +1398,9 @@
|
||||
<data name="SettingsPageShowTitle.Header" xml:space="preserve">
|
||||
<value>显示标题</value>
|
||||
</data>
|
||||
<data name="SettingsPageSlide.Content" xml:space="preserve">
|
||||
<value>滑动</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>当前值: </value>
|
||||
</data>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<value>導出成功</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>無法連接到 LX 音樂服務器,請轉到設置 - 高級選項以檢查是否正確輸入鏈接</value>
|
||||
<value>無法連接到LX 音樂伺服器,請前往設定- 播放來源- LX Music - LX 音樂伺服器以檢查是否正確輸入連結</value>
|
||||
</data>
|
||||
<data name="FullscreenMode" xml:space="preserve">
|
||||
<value>全螢幕模式</value>
|
||||
@@ -384,10 +384,6 @@
|
||||
<data name="MainPageSplitView.Content" xml:space="preserve">
|
||||
<value>分割畫面視圖</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>單擊左上按鈕以啟用沉浸式模式。
|
||||
如果遇到任何問題,請轉到“設置”頁面,關於標籤,查看常見問題解答或聯繫作者以獲取反饋</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
|
||||
<value>歡迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
@@ -566,7 +562,7 @@
|
||||
<value>亞克力(薄層)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Description" xml:space="preserve">
|
||||
<value>同時,將歌詞背景中的純色圖層不透明度和專輯藝術圖層不透明度設置為 0 ,以獲得正確的效果</value>
|
||||
<value>開啟該項目將覆蓋歌詞背景設定中的主題設定</value>
|
||||
</data>
|
||||
<data name="SettingsPageAdaptEnvColor.Header" xml:space="preserve">
|
||||
<value>適應環境色彩</value>
|
||||
@@ -586,6 +582,9 @@
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtHeight.Header" xml:space="preserve">
|
||||
<value>專輯圖片高度</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArtLayer.Header" xml:space="preserve">
|
||||
<value>相簿藝術圖層</value>
|
||||
</data>
|
||||
@@ -595,6 +594,9 @@
|
||||
<data name="SettingsPageAlbumArtSize.Header" xml:space="preserve">
|
||||
<value>相簿藝術尺寸</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumEffect.Text" xml:space="preserve">
|
||||
<value>專輯區域動效</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
|
||||
<value>專輯封面來源</value>
|
||||
</data>
|
||||
@@ -658,9 +660,6 @@
|
||||
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
|
||||
<value>歌詞背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
|
||||
<value>模糊度</value>
|
||||
</data>
|
||||
<data name="SettingsPageBorderless.Header" xml:space="preserve">
|
||||
<value>無邊框窗口</value>
|
||||
</data>
|
||||
@@ -679,6 +678,9 @@
|
||||
<data name="SettingsPageCenter.Content" xml:space="preserve">
|
||||
<value>居中</value>
|
||||
</data>
|
||||
<data name="SettingsPageCheckShortcut.Content" xml:space="preserve">
|
||||
<value>檢查捷徑</value>
|
||||
</data>
|
||||
<data name="SettingsPageChinese.Header" xml:space="preserve">
|
||||
<value>中文注音</value>
|
||||
</data>
|
||||
@@ -691,6 +693,9 @@
|
||||
<data name="SettingsPageCJK.Header" xml:space="preserve">
|
||||
<value>中日韓統一表意文字</value>
|
||||
</data>
|
||||
<data name="SettingsPageClear.Content" xml:space="preserve">
|
||||
<value>清除</value>
|
||||
</data>
|
||||
<data name="SettingsPageClearCache.Content" xml:space="preserve">
|
||||
<value>清除快取檔案</value>
|
||||
</data>
|
||||
@@ -700,6 +705,9 @@
|
||||
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
|
||||
<value>點選穿透快捷鍵</value>
|
||||
</data>
|
||||
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
|
||||
<value>收起下拉列表</value>
|
||||
</data>
|
||||
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
|
||||
<value>緊湊</value>
|
||||
</data>
|
||||
@@ -715,6 +723,9 @@
|
||||
<data name="SettingsPageCreateFromTemplates.Content" xml:space="preserve">
|
||||
<value>從範本建立</value>
|
||||
</data>
|
||||
<data name="SettingsPageCrossfade.Content" xml:space="preserve">
|
||||
<value>交叉淡入淡出</value>
|
||||
</data>
|
||||
<data name="SettingsPageCurrentLyricsWindowStatus.Text" xml:space="preserve">
|
||||
<value>當前歌詞窗口狀態</value>
|
||||
</data>
|
||||
@@ -886,6 +897,9 @@
|
||||
<data name="SettingsPageHorizontalLayoutFactor.Text" xml:space="preserve">
|
||||
<value>水平布局系數</value>
|
||||
</data>
|
||||
<data name="SettingsPageImageSwitchType.Header" xml:space="preserve">
|
||||
<value>專輯圖切換動畫</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>匯入</value>
|
||||
</data>
|
||||
@@ -986,26 +1000,23 @@
|
||||
<value>歌詞背景</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
|
||||
<value>非當前播放區域</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>字體不透明度(非目前播放區域)</value>
|
||||
<value>非目前播放行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
|
||||
<value>黑體</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
|
||||
<value>模糊度</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Description" xml:space="preserve">
|
||||
<value>為非目前行啟用模糊效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
|
||||
<value>調整此數值將同步提升專輯圖片背景模糊強度</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>啟用模糊(> 0)時將顯著提升 GPU 佔用率</value>
|
||||
<data name="SettingsPageLyricsBlurEffect.Header" xml:space="preserve">
|
||||
<value>模糊效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
|
||||
<value>粗體</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsCenterTopOffset.Header" xml:space="preserve">
|
||||
<value>目前行位置</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsColFactor.Header" xml:space="preserve">
|
||||
<value>歌詞區域寬度因子</value>
|
||||
</data>
|
||||
@@ -1022,7 +1033,7 @@
|
||||
<value>超細</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
|
||||
<value>當前播放區域</value>
|
||||
<value>目前播放行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
|
||||
<value>適應歌詞背景(彩色)</value>
|
||||
@@ -1090,15 +1101,6 @@
|
||||
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
|
||||
<value>常規</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
|
||||
<value>目前字元</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
|
||||
<value>目前行</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
|
||||
<value>當前歌詞開始到當前字符</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
|
||||
<value>歌詞區域高度因子</value>
|
||||
</data>
|
||||
@@ -1300,6 +1302,9 @@
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>參考數據</value>
|
||||
</data>
|
||||
<data name="SettingsPageRefreshDropdown.Content" xml:space="preserve">
|
||||
<value>刷新下拉列表</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>路徑中的原始檔案和資料夾不會被刪除</value>
|
||||
</data>
|
||||
@@ -1393,6 +1398,9 @@
|
||||
<data name="SettingsPageShowTitle.Header" xml:space="preserve">
|
||||
<value>顯示標題</value>
|
||||
</data>
|
||||
<data name="SettingsPageSlide.Content" xml:space="preserve">
|
||||
<value>滑動</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>目前值: </value>
|
||||
</data>
|
||||
|
||||
@@ -26,9 +26,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial object ListViewSelectedItemTag { get; set; } = "General";
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<string> MonitorDeviceNames { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsConfigPanelOpened { get; set; } = false;
|
||||
|
||||
@@ -45,14 +42,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
AppSettings = _settingsService.AppSettings;
|
||||
LiveStates = _liveStatesService.LiveStates;
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void RefreshMonitorDeviceNames()
|
||||
{
|
||||
MonitorDeviceNames = [.. MonitorHook.GetAllMonitorDeviceNames()];
|
||||
LiveStates.LyricsWindowStatus?.MonitorDeviceName = MonitorDeviceNames.FirstOrDefault() ?? "";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -11,7 +11,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsPageViewModel : BaseViewModel
|
||||
public partial class NowPlayingPageViewModel : BaseViewModel
|
||||
{
|
||||
public IMediaSessionsService MediaSessionsService { get; private set; }
|
||||
private readonly ILiveStatesService _liveStatesService;
|
||||
@@ -37,7 +37,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial double TimelineSliderThumbSeconds { get; set; } = 0;
|
||||
|
||||
public LyricsPageViewModel(IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
|
||||
public NowPlayingPageViewModel(IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
|
||||
{
|
||||
_liveStatesService = liveStatesService;
|
||||
MediaSessionsService = mediaSessionsService;
|
||||
@@ -24,7 +24,7 @@ using WinUIEx;
|
||||
|
||||
namespace BetterLyrics.WinUI3
|
||||
{
|
||||
public partial class LyricsWindowViewModel
|
||||
public partial class NowPlayingWindowViewModel
|
||||
: BaseWindowViewModel,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<List<string>>>,
|
||||
@@ -36,7 +36,7 @@ namespace BetterLyrics.WinUI3
|
||||
private ForegroundWindowHook? _fgWindowWatcher = null;
|
||||
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
|
||||
public NowPlayingWindowViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_liveStatesService = liveStatesService;
|
||||
@@ -70,11 +70,11 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateLyricsWindowShowHideShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowShowOrHide,
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.LyricsWindowShowOrHide,
|
||||
_settingsService.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut,
|
||||
() =>
|
||||
{
|
||||
var window = WindowHook.GetWindow<LyricsWindow>();
|
||||
var window = WindowHook.GetWindow<NowPlayingWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
if (window.Visible)
|
||||
@@ -83,7 +83,7 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -91,7 +91,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateLyricsWindowBorderlessShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.Borderless,
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.Borderless,
|
||||
_settingsService.AppSettings.GeneralSettings.BorderlessShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -102,7 +102,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateLyricsWindowClickThroughShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.ClickThrough,
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.ClickThrough,
|
||||
_settingsService.AppSettings.GeneralSettings.ClickThroughShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -113,7 +113,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
private void UpdateLyricsWindowSwitchShortcut()
|
||||
{
|
||||
GlobalHotKeyHook.UpdateHotKey<LyricsWindow>(ShortcutID.LyricsWindowSwitch,
|
||||
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.LyricsWindowSwitch,
|
||||
_settingsService.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut,
|
||||
() =>
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace BetterLyrics.WinUI3
|
||||
|
||||
public void InitFgWindowWatcher()
|
||||
{
|
||||
var window = WindowHook.GetWindow<LyricsWindow>();
|
||||
var window = WindowHook.GetWindow<NowPlayingWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
@@ -169,7 +169,7 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
else
|
||||
{
|
||||
var window = WindowHook.GetWindow<LyricsWindow>();
|
||||
var window = WindowHook.GetWindow<NowPlayingWindow>();
|
||||
window?.Hide();
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private static void ResetWindowPosition()
|
||||
{
|
||||
var lyricsWindow = WindowHook.GetWindow<LyricsWindow>();
|
||||
var lyricsWindow = WindowHook.GetWindow<NowPlayingWindow>();
|
||||
lyricsWindow?.MoveAndResize(100, 100, 800, 500);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private static void OpenLyrics()
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void LyricsWindowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
}
|
||||
|
||||
private void SettingsWindowButton_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="BetterLyrics.WinUI3.Views.LyricsPage"
|
||||
x:Class="BetterLyrics.WinUI3.Views.NowPlayingPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
|
||||
@@ -27,9 +27,9 @@
|
||||
SizeChanged="RootGrid_SizeChanged">
|
||||
|
||||
<!-- Win2D drawing area -->
|
||||
<uc:NowPlayingCanvas x:Name="NowPlayingCanvas" />
|
||||
<uc:LyricsCanvas x:Name="LyricsCanvas" />
|
||||
|
||||
<Grid x:Name="TrackSummaryGridContainer" Loaded="TrackSummaryGridContainer_Loaded">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition x:Name="TrackSummaryRowDef" Height="0" />
|
||||
<RowDefinition x:Name="MiddleGapRowDef" Height="0" />
|
||||
@@ -66,38 +66,15 @@
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<!-- Album art -->
|
||||
<Grid
|
||||
x:Name="AlbumArtGrid"
|
||||
Margin="-32"
|
||||
Padding="32"
|
||||
HorizontalAlignment="{Binding ElementName=SongInfoStackPanel, Path=HorizontalAlignment, Mode=OneWay}">
|
||||
<Grid x:Name="AlbumArtGrid" HorizontalAlignment="{Binding ElementName=SongInfoStackPanel, Path=HorizontalAlignment, Mode=OneWay}">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||
</Grid.OpacityTransition>
|
||||
<Grid
|
||||
x:Name="ShadowCastGrid"
|
||||
CornerRadius="{x:Bind AlbumArtCornerRadius, Mode=OneWay}"
|
||||
SizeChanged="ShadowCastGrid_SizeChanged">
|
||||
<Image x:Name="LastAlbumArtImage" Stretch="Uniform">
|
||||
<Image.OpacityTransition>
|
||||
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||
</Image.OpacityTransition>
|
||||
</Image>
|
||||
<Image x:Name="AlbumArtImage" Stretch="Uniform">
|
||||
<Image.OpacityTransition>
|
||||
<ScalarTransition Duration="{x:Bind const:Time.AnimationDuration}" />
|
||||
</Image.OpacityTransition>
|
||||
</Image>
|
||||
</Grid>
|
||||
<Border
|
||||
x:Name="ShadowRect"
|
||||
CornerRadius="{x:Bind AlbumArtCornerRadius, Mode=OneWay}"
|
||||
Loaded="ShadowRect_Loaded"
|
||||
Translation="0,0,0">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow x:Name="Shadow" />
|
||||
</Border.Shadow>
|
||||
</Border>
|
||||
<uc:ImageSwitcher
|
||||
CornerRadiusAmount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.CoverImageRadius, Mode=OneWay}"
|
||||
ShadowAmount="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.CoverImageShadowAmount, Mode=OneWay}"
|
||||
Source="{x:Bind ViewModel.MediaSessionsService.AlbumArtBitmapImage, Mode=OneWay}"
|
||||
SwitchType="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtAreaEffectSettings.ImageSwitchType, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Song info -->
|
||||
@@ -149,7 +126,7 @@
|
||||
<TextBlock
|
||||
x:Name="ArtistsTextBlock"
|
||||
FontWeight="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontWeight, Converter={StaticResource LyricsFontWeightToFontWeightConverter}, Mode=OneWay}"
|
||||
Opacity="0.5"
|
||||
Opacity="0.6"
|
||||
TextTrimming="None"
|
||||
Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.ShowArtists, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</dev:AutoScrollView>
|
||||
@@ -174,7 +151,7 @@
|
||||
<TextBlock
|
||||
x:Name="AlbumTextBlock"
|
||||
FontWeight="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontWeight, Converter={StaticResource LyricsFontWeightToFontWeightConverter}, Mode=OneWay}"
|
||||
Opacity="0.5"
|
||||
Opacity="0.3"
|
||||
TextTrimming="None"
|
||||
Visibility="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.ShowAlbum, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</dev:AutoScrollView>
|
||||
@@ -187,8 +164,13 @@
|
||||
<Grid x:Name="LyricsPlaceholder" SizeChanged="LyricsPlaceholder_SizeChanged">
|
||||
<ScrollViewer
|
||||
x:Name="LyricsScrollViewer"
|
||||
PointerEntered="LyricsScrollViewer_PointerEntered"
|
||||
PointerExited="LyricsScrollViewer_PointerExited"
|
||||
PointerMoved="LyricsScrollViewer_PointerMoved"
|
||||
PointerPressed="LyricsScrollViewer_PointerPressed"
|
||||
PointerReleased="LyricsScrollViewer_PointerReleased"
|
||||
PointerWheelChanged="LyricsScrollViewer_PointerWheelChanged"
|
||||
Visibility="Collapsed">
|
||||
VerticalScrollBarVisibility="Hidden">
|
||||
<Grid />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
@@ -25,12 +25,11 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class LyricsPage : Page,
|
||||
public sealed partial class NowPlayingPage : Page,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<SongInfo?>>,
|
||||
IRecipient<PropertyChangedMessage<BitmapImage?>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsLayoutOrientation>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsDisplayType>>,
|
||||
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>,
|
||||
@@ -39,30 +38,21 @@ namespace BetterLyrics.WinUI3.Views
|
||||
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
|
||||
private readonly DispatcherQueueTimer _timer = App.Current.Resources.DispatcherQueue.CreateTimer();
|
||||
private readonly DispatcherQueueTimer _layoutChangedTimer = App.Current.Resources.DispatcherQueue.CreateTimer();
|
||||
private readonly DispatcherQueueTimer _scrollChangedTimer = App.Current.Resources.DispatcherQueue.CreateTimer();
|
||||
|
||||
public CornerRadius AlbumArtCornerRadius
|
||||
{
|
||||
get { return (CornerRadius)GetValue(AlbumArtCornerRadiusProperty); }
|
||||
set { SetValue(AlbumArtCornerRadiusProperty, value); }
|
||||
}
|
||||
public NowPlayingPageViewModel ViewModel => (NowPlayingPageViewModel)DataContext;
|
||||
|
||||
public static readonly DependencyProperty AlbumArtCornerRadiusProperty =
|
||||
DependencyProperty.Register(nameof(AlbumArtCornerRadius), typeof(double), typeof(NowPlayingCanvas), new PropertyMetadata(new CornerRadius(0)));
|
||||
|
||||
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
|
||||
|
||||
public LyricsPage()
|
||||
public NowPlayingPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
DataContext = Ioc.Default.GetRequiredService<LyricsPageViewModel>();
|
||||
DataContext = Ioc.Default.GetRequiredService<NowPlayingPageViewModel>();
|
||||
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<int>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<bool>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<SongInfo?>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<BitmapImage?>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsLayoutOrientation>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsDisplayType>>(this);
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<AlbumArtThemeColors>>(this);
|
||||
@@ -71,7 +61,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void CompositionTarget_Rendering(object? sender, object e)
|
||||
{
|
||||
var currentTime = NowPlayingCanvas.SongPosition.TotalSeconds;
|
||||
var currentTime = LyricsCanvas.SongPosition.TotalSeconds;
|
||||
TimelineSlider.Value = currentTime;
|
||||
}
|
||||
|
||||
@@ -79,7 +69,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void RenderTextBlock(TextBlock? sender, string? text, double fontSize)
|
||||
{
|
||||
if (sender == null || string.IsNullOrEmpty(text) || fontSize == 0) return;
|
||||
if (sender == null || text == null || fontSize == 0) return;
|
||||
|
||||
var lyricsStyleSettings = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
|
||||
|
||||
@@ -127,19 +117,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
|
||||
// ==== AlbumArt
|
||||
|
||||
private void UpdateAlbumArtCornerRadius()
|
||||
{
|
||||
var factor = _liveStatesService.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.CoverImageRadius / 100.0;
|
||||
AlbumArtCornerRadius = new((AlbumArtImage.ActualHeight / 2) * factor);
|
||||
}
|
||||
|
||||
private void UpdateAlbumArtShadow()
|
||||
{
|
||||
var amount = _liveStatesService.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.CoverImageShadowAmount;
|
||||
ShadowRect.Translation = new(0, 0, amount);
|
||||
}
|
||||
|
||||
private void UpdateAlbumArtOpacity()
|
||||
{
|
||||
switch (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsDisplayType)
|
||||
@@ -202,7 +179,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
Grid.SetRow(SongInfoStackPanel, 3);
|
||||
Grid.SetRowSpan(SongInfoStackPanel, 1);
|
||||
Grid.SetColumn(SongInfoStackPanel, 0);
|
||||
Grid.SetColumnSpan(SongInfoStackPanel, 3);
|
||||
Grid.SetColumnSpan(SongInfoStackPanel, 2);
|
||||
break;
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
Grid.SetRow(SongInfoStackPanel, 1);
|
||||
@@ -263,7 +240,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
Grid.SetRow(AlbumArtGrid, 1);
|
||||
Grid.SetRowSpan(AlbumArtGrid, 1);
|
||||
Grid.SetColumn(AlbumArtGrid, 0);
|
||||
Grid.SetColumnSpan(AlbumArtGrid, 3);
|
||||
Grid.SetColumnSpan(AlbumArtGrid, 2);
|
||||
break;
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
Grid.SetRow(AlbumArtGrid, 1);
|
||||
@@ -283,11 +260,11 @@ namespace BetterLyrics.WinUI3.Views
|
||||
switch (_liveStatesService.LiveStates.LyricsWindowStatus.LyricsDisplayType)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
NowPlayingCanvas.LyricsOpacity = 0;
|
||||
LyricsCanvas.LyricsOpacity = 0;
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
case LyricsDisplayType.SplitView:
|
||||
NowPlayingCanvas.LyricsOpacity = 1;
|
||||
LyricsCanvas.LyricsOpacity = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -302,25 +279,25 @@ namespace BetterLyrics.WinUI3.Views
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
NowPlayingCanvas.LyricsStartX = LeftGapDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsStartY = 0;
|
||||
NowPlayingCanvas.LyricsWidth = TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth + LyricsColDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
LyricsCanvas.LyricsStartX = LeftGapDef.ActualWidth;
|
||||
LyricsCanvas.LyricsStartY = 0;
|
||||
LyricsCanvas.LyricsWidth = TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth + LyricsColDef.ActualWidth;
|
||||
LyricsCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
switch (status.LyricsLayoutOrientation)
|
||||
{
|
||||
case LyricsLayoutOrientation.Horizontal:
|
||||
NowPlayingCanvas.LyricsStartX = LeftGapDef.ActualWidth + TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsStartY = 0;
|
||||
NowPlayingCanvas.LyricsWidth = LyricsColDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
LyricsCanvas.LyricsStartX = LeftGapDef.ActualWidth + TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth;
|
||||
LyricsCanvas.LyricsStartY = 0;
|
||||
LyricsCanvas.LyricsWidth = LyricsColDef.ActualWidth;
|
||||
LyricsCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
break;
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
NowPlayingCanvas.LyricsStartX = LeftGapDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsStartY = 0;
|
||||
NowPlayingCanvas.LyricsWidth = TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth + LyricsColDef.ActualWidth;
|
||||
NowPlayingCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
LyricsCanvas.LyricsStartX = LeftGapDef.ActualWidth;
|
||||
LyricsCanvas.LyricsStartY = 0;
|
||||
LyricsCanvas.LyricsWidth = TrackSummaryColDef.ActualWidth + MiddleGapColDef.ActualWidth + LyricsColDef.ActualWidth;
|
||||
LyricsCanvas.LyricsHeight = TrackSummaryRowDef.ActualHeight + MiddleGapRowDef.ActualHeight + LyricsRowDef.ActualHeight;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -342,20 +319,51 @@ namespace BetterLyrics.WinUI3.Views
|
||||
double height = RootGrid.ActualHeight;
|
||||
double width = RootGrid.ActualWidth;
|
||||
|
||||
double middleGapCol = 0;
|
||||
double minSize = Math.Min(width, height);
|
||||
|
||||
double gapBetweenTrackSummaryAndLyrics = 0;
|
||||
double gapBetweenAlbumArtAndSongInfo = 0;
|
||||
double trackSummaryRowHeight = 0;
|
||||
|
||||
double xMargin = 0;
|
||||
double yMargin = 0;
|
||||
|
||||
if (height < 400)
|
||||
switch (status.LyricsLayoutOrientation)
|
||||
{
|
||||
middleGapCol = Math.Max(16, height * 0.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
middleGapCol = Math.Max(16, width * 0.15);
|
||||
case LyricsLayoutOrientation.Horizontal:
|
||||
if (width < 800)
|
||||
{
|
||||
xMargin = Math.Clamp(minSize * 0.15, 16, 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
xMargin = Math.Clamp(minSize * 0.25, 16, 128);
|
||||
}
|
||||
yMargin = Math.Max(32, Math.Min(width, height) * 0.15);
|
||||
if (height < 100)
|
||||
{
|
||||
gapBetweenTrackSummaryAndLyrics = Math.Max(16, height * 0.1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gapBetweenTrackSummaryAndLyrics = 0;
|
||||
}
|
||||
TrackSummaryGridCol0.Width = new(1, GridUnitType.Star);
|
||||
TrackSummaryGridCol2.Width = new(xMargin);
|
||||
TrackSummaryGridRow1.Height =
|
||||
status.AlbumArtLayoutSettings.IsAutoCoverImageHeight ? new(1, GridUnitType.Star) : new(status.AlbumArtLayoutSettings.CoverImageHeight);
|
||||
break;
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
xMargin = Math.Max(16, minSize * 0.05);
|
||||
yMargin = Math.Max(16, minSize * 0.05);
|
||||
trackSummaryRowHeight =
|
||||
status.AlbumArtLayoutSettings.IsAutoCoverImageHeight ? Math.Max(64, minSize * 0.25) : status.AlbumArtLayoutSettings.CoverImageHeight;
|
||||
gapBetweenTrackSummaryAndLyrics = Math.Max(16, width * 0.15);
|
||||
TrackSummaryGridCol0.Width = new(1, GridUnitType.Auto);
|
||||
TrackSummaryGridCol2.Width = new(1, GridUnitType.Star);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (height < 100)
|
||||
@@ -367,22 +375,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
gapBetweenAlbumArtAndSongInfo = lyricsLayoutMetrics.SongTitleSize / 2;
|
||||
}
|
||||
|
||||
switch (status.LyricsLayoutOrientation)
|
||||
{
|
||||
case LyricsLayoutOrientation.Horizontal:
|
||||
xMargin = Math.Clamp(Math.Min(width, height) * 0.25, 16, 128);
|
||||
yMargin = Math.Max(32, Math.Min(width, height) * 0.15);
|
||||
break;
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
xMargin = Math.Max(16, Math.Min(width, height) * 0.05);
|
||||
yMargin = Math.Max(16, Math.Min(width, height) * 0.05);
|
||||
trackSummaryRowHeight = Math.Max(64, height * 0.25);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MiddleGapColDef.Width = new(middleGapCol);
|
||||
MiddleGapColDef.Width = new(gapBetweenTrackSummaryAndLyrics);
|
||||
|
||||
TrackSummaryGridRow0.Height = new(yMargin);
|
||||
TrackSummaryGridRow4.Height = new(yMargin);
|
||||
@@ -395,7 +388,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void OnLayoutChanged()
|
||||
{
|
||||
_timer.Debounce(() =>
|
||||
_layoutChangedTimer.Debounce(() =>
|
||||
{
|
||||
UpdateGap();
|
||||
|
||||
@@ -403,15 +396,11 @@ namespace BetterLyrics.WinUI3.Views
|
||||
UpdateLyricsOpacity();
|
||||
UpdateAlbumArtOpacity();
|
||||
|
||||
UpdateAlbumArtShadow();
|
||||
|
||||
UpdateTrackSummaryGridSpan();
|
||||
UpdateAlbumArtGridSpan();
|
||||
UpdateSongInfoStackPanelSpan();
|
||||
UpdateLyricsPlaceholderSpan();
|
||||
|
||||
UpdateAlbumArtCornerRadius();
|
||||
|
||||
UpdateLyricsLayout();
|
||||
}, Constants.Time.DebounceTimeout);
|
||||
}
|
||||
@@ -578,11 +567,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
SystemVolumeHook.MasterVolume = ViewModel.Volume;
|
||||
}
|
||||
|
||||
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Shadow.Receivers.Add(ShadowCastGrid);
|
||||
}
|
||||
|
||||
private void TitleAutoScrollHoverEffectView_PointerCanceled(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
TitleAutoScrollHoverEffectView.IsPlaying = false;
|
||||
@@ -643,45 +627,90 @@ namespace BetterLyrics.WinUI3.Views
|
||||
OnLayoutChanged();
|
||||
}
|
||||
|
||||
private void TrackSummaryGridContainer_Loaded(object sender, RoutedEventArgs e)
|
||||
private void LyricsScrollViewer_PointerWheelChanged(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
OnLayoutChanged();
|
||||
LyricsCanvas.IsMouseScrolling = true;
|
||||
|
||||
var pointerPoint = e.GetCurrentPoint(LyricsScrollViewer);
|
||||
int mouseWheelDelta = pointerPoint.Properties.MouseWheelDelta;
|
||||
|
||||
var value = LyricsCanvas.MouseScrollOffset + mouseWheelDelta;
|
||||
// 阻止向上滚动超过歌词最大边界
|
||||
if (value > 0)
|
||||
{
|
||||
value = Math.Min(-LyricsCanvas.CurrentCanvasYScroll, value);
|
||||
}
|
||||
// 阻止向下滚动超过歌词最大边界
|
||||
else
|
||||
{
|
||||
value = Math.Max(-LyricsCanvas.CurrentCanvasYScroll - LyricsCanvas.ActualLyricsHeight, value);
|
||||
}
|
||||
LyricsCanvas.MouseScrollOffset = value;
|
||||
|
||||
_scrollChangedTimer.Debounce(() =>
|
||||
{
|
||||
LyricsCanvas.MouseScrollOffset = 0;
|
||||
LyricsCanvas.IsMouseScrolling = false;
|
||||
}, TimeSpan.FromSeconds(3));
|
||||
}
|
||||
|
||||
private void ShadowCastGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
private void LyricsScrollViewer_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
UpdateAlbumArtCornerRadius();
|
||||
var pointerPoint = e.GetCurrentPoint(LyricsScrollViewer);
|
||||
|
||||
LyricsCanvas.MousePosition = pointerPoint.Position;
|
||||
}
|
||||
|
||||
private void LyricsScrollViewer_PointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
LyricsCanvas.IsMousePressing = false;
|
||||
_mediaSessionsService.ChangeLyricsLine(LyricsCanvas.CurrentHoveringLineIndex);
|
||||
}
|
||||
|
||||
private void LyricsScrollViewer_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
LyricsCanvas.IsMouseInLyricsArea = false;
|
||||
}
|
||||
|
||||
private void LyricsScrollViewer_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
LyricsCanvas.IsMouseInLyricsArea = true;
|
||||
}
|
||||
|
||||
private void LyricsScrollViewer_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
LyricsCanvas.IsMousePressing = true;
|
||||
}
|
||||
|
||||
// ====
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is AlbumArtLayoutSettings)
|
||||
if (message.Sender is AlbumArtAreaStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(AlbumArtLayoutSettings.SongInfoFontSize))
|
||||
if (message.PropertyName == nameof(AlbumArtAreaStyleSettings.SongInfoFontSize))
|
||||
{
|
||||
RenderSongInfo();
|
||||
}
|
||||
else if (message.PropertyName == nameof(AlbumArtLayoutSettings.CoverImageRadius))
|
||||
else if (message.PropertyName == nameof(AlbumArtAreaStyleSettings.CoverImageHeight))
|
||||
{
|
||||
UpdateAlbumArtCornerRadius();
|
||||
}
|
||||
else if (message.PropertyName == nameof(AlbumArtLayoutSettings.CoverImageShadowAmount))
|
||||
{
|
||||
UpdateAlbumArtShadow();
|
||||
OnLayoutChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is AlbumArtLayoutSettings)
|
||||
if (message.Sender is AlbumArtAreaStyleSettings)
|
||||
{
|
||||
if (message.PropertyName == nameof(AlbumArtLayoutSettings.IsAutoSongInfoFontSize))
|
||||
if (message.PropertyName == nameof(AlbumArtAreaStyleSettings.IsAutoSongInfoFontSize))
|
||||
{
|
||||
RenderSongInfo();
|
||||
}
|
||||
else if (message.PropertyName == nameof(AlbumArtAreaStyleSettings.IsAutoCoverImageHeight))
|
||||
{
|
||||
OnLayoutChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,28 +744,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
public async void Receive(PropertyChangedMessage<BitmapImage?> message)
|
||||
{
|
||||
if (message.Sender is IMediaSessionsService)
|
||||
{
|
||||
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtBitmapImage))
|
||||
{
|
||||
LastAlbumArtImage.Source = AlbumArtImage.Source;
|
||||
LastAlbumArtImage.Opacity = 1;
|
||||
await Task.Delay(Constants.Time.AnimationDuration);
|
||||
|
||||
AlbumArtImage.Opacity = 0;
|
||||
await Task.Delay(Constants.Time.AnimationDuration);
|
||||
AlbumArtImage.Source = message.NewValue;
|
||||
|
||||
LastAlbumArtImage.Opacity = 0;
|
||||
AlbumArtImage.Opacity = 1;
|
||||
|
||||
UpdateAlbumArtCornerRadius();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsLayoutOrientation> message)
|
||||
{
|
||||
if (message.Sender is LyricsWindowStatus)
|
||||
@@ -777,15 +784,10 @@ namespace BetterLyrics.WinUI3.Views
|
||||
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
|
||||
{
|
||||
OnLayoutChanged();
|
||||
RenderSongInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LyricsScrollViewer_PointerWheelChanged(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
var pointerPoint = e.GetCurrentPoint(LyricsScrollViewer);
|
||||
int mouseWheelDelta = pointerPoint.Properties.MouseWheelDelta;
|
||||
NowPlayingCanvas.LyricsStartY += mouseWheelDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Window
|
||||
x:Class="BetterLyrics.WinUI3.Views.LyricsWindow"
|
||||
x:Class="BetterLyrics.WinUI3.Views.NowPlayingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||
@@ -20,7 +20,7 @@
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Bottom" />
|
||||
|
||||
<local:LyricsPage />
|
||||
<local:NowPlayingPage />
|
||||
|
||||
<!-- Top command -->
|
||||
<Grid
|
||||
@@ -13,15 +13,15 @@ using WinUIEx.Messaging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class LyricsWindow : Window
|
||||
public sealed partial class NowPlayingWindow : Window
|
||||
{
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
|
||||
private readonly WindowMessageMonitor _wmm;
|
||||
|
||||
public LyricsWindowViewModel ViewModel { get; private set; } = Ioc.Default.GetRequiredService<LyricsWindowViewModel>();
|
||||
public NowPlayingWindowViewModel ViewModel { get; private set; } = Ioc.Default.GetRequiredService<NowPlayingWindowViewModel>();
|
||||
|
||||
public LyricsWindow()
|
||||
public NowPlayingWindow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHook.MinimizeWindow<LyricsWindow>();
|
||||
WindowHook.MinimizeWindow<NowPlayingWindow>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void LyricsWindowButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHook.OpenOrShowWindow<LyricsWindow>();
|
||||
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
|
||||
}
|
||||
|
||||
private void MusicGalleryButton_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
674
LICENSE
Normal file
674
LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
21
LICENSE.txt
21
LICENSE.txt
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Zhe Fang and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -20,7 +20,7 @@ BetterLyrics
|
||||
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
@@ -147,6 +147,10 @@ BetterLyrics
|
||||
|
||||
</details>
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
本项目采用 GNU 通用公共许可证 v3.0 授权。详情请参阅 [LICENSE](https://github.com/jayfunc/BetterLyrics/blob/dev/LICENSE) 文件。
|
||||
|
||||
## 💖 感谢
|
||||
|
||||
部分功能及代码引用或修改自公开资料库,包括但不限于下述开源项目/包、教程等,在此一并感谢。
|
||||
|
||||
@@ -20,7 +20,7 @@ BetterLyrics
|
||||
|
||||
<div align=center>
|
||||
|
||||
   
|
||||
   
|
||||
|
||||
</div>
|
||||
|
||||
@@ -153,6 +153,10 @@ You can donate via:
|
||||
|
||||
</details>
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the GNU General Public License v3.0. See the [LICENSE](https://github.com/jayfunc/BetterLyrics/blob/dev/LICENSE) file for details.
|
||||
|
||||
## 💖 Many thanks to
|
||||
|
||||
Some functions and code are referenced or modified from public repositories, including but not limited to the following open source projects/packages, tutorials, etc., and we would like to express our gratitude to them here.
|
||||
|
||||
Reference in New Issue
Block a user