mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
Compare commits
130 Commits
v1.0.14.0-
...
v1.0.39.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
010040b376 | ||
|
|
35272d8324 | ||
|
|
29a714fe87 | ||
|
|
78edc2b3ce | ||
|
|
932f9d3a4f | ||
|
|
a56a7af08c | ||
|
|
5427c1992f | ||
|
|
64d69f44c3 | ||
|
|
d75fe4b27a | ||
|
|
eac9b3e28a | ||
|
|
d6975ba2d4 | ||
|
|
aae8e1322a | ||
|
|
88aac0eaf0 | ||
|
|
c463d9bc5c | ||
|
|
f305b469f2 | ||
|
|
ea276f3e2e | ||
|
|
15601e6ee4 | ||
|
|
44b14cab17 | ||
|
|
0a97c56c77 | ||
|
|
ac5dc5991f | ||
|
|
d22dc673e8 | ||
|
|
9eb2b83796 | ||
|
|
1cf6226a06 | ||
|
|
bce330a9e0 | ||
|
|
687e286c2e | ||
|
|
fc05035053 | ||
|
|
f45f5ead01 | ||
|
|
0eeea77896 | ||
|
|
78ca7d8435 | ||
|
|
98d2ac404d | ||
|
|
b4b1ffd58e | ||
|
|
2522bd00ab | ||
|
|
0601039fcf | ||
|
|
857b95e525 | ||
|
|
4be855f11a | ||
|
|
9e1018770d | ||
|
|
1235a09d19 | ||
|
|
1647c3a2f1 | ||
|
|
588838acaa | ||
|
|
25c772434c | ||
|
|
2c597a3b37 | ||
|
|
80a44977a6 | ||
|
|
0389fa6a56 | ||
|
|
30ca476d8d | ||
|
|
6439dee5ef | ||
|
|
f6e5a24fe4 | ||
|
|
77aad546bc | ||
|
|
7fdbe664ba | ||
|
|
df76074ce9 | ||
|
|
31939630a3 | ||
|
|
50500626f8 | ||
|
|
fb1f0c8fc7 | ||
|
|
9036b3be5f | ||
|
|
51f840c0ef | ||
|
|
b3a98cdaa2 | ||
|
|
7799a8dd94 | ||
|
|
e84d1faf71 | ||
|
|
66f0fd0f8f | ||
|
|
4f0dbe4836 | ||
|
|
d7a53e360a | ||
|
|
cb76341666 | ||
|
|
537584e557 | ||
|
|
e06a50d8e8 | ||
|
|
01776ed9a8 | ||
|
|
1b7f53c055 | ||
|
|
3233d35a05 | ||
|
|
ee52dafa0b | ||
|
|
03ddf42152 | ||
|
|
bb4ee6bae7 | ||
|
|
cb5a9f8042 | ||
|
|
1d5d3bfa72 | ||
|
|
06379ebaba | ||
|
|
45eb2a1506 | ||
|
|
0e96c35d2d | ||
|
|
152ecc7ff0 | ||
|
|
8b38aa1e33 | ||
|
|
79e6995384 | ||
|
|
1d6e19a718 | ||
|
|
23cb4b11a9 | ||
|
|
11461a615b | ||
|
|
e8f0359fb2 | ||
|
|
eba81e8be3 | ||
|
|
2e0d437b08 | ||
|
|
cbb0b87392 | ||
|
|
3f63043150 | ||
|
|
77f2474562 | ||
|
|
8aa5c392df | ||
|
|
a453f4862d | ||
|
|
0e6702f3c3 | ||
|
|
412cdbdaf4 | ||
|
|
02dbbf983c | ||
|
|
8cf9830644 | ||
|
|
b04dfedf7e | ||
|
|
3d4e3209e6 | ||
|
|
e92130af85 | ||
|
|
de3d6f1695 | ||
|
|
da377838e8 | ||
|
|
67cf6e47c8 | ||
|
|
abf4c3498f | ||
|
|
ff8c85b2d0 | ||
|
|
8cbdb32931 | ||
|
|
757f9f4156 | ||
|
|
c632f4b01a | ||
|
|
a843d0a0e3 | ||
|
|
905df92b05 | ||
|
|
7445299537 | ||
|
|
ba4958f837 | ||
|
|
8ca5245bf5 | ||
|
|
89aa97552a | ||
|
|
aa692e2735 | ||
|
|
c7ee26f284 | ||
|
|
b103e6efd1 | ||
|
|
16cd12e22b | ||
|
|
34bdbc89bc | ||
|
|
b649e9761d | ||
|
|
a9807f4f09 | ||
|
|
def287715d | ||
|
|
966f926112 | ||
|
|
4568293b51 | ||
|
|
10115ab0a8 | ||
|
|
ecefaedcb9 | ||
|
|
b9aae0866d | ||
|
|
afbfcc921e | ||
|
|
8f997ca3d9 | ||
|
|
042d557eb1 | ||
|
|
153679228d | ||
|
|
5a4fe54ac2 | ||
|
|
14e8d88cd1 | ||
|
|
79e726db33 | ||
|
|
d4f1949833 |
18
.github/workflows/issues-translator.yml
vendored
Normal file
18
.github/workflows/issues-translator.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: 'issue-translator'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: false
|
||||
# not require, default false, . Decide whether to modify the issue title
|
||||
# if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
|
||||
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
|
||||
# not require. Customize the translation robot prefix message.
|
||||
27
.github/workflows/release-to-telegram.yml
vendored
Normal file
27
.github/workflows/release-to-telegram.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Notify Telegram on GitHub Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send release info to Telegram
|
||||
env:
|
||||
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||
CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
RELEASE_URL: ${{ github.event.release.html_url }}
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
RELEASE_NAME: ${{ github.event.release.name }}
|
||||
RELEASE_BODY: ${{ github.event.release.body }}
|
||||
run: |
|
||||
TEXT="🚀 *New Release:* ${RELEASE_TAG} - ${RELEASE_NAME}%0A"
|
||||
TEXT+="📝 *Description:*%0A${RELEASE_BODY}%0A"
|
||||
TEXT+="🔗 [View on GitHub](${RELEASE_URL})"
|
||||
|
||||
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
|
||||
-d chat_id="${CHAT_ID}" \
|
||||
-d text="${TEXT}" \
|
||||
-d parse_mode=Markdown
|
||||
6
.github/workflows/releases-to-discord.yml
vendored
6
.github/workflows/releases-to-discord.yml
vendored
@@ -1,3 +1,5 @@
|
||||
name: Notify Discord on GitHub Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
@@ -13,8 +15,8 @@ jobs:
|
||||
with:
|
||||
webhook_url: ${{ secrets.WEBHOOK_URL }}
|
||||
color: "2105893"
|
||||
username: "Release Changelog"
|
||||
avatar_url: "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png"
|
||||
username: "GitHub"
|
||||
avatar_url: "https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png"
|
||||
content: "||@everyone||"
|
||||
footer_title: "Changelog"
|
||||
reduce_headings: true
|
||||
|
||||
@@ -142,8 +142,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||
</Project>
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.14.0" />
|
||||
Version="1.0.39.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.SettingsControls/SettingsExpander/SettingsExpander.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.Segmented/Segmented/Segmented.xaml" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
@@ -47,7 +48,9 @@
|
||||
<converter:IntToCornerRadius x:Key="IntToCornerRadius" />
|
||||
<converter:CornerRadiusToDoubleConverter x:Key="CornerRadiusToDoubleConverter" />
|
||||
<converter:LyricsSearchProviderToDisplayNameConverter x:Key="LyricsSearchProviderToDisplayNameConverter" />
|
||||
<converter:TranslationSearchProviderToDisplayNameConverter x:Key="TranslationSearchProviderToDisplayNameConverter" />
|
||||
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
|
||||
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<converters:ColorToDisplayNameConverter x:Key="ColorToDisplayNameConverter" />
|
||||
@@ -82,7 +85,7 @@
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="10,0" />
|
||||
<Setter Property="Padding" Value="16,9,16,11" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">
|
||||
@@ -99,6 +102,196 @@
|
||||
<Setter Property="CornerRadius" Value="6" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GhostSliderStyle" TargetType="Slider">
|
||||
<Setter Property="Background" Value="{ThemeResource ControlStrokeColorOnAccentDefaultBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource SliderBorderThemeThickness}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TextFillColorPrimaryBrush}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="ManipulationMode" Value="None" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
|
||||
<Setter Property="FocusVisualMargin" Value="-7,0,-7,0" />
|
||||
<Setter Property="IsFocusEngagementEnabled" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Slider">
|
||||
<Grid Margin="{TemplateBinding Padding}">
|
||||
<Grid.Resources>
|
||||
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Background" Value="{ThemeResource TextFillColorPrimaryBrush}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Border
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="0,1,1,0" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Grid.Resources>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ContentPresenter
|
||||
x:Name="HeaderContentPresenter"
|
||||
Grid.Row="0"
|
||||
Margin="{ThemeResource SliderTopHeaderMargin}"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
FontWeight="{ThemeResource SliderHeaderThemeFontWeight}"
|
||||
Foreground="{ThemeResource SliderHeaderForeground}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="Collapsed" />
|
||||
<Grid
|
||||
x:Name="SliderContainer"
|
||||
Grid.Row="1"
|
||||
Background="{ThemeResource SliderContainerBackground}"
|
||||
Control.IsTemplateFocusTarget="True">
|
||||
<Grid x:Name="HorizontalTemplate" MinHeight="{ThemeResource SliderHorizontalHeight}">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="{ThemeResource SliderPreContentMargin}" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="{ThemeResource SliderPostContentMargin}" />
|
||||
</Grid.RowDefinitions>
|
||||
<Rectangle
|
||||
x:Name="HorizontalTrackRect"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="3"
|
||||
Height="2"
|
||||
Fill="{TemplateBinding Background}" />
|
||||
<Rectangle
|
||||
x:Name="HorizontalDecreaseRect"
|
||||
Grid.Row="1"
|
||||
Fill="{TemplateBinding Foreground}" />
|
||||
<TickBar
|
||||
x:Name="TopTickBar"
|
||||
Grid.ColumnSpan="3"
|
||||
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||
Margin="0,0,0,4"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource SliderTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<TickBar
|
||||
x:Name="HorizontalInlineTickBar"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="3"
|
||||
Height="2"
|
||||
Fill="{ThemeResource SliderInlineTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<TickBar
|
||||
x:Name="BottomTickBar"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
Height="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||
Margin="0,4,0,0"
|
||||
VerticalAlignment="Top"
|
||||
Fill="{ThemeResource SliderTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<Thumb
|
||||
x:Name="HorizontalThumb"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="1"
|
||||
Width="2"
|
||||
Height="2"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
DataContext="{TemplateBinding Value}"
|
||||
FocusVisualMargin="-14,-6,-14,-6"
|
||||
Style="{StaticResource SliderThumbStyle}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="VerticalTemplate"
|
||||
MinWidth="{ThemeResource SliderVerticalWidth}"
|
||||
Visibility="Collapsed">
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{ThemeResource SliderPreContentMargin}" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="{ThemeResource SliderPostContentMargin}" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
x:Name="VerticalTrackRect"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="1"
|
||||
Width="{ThemeResource SliderTrackThemeHeight}"
|
||||
Fill="{TemplateBinding Background}" />
|
||||
<Rectangle
|
||||
x:Name="VerticalDecreaseRect"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Fill="{TemplateBinding Foreground}" />
|
||||
<TickBar
|
||||
x:Name="LeftTickBar"
|
||||
Grid.RowSpan="3"
|
||||
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||
Margin="0,0,4,0"
|
||||
HorizontalAlignment="Right"
|
||||
Fill="{ThemeResource SliderTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<TickBar
|
||||
x:Name="VerticalInlineTickBar"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="1"
|
||||
Width="{ThemeResource SliderTrackThemeHeight}"
|
||||
Fill="{ThemeResource SliderInlineTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<TickBar
|
||||
x:Name="RightTickBar"
|
||||
Grid.RowSpan="3"
|
||||
Grid.Column="2"
|
||||
Width="{ThemeResource SliderOutsideTickBarThemeHeight}"
|
||||
Margin="4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Fill="{ThemeResource SliderTickBarFill}"
|
||||
Visibility="Collapsed" />
|
||||
<Thumb
|
||||
x:Name="VerticalThumb"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Width="24"
|
||||
Height="8"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
DataContext="{TemplateBinding Value}"
|
||||
FocusVisualMargin="-6,-14,-6,-14"
|
||||
Style="{StaticResource SliderThumbStyle}" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ListViewStretchedItemContainerStyle" TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
|
||||
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
|
||||
@@ -10,13 +10,17 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
using Serilog;
|
||||
using ShadowViewer.Controls;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterLyrics.WinUI3
|
||||
{
|
||||
@@ -33,6 +37,8 @@ namespace BetterLyrics.WinUI3
|
||||
public NotificationPanel? LyricsWindowNotificationPanel { get; set; }
|
||||
public NotificationPanel? SettingsWindowNotificationPanel { get; set; }
|
||||
|
||||
private static Mutex? _instanceMutex;
|
||||
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -41,6 +47,8 @@ namespace BetterLyrics.WinUI3
|
||||
DispatcherQueueTimer = DispatcherQueue.CreateTimer();
|
||||
ResourceLoader = new ResourceLoader();
|
||||
|
||||
EnsureSingleInstance();
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
PathHelper.EnsureDirectories();
|
||||
ConfigureServices();
|
||||
@@ -53,14 +61,28 @@ namespace BetterLyrics.WinUI3
|
||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
}
|
||||
|
||||
private void EnsureSingleInstance()
|
||||
{
|
||||
bool createdNew;
|
||||
_instanceMutex = new Mutex(true, MetadataHelper.AppName, out createdNew);
|
||||
|
||||
if (!createdNew)
|
||||
{
|
||||
User32.MessageBox(HWND.NULL, ResourceLoader!.GetString("TryRunMultipleInstance"), null, User32.MB_FLAGS.MB_APPLMODAL);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<LyricsWindow>();
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow == null) return;
|
||||
WindowHelper.OpenWindow<LyricsWindow>();
|
||||
|
||||
lyricsWindow.ViewModel.InitLockHotKey();
|
||||
lyricsWindow.AutoSelectLyricsMode();
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow != null)
|
||||
{
|
||||
lyricsWindow.ViewModel.InitLockHotKey();
|
||||
lyricsWindow.AutoSelectLyricsMode();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ConfigureServices()
|
||||
@@ -85,13 +107,13 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ITranslateService, TranslateService>()
|
||||
// Manager
|
||||
// ViewModels
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
.AddSingleton<SystemTrayViewModel>()
|
||||
.AddSingleton<SettingsPageViewModel>()
|
||||
.AddSingleton<LyricsPageViewModel>()
|
||||
.AddSingleton<MusicGalleryViewModel>()
|
||||
.AddSingleton<LyricsRendererViewModel>()
|
||||
.BuildServiceProvider()
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/EmptyBox.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/EmptyBox.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/EmptyState.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/EmptyState.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Telegram.png
Normal file
BIN
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Telegram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
328503
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Wiki82.profile.xml
Normal file
328503
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Wiki82.profile.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="ViewModels\Lyrics\**" />
|
||||
@@ -19,9 +20,11 @@
|
||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Core14.profile.xml" />
|
||||
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Assets\Wiki82.profile.xml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Views\MusicGalleryPage.xaml" />
|
||||
<None Remove="Views\MusicGalleryWindow.xaml" />
|
||||
<None Remove="Views\SettingsWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -32,13 +35,17 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="3v.EvtSource" Version="2.0.0" />
|
||||
<PackageReference Include="ColorThief.ImageSharp" Version="1.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250513-build.2126" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250703-build.2173" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.Shimmer" Version="0.1.250703-build.2173" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.MetadataControl" 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.SettingsControls" 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" />
|
||||
@@ -46,27 +53,30 @@
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.3-dev-02320" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.6" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.6" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.7" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.7" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="TinyPinyin.Net" Version="1.0.2" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="Vanara.PInvoke.CoreAudio" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.1.6" />
|
||||
<PackageReference Include="WinUIEx" Version="2.6.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.0.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\InAppLyricsRenderer.xaml">
|
||||
@@ -83,9 +93,43 @@
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Discord.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EmptyBox.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EmptyState.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.ico">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Logo.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\QQ.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Segoe Fluent Icons.ttf">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Telegram.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Wiki82.profile.xml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\MusicGalleryWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\MusicGalleryPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\SettingsWindow.xaml">
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
x:Name="TrayIcon"
|
||||
x:FieldModifier="public"
|
||||
ContextMenuMode="SecondWindow"
|
||||
DoubleClickCommand="{x:Bind ViewModel.OpenLyricsWindowCommand}"
|
||||
IconSource="ms-appx:///Assets/Logo.ico"
|
||||
LeftClickCommand="{x:Bind ViewModel.OpenLyricsWindowCommand}"
|
||||
NoLeftClickDelay="True"
|
||||
ToolTipText="{x:Bind ViewModel.ToolTipText, Mode=OneWay}">
|
||||
<tb:TaskbarIcon.ContextFlyout>
|
||||
@@ -22,7 +24,10 @@
|
||||
AreOpenCloseAnimationsEnabled="True"
|
||||
LightDismissOverlayMode="On"
|
||||
ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||
<MenuFlyoutItem x:Uid="SystemTrayMusicGallery" Command="{x:Bind ViewModel.OpenMusicGalleryCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SystemTraySettings" Command="{x:Bind ViewModel.OpenSettingsCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SystemTrayResetWindowPosition" Command="{x:Bind ViewModel.ResetWindowPositionCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SystemTrayRestart" Command="{x:Bind ViewModel.RestartAppCommand}" />
|
||||
<MenuFlyoutItem x:Uid="SystemTrayExit" Command="{x:Bind ViewModel.ExitAppCommand}" />
|
||||
<MenuFlyoutItem
|
||||
x:Uid="SystemTrayUnlock"
|
||||
|
||||
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
|
||||
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
internal partial class CornerRadiusToDoubleConverter : IValueConverter
|
||||
public partial class CornerRadiusToDoubleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
internal partial class EnumToIntConverter : IValueConverter
|
||||
public partial class EnumToIntConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
|
||||
@@ -14,19 +14,19 @@ namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => App.ResourceLoader!.GetString("LyricsSearchProviderLrcLib"),
|
||||
LyricsSearchProvider.QQ => App.ResourceLoader!.GetString("LyricsSearchProviderQQ"),
|
||||
LyricsSearchProvider.Netease => App.ResourceLoader!.GetString("LyricsSearchProviderNetease"),
|
||||
LyricsSearchProvider.Kugou => App.ResourceLoader!.GetString("LyricsSearchProviderKugou"),
|
||||
LyricsSearchProvider.AmllTtmlDb => App.ResourceLoader!.GetString("LyricsSearchProviderAmllTtmlDb"),
|
||||
LyricsSearchProvider.LrcLib => "LrcLib",
|
||||
LyricsSearchProvider.QQ => "QQ",
|
||||
LyricsSearchProvider.Netease => "Netease",
|
||||
LyricsSearchProvider.Kugou => "Kugou",
|
||||
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||
LyricsSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
|
||||
LyricsSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
|
||||
LyricsSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),
|
||||
LyricsSearchProvider.LocalTtmlFile => App.ResourceLoader!.GetString("LyricsSearchProviderTtmlFile"),
|
||||
_ => "",
|
||||
_ => "N/A",
|
||||
};
|
||||
}
|
||||
return "";
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class SecondsToFormattedTimeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is double seconds)
|
||||
{
|
||||
return TimeSpan.FromSeconds(seconds).ToString(@"mm\:ss");
|
||||
}
|
||||
else if (value is int secondsInt)
|
||||
{
|
||||
return TimeSpan.FromSeconds(secondsInt).ToString(@"mm\:ss");
|
||||
}
|
||||
return value?.ToString() ?? "";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class TranslationSearchProviderToDisplayNameConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is TranslationSearchProvider provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
TranslationSearchProvider.LrcLib => "LrcLib",
|
||||
TranslationSearchProvider.QQ => "QQ",
|
||||
TranslationSearchProvider.Netease => "Netease",
|
||||
TranslationSearchProvider.Kugou => "Kugou",
|
||||
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
|
||||
TranslationSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
|
||||
TranslationSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
|
||||
TranslationSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),
|
||||
TranslationSearchProvider.LocalTtmlFile => App.ResourceLoader!.GetString("LyricsSearchProviderTtmlFile"),
|
||||
TranslationSearchProvider.LibreTranslate => "LibreTranslate",
|
||||
_ => "N/A",
|
||||
};
|
||||
}
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum CommonSongProperty
|
||||
{
|
||||
Title,
|
||||
Album,
|
||||
Artist
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsLayoutOrientation
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
}
|
||||
@@ -61,5 +61,22 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
return !provider.IsLocal();
|
||||
}
|
||||
|
||||
public static TranslationSearchProvider? ToTranslationSearchProvider(this LyricsSearchProvider? provider)
|
||||
{
|
||||
return provider switch
|
||||
{
|
||||
LyricsSearchProvider.LrcLib => TranslationSearchProvider.LrcLib,
|
||||
LyricsSearchProvider.QQ => TranslationSearchProvider.QQ,
|
||||
LyricsSearchProvider.Kugou => TranslationSearchProvider.Kugou,
|
||||
LyricsSearchProvider.Netease => TranslationSearchProvider.Netease,
|
||||
LyricsSearchProvider.AmllTtmlDb => TranslationSearchProvider.AmllTtmlDb,
|
||||
LyricsSearchProvider.LocalMusicFile => TranslationSearchProvider.LocalMusicFile,
|
||||
LyricsSearchProvider.LocalLrcFile => TranslationSearchProvider.LocalLrcFile,
|
||||
LyricsSearchProvider.LocalEslrcFile => TranslationSearchProvider.LocalEslrcFile,
|
||||
LyricsSearchProvider.LocalTtmlFile => TranslationSearchProvider.LocalTtmlFile,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum PlaybackOrder
|
||||
{
|
||||
RepeatAll,
|
||||
RepeatOne,
|
||||
Shuffle,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum TranslationSearchProvider
|
||||
{
|
||||
QQ,
|
||||
Kugou,
|
||||
Netease,
|
||||
LrcLib,
|
||||
AmllTtmlDb,
|
||||
LocalMusicFile,
|
||||
LocalLrcFile,
|
||||
LocalEslrcFile,
|
||||
LocalTtmlFile,
|
||||
LibreTranslate,
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,10 @@ using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class AlbumArtChangedEventArgs : EventArgs
|
||||
public class AlbumArtChangedEventArgs(SoftwareBitmap? albumArtSwBitmap, Color? albumArtLightAccentColor, Color? albumArtDarkAccentColor) : EventArgs
|
||||
{
|
||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = null;
|
||||
public Color? AlbumArtAccentColor { get; set; } = null;
|
||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = albumArtSwBitmap;
|
||||
public Color? AlbumArtLightAccentColor { get; set; } = albumArtLightAccentColor;
|
||||
public Color? AlbumArtDarkAccentColor { get; set; } = albumArtDarkAccentColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class PositionChangedEventArgs(TimeSpan position) : EventArgs()
|
||||
public class TimelineChangedEventArgs(TimeSpan position, TimeSpan end) : EventArgs()
|
||||
{
|
||||
public TimeSpan Position { get; set; } = position;
|
||||
public TimeSpan End { get; set; } = end;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.UI.Windowing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class AppWindowHelper
|
||||
{
|
||||
public static void SetIcons(this AppWindow appWindow)
|
||||
{
|
||||
appWindow.SetIcon(PathHelper.LogoPath);
|
||||
appWindow.SetTaskbarIcon(PathHelper.LogoPath);
|
||||
appWindow.SetTitleBarIcon(PathHelper.LogoPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class CollectionHelper
|
||||
{
|
||||
public static ObservableCollection<GroupInfoList> GetGroupedBy<T>(
|
||||
this IEnumerable<T> items,
|
||||
Func<T, object> groupKeySelector,
|
||||
Func<object, object>? orderSelector = null)
|
||||
{
|
||||
var query = from item in items
|
||||
group item by groupKeySelector(item) into g
|
||||
orderby g.Key
|
||||
select new GroupInfoList(g.Cast<object>(), orderSelector) { Key = g.Key };
|
||||
|
||||
return new ObservableCollection<GroupInfoList>(query);
|
||||
}
|
||||
|
||||
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
|
||||
{
|
||||
if (collection == null) return;
|
||||
if (items == null) return;
|
||||
foreach (var item in items)
|
||||
{
|
||||
collection.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InsertRange<T>(this IList<T> list, int index, IEnumerable<T> items)
|
||||
{
|
||||
if (list == null) return;
|
||||
if (items == null) return;
|
||||
if (index < 0 || index > list.Count) return;
|
||||
foreach (var item in items)
|
||||
{
|
||||
list.Insert(index++, item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -106,25 +106,25 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, brightness);
|
||||
}
|
||||
|
||||
public static System.Drawing.Color GetAccentColor(IntPtr myHwnd, WindowPixelSampleMode mode)
|
||||
public static System.Drawing.Color GetAccentColor(IntPtr myHwnd, string monitorDeviceName, WindowPixelSampleMode mode)
|
||||
{
|
||||
if (!User32.GetWindowRect(myHwnd, out RECT myRect)) return System.Drawing.Color.Transparent;
|
||||
|
||||
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(monitorDeviceName);
|
||||
int screenWidth = monitorInfo.rcMonitor.Width;
|
||||
switch (mode)
|
||||
{
|
||||
case WindowPixelSampleMode.BelowWindow:
|
||||
{
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int sampleHeight = 1;
|
||||
int sampleY = myRect.Bottom + 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
return GetAverageColorFromScreenRegion(myRect.Left, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
case WindowPixelSampleMode.AboveWindow:
|
||||
{
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int sampleHeight = 1;
|
||||
int sampleY = myRect.Top - 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
return GetAverageColorFromScreenRegion(myRect.Left, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
case WindowPixelSampleMode.WindowArea:
|
||||
{
|
||||
|
||||
@@ -19,10 +19,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
private static readonly Dictionary<IntPtr, bool> _originalTopmostStates = [];
|
||||
private static readonly Dictionary<IntPtr, nint> _oldWndProcs = new();
|
||||
private static readonly Dictionary<IntPtr, (double X, double Y, double Width, double Height)> _originalWindowBounds = [];
|
||||
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyles = [];
|
||||
private static List<Rectangle> _interactiveRects = new();
|
||||
|
||||
private delegate nint WndProcDelegate(nint hWnd, uint msg, nint wParam, nint lParam);
|
||||
|
||||
@@ -77,7 +75,12 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
// <20><><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD><D0A1>λ<EFBFBD><CEBB>
|
||||
window.AppWindow.MoveAndResize(
|
||||
new Windows.Graphics.RectInt32(targetX, targetY, targetWidth, targetHeight)
|
||||
new Windows.Graphics.RectInt32(
|
||||
targetX,
|
||||
targetY,
|
||||
targetWidth,
|
||||
targetHeight
|
||||
)
|
||||
);
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ԭTopMost״̬
|
||||
@@ -103,16 +106,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
|
||||
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle | (int)User32.WindowStylesEx.WS_EX_TRANSPARENT | (int)User32.WindowStylesEx.WS_EX_LAYERED);
|
||||
|
||||
//// <20><>װ<EFBFBD>Զ<EFBFBD><D4B6><EFBFBD>WndProc
|
||||
//if (!_oldWndProcs.ContainsKey(hwnd))
|
||||
//{
|
||||
// nint newWndProc = Marshal.GetFunctionPointerForDelegate((WndProcDelegate)((hWnd, msg, wParam, lParam) =>
|
||||
// CustomWndProc(hWnd, msg, wParam, lParam, hwnd)
|
||||
// ));
|
||||
// nint oldWndProc = User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWLP_WNDPROC, newWndProc);
|
||||
// _oldWndProcs[hwnd] = oldWndProc;
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -123,51 +116,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
window.SetWindowStyle(style);
|
||||
_originalWindowStyles.Remove(hwnd);
|
||||
}
|
||||
|
||||
//// <20>ָ<EFBFBD>ԭWndProc
|
||||
//if (_oldWndProcs.TryGetValue(hwnd, out var oldWndProc))
|
||||
//{
|
||||
// User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWLP_WNDPROC, oldWndProc);
|
||||
// _oldWndProcs.Remove(hwnd);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private static nint CustomWndProc(nint hWnd, uint msg, nint wParam, nint lParam, IntPtr hwnd)
|
||||
{
|
||||
const int WM_NCHITTEST = 0x84;
|
||||
const int HTCLIENT = 1;
|
||||
const int HTTRANSPARENT = -1;
|
||||
|
||||
if (msg == WM_NCHITTEST)
|
||||
{
|
||||
int x = (short)(lParam.ToInt32() & 0xFFFF);
|
||||
int y = (short)((lParam.ToInt32() >> 16) & 0xFFFF);
|
||||
|
||||
// תΪ<D7AA><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
POINT pt = new() { x = x, y = y };
|
||||
User32.ScreenToClient(hWnd, ref pt);
|
||||
|
||||
foreach (var rect in _interactiveRects)
|
||||
{
|
||||
if (rect.Contains(pt.x, pt.y))
|
||||
return HTCLIENT;
|
||||
}
|
||||
return HTTRANSPARENT;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ԭWndProc
|
||||
if (_oldWndProcs.TryGetValue(hwnd, out var oldWndProc))
|
||||
{
|
||||
return User32.CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
return User32.DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
public static void SetInteractiveRects(IEnumerable<Rectangle> rects)
|
||||
{
|
||||
_interactiveRects = rects.ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
@@ -16,24 +18,29 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class DockModeHelper
|
||||
{
|
||||
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
private static readonly HashSet<IntPtr> _registered = [];
|
||||
private static readonly Dictionary<IntPtr, RECT> _originalPositions = [];
|
||||
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyle = [];
|
||||
|
||||
public static void Disable(Window window)
|
||||
{
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (!_registered.Contains(hwnd)) return;
|
||||
|
||||
window.SetIsShownInSwitchers(true);
|
||||
window.ExtendsContentIntoTitleBar = true;
|
||||
window.SetIsAlwaysOnTop(false);
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
UnregisterAppBar(hwnd);
|
||||
RefreshWorkArea();
|
||||
|
||||
window.SetWindowStyle(_originalWindowStyle[hwnd]);
|
||||
_originalWindowStyle.Remove(hwnd);
|
||||
|
||||
window.ExtendsContentIntoTitleBar = true;
|
||||
|
||||
if (_originalPositions.TryGetValue(hwnd, out var rect))
|
||||
{
|
||||
User32.SetWindowPos(
|
||||
@@ -49,10 +56,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void Enable(Window window, int appBarHeight, DockPlacement dockPlacement)
|
||||
public static void Enable(Window window, string monitorDeviceName, int appBarHeight, DockPlacement dockPlacement)
|
||||
{
|
||||
window.SetIsShownInSwitchers(false);
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
window.SetIsAlwaysOnTop(true);
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
@@ -61,7 +67,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
_originalWindowStyle[hwnd] = window.GetWindowStyle();
|
||||
}
|
||||
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
|
||||
|
||||
if (!_originalPositions.ContainsKey(hwnd))
|
||||
{
|
||||
@@ -71,32 +76,38 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
RegisterAppBar(hwnd, appBarHeight, dockPlacement);
|
||||
RegisterAppBar(hwnd, monitorDeviceName, appBarHeight, dockPlacement);
|
||||
|
||||
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(_settingsService.DockMonitorDeviceName);
|
||||
|
||||
int screenWidth = monitorInfo.rcMonitor.Width;
|
||||
int screenHeight = monitorInfo.rcMonitor.Bottom - monitorInfo.rcMonitor.Top;
|
||||
int y = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top : monitorInfo.rcMonitor.Bottom - appBarHeight;
|
||||
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int y = dockPlacement == DockPlacement.Top ? 0 : screenHeight - appBarHeight;
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
monitorInfo.rcMonitor.Left,
|
||||
y,
|
||||
screenWidth,
|
||||
appBarHeight,
|
||||
User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
User32.SetWindowPosFlags.SWP_HIDEWINDOW
|
||||
);
|
||||
|
||||
RefreshWorkArea();
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
window.ToggleWindowStyle(true, WindowStyle.Popup);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private static void RegisterAppBar(IntPtr hwnd, int height, DockPlacement dockPlacement)
|
||||
private static void RegisterAppBar(IntPtr hwnd, string monitorDeviceName, int height, DockPlacement dockPlacement)
|
||||
{
|
||||
if (_registered.Contains(hwnd)) return;
|
||||
|
||||
var uEdge = dockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int top = dockPlacement == DockPlacement.Top ? 0 : screenHeight - height;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? height : screenHeight;
|
||||
|
||||
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(monitorDeviceName);
|
||||
|
||||
int top = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top : monitorInfo.rcMonitor.Bottom - height;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top + height : monitorInfo.rcMonitor.Bottom;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
@@ -105,14 +116,15 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = 0,
|
||||
Left = monitorInfo.rcMonitor.Left,
|
||||
Top = top,
|
||||
Right = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
Right = monitorInfo.rcMonitor.Right,
|
||||
Bottom = bottom,
|
||||
},
|
||||
};
|
||||
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_NEW, ref abd);
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_QUERYPOS, ref abd);
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
|
||||
_registered.Add(hwnd);
|
||||
@@ -130,16 +142,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
};
|
||||
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_REMOVE, ref abd);
|
||||
|
||||
|
||||
_registered.Remove(hwnd);
|
||||
}
|
||||
|
||||
private static void RefreshWorkArea()
|
||||
{
|
||||
User32.SendMessage(HWND.HWND_BROADCAST, User32.WindowMessage.WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
|
||||
public static void UpdateAppBarHeight(IntPtr hwnd, int newHeight, DockPlacement dockPlacement)
|
||||
public static void UpdateAppBarHeight(IntPtr hwnd, string monitorDeviceName, int newHeight, DockPlacement dockPlacement)
|
||||
{
|
||||
App.DispatcherQueueTimer?.Debounce(() =>
|
||||
{
|
||||
@@ -147,9 +154,12 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return;
|
||||
|
||||
var uEdge = dockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int top = dockPlacement == DockPlacement.Top ? 0 : screenHeight - newHeight;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? newHeight : screenHeight;
|
||||
|
||||
var monitorInfo = MonitorHelper.GetMonitorInfoExFromDeviceName(monitorDeviceName);
|
||||
|
||||
int screenWidth = monitorInfo.rcMonitor.Width;
|
||||
int top = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top : monitorInfo.rcMonitor.Bottom - newHeight;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top + newHeight : monitorInfo.rcMonitor.Bottom;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
@@ -158,28 +168,32 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = 0,
|
||||
Left = monitorInfo.rcMonitor.Left,
|
||||
Top = top,
|
||||
Right = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
Right = monitorInfo.rcMonitor.Right,
|
||||
Bottom = bottom,
|
||||
},
|
||||
};
|
||||
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_QUERYPOS, ref abd);
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
|
||||
// 同步窗口实际高度和位置
|
||||
int y = dockPlacement == DockPlacement.Top ? 0 : screenHeight - newHeight;
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
y,
|
||||
User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
newHeight,
|
||||
User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
);
|
||||
|
||||
RefreshWorkArea();
|
||||
int y = dockPlacement == DockPlacement.Top ? monitorInfo.rcMonitor.Top : monitorInfo.rcMonitor.Bottom - newHeight;
|
||||
int repeatCount = 2;
|
||||
while (repeatCount > 0)
|
||||
{
|
||||
repeatCount--;
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
monitorInfo.rcMonitor.Left,
|
||||
y,
|
||||
screenWidth,
|
||||
newHeight,
|
||||
newHeight == 0 ? User32.SetWindowPosFlags.SWP_HIDEWINDOW : User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
);
|
||||
}
|
||||
}, TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
20
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs
Normal file
20
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/FontHelper.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class FontHelper
|
||||
{
|
||||
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies();
|
||||
|
||||
public static string GetUserPreferredFontFamily() => SystemFontFamilies.ElementAtOrDefault(_settingsService.SelectedFontFamilyIndex) ?? "Segoe UI";
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.System;
|
||||
@@ -16,6 +17,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private readonly List<User32.HWINEVENTHOOK> _hooks = new();
|
||||
private HWND _currentForeground = HWND.NULL;
|
||||
private readonly IntPtr _selfHwnd;
|
||||
private readonly ThrottleHelper _winEventProcThrottle = new(TimeSpan.FromSeconds(1));
|
||||
|
||||
public delegate void WindowChangedHandler(HWND hwnd);
|
||||
private readonly WindowChangedHandler _onWindowChanged;
|
||||
|
||||
@@ -24,21 +24,30 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ImageHelper
|
||||
{
|
||||
private const int _accentColorCount = 1;
|
||||
|
||||
public static async Task<InMemoryRandomAccessStream> ByteArrayToStream(byte[] bytes)
|
||||
{
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
stream.Seek(0);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static RandomAccessStreamReference ByteArrayToRandomAccessStreamReference(byte[] bytes)
|
||||
{
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
using var writer = new DataWriter(stream);
|
||||
writer.WriteBytes(bytes);
|
||||
writer.StoreAsync().GetAwaiter().GetResult();
|
||||
writer.FlushAsync().GetAwaiter().GetResult();
|
||||
writer.DetachStream();
|
||||
return RandomAccessStreamReference.CreateFromStream(stream);
|
||||
}
|
||||
|
||||
public static async Task<byte[]> CreateTextPlaceholderBytesAsync(int width, int height)
|
||||
{
|
||||
var device = CanvasDevice.GetSharedDevice();
|
||||
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
|
||||
using var device = CanvasDevice.GetSharedDevice();
|
||||
using var renderTarget = new CanvasRenderTarget(device, width, height, 96);
|
||||
|
||||
// 随机生成渐变色
|
||||
Windows.UI.Color RandomColor()
|
||||
@@ -47,7 +56,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
double h = rand.NextDouble() * 360;
|
||||
double s = 0.35 + rand.NextDouble() * 0.3; // 0.35~0.65,适中饱和度
|
||||
double l = 0.5 + rand.NextDouble() * 0.3; // 0.5~0.8,明亮
|
||||
return HslToColor(h, s, l);
|
||||
return CommunityToolkit.WinUI.Helpers.ColorHelper.FromHsl(h, s, l);
|
||||
}
|
||||
|
||||
Windows.UI.Color color1 = RandomColor();
|
||||
@@ -56,86 +65,40 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
using (var ds = renderTarget.CreateDrawingSession())
|
||||
{
|
||||
// 绘制线性渐变背景
|
||||
var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
|
||||
using var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
|
||||
{
|
||||
StartPoint = new System.Numerics.Vector2(0, 0),
|
||||
EndPoint = new System.Numerics.Vector2(width, height)
|
||||
StartPoint = new Vector2(0, 0),
|
||||
EndPoint = new Vector2(width, height)
|
||||
};
|
||||
ds.FillRectangle(0, 0, width, height, gradientBrush);
|
||||
}
|
||||
|
||||
// 保存为 PNG 并转为 byte[]
|
||||
using (var stream = new InMemoryRandomAccessStream())
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
|
||||
var buffer = new byte[stream.Size];
|
||||
using (var reader = new DataReader(stream.GetInputStreamAt(0)))
|
||||
{
|
||||
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
|
||||
var buffer = new byte[stream.Size];
|
||||
using (var reader = new DataReader(stream.GetInputStreamAt(0)))
|
||||
{
|
||||
await reader.LoadAsync((uint)stream.Size);
|
||||
reader.ReadBytes(buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// HSL转Color
|
||||
static Windows.UI.Color HslToColor(double h, double s, double l)
|
||||
{
|
||||
h = h / 360.0;
|
||||
double r = l, g = l, b = l;
|
||||
if (s != 0)
|
||||
{
|
||||
double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
double p = 2 * l - q;
|
||||
r = HueToRgb(p, q, h + 1.0 / 3.0);
|
||||
g = HueToRgb(p, q, h);
|
||||
b = HueToRgb(p, q, h - 1.0 / 3.0);
|
||||
}
|
||||
return Windows.UI.Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
|
||||
}
|
||||
static double HueToRgb(double p, double q, double t)
|
||||
{
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1.0 / 6.0) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0 / 2.0) return q;
|
||||
if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6;
|
||||
return p;
|
||||
await reader.LoadAsync((uint)stream.Size);
|
||||
reader.ReadBytes(buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static List<Windows.UI.Color> GetAccentColorsFromByte(byte[] bytes)
|
||||
public static List<Windows.UI.Color> GetAccentColorsFromByte(byte[] bytes, int count, bool? isDark = null)
|
||||
{
|
||||
// 使用 ImageSharp 读取图片
|
||||
using var image = SixLabors.ImageSharp.Image.Load<SixLabors.ImageSharp.PixelFormats.Rgba32>(bytes);
|
||||
|
||||
// 简单聚类法:统计所有像素出现频率,取出现最多的前 AccentColorCount 个颜色
|
||||
var colorCount = new Dictionary<SixLabors.ImageSharp.PixelFormats.Rgba32, int>();
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
var color = image[x, y];
|
||||
// 可选:忽略透明像素
|
||||
if (color.A < 32) continue;
|
||||
if (colorCount.ContainsKey(color))
|
||||
colorCount[color]++;
|
||||
else
|
||||
colorCount[color] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 按出现次数排序,取前 AccentColorCount 个
|
||||
var topColors = colorCount
|
||||
.OrderByDescending(kv => kv.Value)
|
||||
.Take(_accentColorCount)
|
||||
.Select(kv => kv.Key)
|
||||
using var image = Image.Load<Rgba32>(bytes);
|
||||
var colorThief = new ColorThief.ImageSharp.ColorThief();
|
||||
var mainColor = colorThief.GetColor(image, 10, false);
|
||||
var palette = colorThief.GetPalette(image, 255, 10, false);
|
||||
var topColors = palette
|
||||
.OrderByDescending(x => x.Population)
|
||||
.Where(x => x.IsDark == (isDark ?? mainColor.IsDark))
|
||||
.Select(x => Windows.UI.Color.FromArgb(x.Color.A, x.Color.R, x.Color.G, x.Color.B))
|
||||
.Take(count)
|
||||
.ToList();
|
||||
|
||||
// 转换为 Windows.UI.Color
|
||||
return topColors
|
||||
.Select(c => Windows.UI.Color.FromArgb(c.A, c.R, c.G, c.B))
|
||||
.ToList();
|
||||
return topColors;
|
||||
}
|
||||
|
||||
//public static async Task<BitmapImage> GetBitmapImageFromBytesAsync(byte[] imageBytes)
|
||||
@@ -167,9 +130,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static async Task<byte[]> ToByteArrayAsync(IRandomAccessStreamReference streamRef)
|
||||
{
|
||||
using IRandomAccessStream stream = await streamRef.OpenReadAsync();
|
||||
using var memoryStream = new MemoryStream();
|
||||
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
using var reader = new DataReader(stream);
|
||||
await reader.LoadAsync((uint)stream.Size);
|
||||
byte[] buffer = new byte[stream.Size];
|
||||
reader.ReadBytes(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static float GetAverageLuminance(CanvasBitmap bitmap)
|
||||
@@ -201,7 +166,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
int size = Math.Max(image.Width, image.Height);
|
||||
|
||||
var themeColor = Rgba32.ParseHex(GetAccentColorsFromByte(imageBytes).FirstOrDefault().ToHex());
|
||||
var themeColor = Rgba32.ParseHex(GetAccentColorsFromByte(imageBytes, 1).FirstOrDefault().ToHex());
|
||||
|
||||
// 新建正方形画布
|
||||
using var square = new Image<Rgba32>(size, size, themeColor);
|
||||
@@ -218,5 +183,22 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
square.Save(ms, new PngEncoder());
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] Resize(byte[] imageBytes, int size)
|
||||
{
|
||||
using Image image = Image.Load(imageBytes);
|
||||
var factor = Math.Max(size / image.Width, size / image.Height);
|
||||
if (factor <= 1)
|
||||
{
|
||||
return imageBytes;
|
||||
}
|
||||
int width = image.Width * factor;
|
||||
int height = image.Height * factor;
|
||||
image.Mutate(x => x.Resize(width, height, KnownResamplers.Welch));
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
image.Save(ms, new PngEncoder());
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using TinyPinyin;
|
||||
using Windows.Globalization;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
@@ -56,36 +57,20 @@ namespace BetterLyrics.WinUI3.Services
|
||||
_identifier = _factory.Load(PathHelper.LanguageProfilePath);
|
||||
}
|
||||
|
||||
private static string? ThreeLetterToTwoLetter(string? threeLetterCode)
|
||||
{
|
||||
if (threeLetterCode == null) return null;
|
||||
|
||||
foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
|
||||
{
|
||||
if (string.Equals(ci.ThreeLetterISOLanguageName, threeLetterCode, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ci.TwoLetterISOLanguageName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
string? code = ThreeLetterToTwoLetter(_identifier.Identify(text).FirstOrDefault()?.Item1.Iso639_2T);
|
||||
if (code != null && code == "zh")
|
||||
var guessList = _identifier.Identify(text);
|
||||
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
||||
code = code switch
|
||||
{
|
||||
if (ChineseConverter.ConvertToTraditionalChinese(text) == text)
|
||||
{
|
||||
return "zh-Hant";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "zh-Hans";
|
||||
}
|
||||
}
|
||||
"simple" => "en",
|
||||
"zh_classical" => "zh-Hant",
|
||||
"zh_yue" => "zh-Hant",
|
||||
"zh" => "zh-Hans",
|
||||
_ => code
|
||||
};
|
||||
return code;
|
||||
}
|
||||
|
||||
@@ -124,5 +109,20 @@ namespace BetterLyrics.WinUI3.Services
|
||||
if (found == -1) found = 7; // 默认使用英语
|
||||
return found;
|
||||
}
|
||||
|
||||
public static string GetOrderChar(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return "#";
|
||||
char c = text.ElementAtOrDefault(0);
|
||||
if (char.IsLetter(c) && c < 128)
|
||||
return char.ToUpper(c).ToString();
|
||||
|
||||
if (PinyinHelper.IsChinese(c))
|
||||
{
|
||||
return PinyinHelper.GetPinyinInitials($"{c}");
|
||||
}
|
||||
|
||||
return "#";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Nito.AsyncEx;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -9,23 +10,34 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class LatestOnlyTaskRunner
|
||||
{
|
||||
private CancellationTokenSource? _cts;
|
||||
private readonly AsyncLock _mutex = new();
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
public async Task RunAsync(Func<CancellationToken, Task> func)
|
||||
public async Task RunAsync(Func<CancellationToken, Task> action)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
CancellationTokenSource oldCts;
|
||||
|
||||
// 使用 AsyncLock 保证线程安全
|
||||
using (await _mutex.LockAsync())
|
||||
{
|
||||
// 取消旧的
|
||||
oldCts = _cts;
|
||||
_cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
oldCts?.Cancel();
|
||||
oldCts?.Dispose();
|
||||
|
||||
CancellationToken token = _cts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
await func(token);
|
||||
await action(token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 可以选择忽略取消异常
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
_cts?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class LauncherHelper
|
||||
{
|
||||
public static async Task SelectAndShowFile(string filePath)
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
var folder = await file.GetParentAsync();
|
||||
|
||||
var folderOptions = new FolderLauncherOptions();
|
||||
folderOptions.ItemsToSelect.Add(file);
|
||||
|
||||
await Launcher.LaunchFolderAsync(folder, folderOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
break;
|
||||
}
|
||||
}
|
||||
PostProcessLyricsLines(durationMs.Value);
|
||||
_lyricsDataArr.Add(new LyricsData()); // 为机翻预留
|
||||
return _lyricsDataArr;
|
||||
}
|
||||
|
||||
@@ -109,47 +109,57 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
// 按时间分组
|
||||
var grouped = lrcLines.GroupBy(l => l.time).OrderBy(g => g.Key).ToList();
|
||||
int languageCount = grouped.Max(g => g.Count());
|
||||
int languageCount = 0;
|
||||
if (grouped != null && grouped.Count > 0)
|
||||
{
|
||||
// 计算最大语言数量
|
||||
languageCount = grouped.Max(g => g.Count());
|
||||
}
|
||||
|
||||
// 初始化每种语言的歌词列表
|
||||
_lyricsDataArr.Clear();
|
||||
for (int i = 0; i < languageCount; i++)
|
||||
_lyricsDataArr.Add(new LyricsData());
|
||||
for (int i = 0; i < languageCount; i++) _lyricsDataArr.Add(new LyricsData());
|
||||
|
||||
// 遍历每个时间分组
|
||||
foreach (var group in grouped)
|
||||
if (grouped != null)
|
||||
{
|
||||
var linesInGroup = group.ToList();
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
// 如果该语言有翻译,取对应行,否则用原文(第一行)
|
||||
var (start, text, syllables) =
|
||||
langIdx < linesInGroup.Count ? linesInGroup[langIdx] : linesInGroup[0];
|
||||
var line = new LyricsLine
|
||||
var linesInGroup = group.ToList();
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
{
|
||||
StartMs = start,
|
||||
OriginalText = text,
|
||||
LyricsChars = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
for (int j = 0; j < syllables.Count; j++)
|
||||
// 只添加有对应行的语言,否则跳过
|
||||
if (langIdx < linesInGroup.Count)
|
||||
{
|
||||
var (charStart, charText) = syllables[j];
|
||||
int startIndex = currentIndex;
|
||||
line.LyricsChars.Add(
|
||||
new LyricsChar
|
||||
var (start, text, syllables) = linesInGroup[langIdx];
|
||||
var line = new LyricsLine
|
||||
{
|
||||
StartMs = start,
|
||||
OriginalText = text,
|
||||
LyricsChars = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
for (int j = 0; j < syllables.Count; j++)
|
||||
{
|
||||
StartMs = charStart,
|
||||
Text = charText ?? "",
|
||||
StartIndex = startIndex,
|
||||
var (charStart, charText) = syllables[j];
|
||||
int startIndex = currentIndex;
|
||||
line.LyricsChars.Add(
|
||||
new LyricsChar
|
||||
{
|
||||
StartMs = charStart,
|
||||
Text = charText ?? "",
|
||||
StartIndex = startIndex,
|
||||
}
|
||||
);
|
||||
currentIndex += charText?.Length ?? 0;
|
||||
}
|
||||
);
|
||||
currentIndex += charText?.Length ?? 0;
|
||||
}
|
||||
_lyricsDataArr[langIdx].LyricsLines.Add(line);
|
||||
}
|
||||
// 没有翻译行则不补原文,直接跳过
|
||||
}
|
||||
_lyricsDataArr[langIdx].LyricsLines.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,7 +170,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
List<LyricsLine> originalLines = [];
|
||||
List<LyricsLine> translationLines = [];
|
||||
var xdoc = XDocument.Parse(raw);
|
||||
var xdoc = XDocument.Parse(raw, LoadOptions.PreserveWhitespace);
|
||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||
if (body == null) return;
|
||||
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
||||
@@ -186,16 +196,18 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == "x-translation")
|
||||
.ToList();
|
||||
|
||||
// 原文(非 CJK 语言添加空格)
|
||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
if (!LanguageHelper.IsCJK(originalText))
|
||||
// 处理原文span后的空白
|
||||
for (int i = 0; i < originalTextSpans.Count; i++)
|
||||
{
|
||||
foreach (var span in originalTextSpans)
|
||||
var span = originalTextSpans[i];
|
||||
var nextNode = span.NodesAfterSelf().FirstOrDefault();
|
||||
if (nextNode is XText textNode)
|
||||
{
|
||||
span.Value += " ";
|
||||
span.Value += textNode.Value;
|
||||
}
|
||||
originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
}
|
||||
// 拼接空白字符后的原文
|
||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
|
||||
var originalCharTimings = new List<LyricsChar>();
|
||||
int originalStartIndex = 0;
|
||||
@@ -374,29 +386,5 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
_lyricsDataArr.Add(new LyricsData(lyricsLines));
|
||||
}
|
||||
|
||||
private void PostProcessLyricsLines(int durationMs)
|
||||
{
|
||||
for (int langIdx = 0; langIdx < _lyricsDataArr.Count; langIdx++)
|
||||
{
|
||||
var lines = _lyricsDataArr[langIdx].LyricsLines;
|
||||
if (lines.Count > 0)
|
||||
{
|
||||
if (lines[0].StartMs > 0)
|
||||
{
|
||||
lines.Insert(
|
||||
0,
|
||||
new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = lines[0].StartMs,
|
||||
OriginalText = "● ● ●",
|
||||
LyricsChars = [],
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public const string GithubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||
public const string QQGroupUrl = "https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info";
|
||||
public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv";
|
||||
public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1";
|
||||
|
||||
public static async Task<DateTime> GetBuildDate()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class MonitorHelper
|
||||
{
|
||||
public static IEnumerable<string> GetAllMonitorDeviceNames()
|
||||
{
|
||||
var deviceNames = new List<string>();
|
||||
User32.EnumDisplayMonitors(IntPtr.Zero, null, (hMonitor, hdcMonitor, lprcMonitor, dwData) =>
|
||||
{
|
||||
User32.MONITORINFOEX monitorInfoEx = new() { cbSize = (uint)Marshal.SizeOf<User32.MONITORINFOEX>() };
|
||||
if (User32.GetMonitorInfo(hMonitor, ref monitorInfoEx))
|
||||
{
|
||||
deviceNames.Add(monitorInfoEx.szDevice);
|
||||
}
|
||||
return true; // 继续枚举
|
||||
}, IntPtr.Zero);
|
||||
return deviceNames;
|
||||
}
|
||||
|
||||
public static User32.MONITORINFOEX GetMonitorInfoExFromDeviceName(string deviceName)
|
||||
{
|
||||
User32.MONITORINFOEX? result = null;
|
||||
User32.EnumDisplayMonitors(IntPtr.Zero, null, (hMonitor, hdcMonitor, lprcMonitor, dwData) =>
|
||||
{
|
||||
User32.MONITORINFOEX monitorInfoEx = new() { cbSize = (uint)Marshal.SizeOf<User32.MONITORINFOEX>() };
|
||||
if (User32.GetMonitorInfo(hMonitor, ref monitorInfoEx))
|
||||
{
|
||||
if (string.Equals(monitorInfoEx.szDevice, deviceName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = monitorInfoEx;
|
||||
return false; // 找到后停止枚举
|
||||
}
|
||||
}
|
||||
return true; // 继续枚举
|
||||
}, IntPtr.Zero);
|
||||
return result ?? GetPrimaryMonitorInfoEx();
|
||||
}
|
||||
|
||||
public static User32.MONITORINFOEX GetPrimaryMonitorInfoEx()
|
||||
{
|
||||
// (0,0) 总是在主屏
|
||||
var ptZero = new POINT(0, 0);
|
||||
HMONITOR hMonitor = User32.MonitorFromPoint(ptZero, User32.MonitorFlags.MONITOR_DEFAULTTOPRIMARY);
|
||||
User32.MONITORINFOEX monitorInfoEx = new() { cbSize = (uint)Marshal.SizeOf<User32.MONITORINFOEX>() };
|
||||
User32.GetMonitorInfo(hMonitor, ref monitorInfoEx);
|
||||
return monitorInfoEx;
|
||||
}
|
||||
|
||||
public static string GetPrimaryMonitorDeviceName()
|
||||
{
|
||||
var primaryMonitorInfo = GetPrimaryMonitorInfoEx();
|
||||
return primaryMonitorInfo.szDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using static BetterLyrics.WinUI3.Helper.NoiseOverlayHelper.BitmapFileCreator;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
internal static class NoiseOverlayHelper
|
||||
{
|
||||
|
||||
const string NoiseOverlayFileName = "noise_overlay.bmp";
|
||||
|
||||
static readonly string NoiseOverlayFilePath = Path.Combine(ApplicationData.Current.LocalFolder.Path, "Assets", NoiseOverlayFileName);
|
||||
|
||||
/// <summary>
|
||||
/// 生成 BGRA 格式的灰阶噪声像素数据
|
||||
/// </summary>
|
||||
public static byte[] GenerateNoiseBitmapBGRA(int width, int height)
|
||||
{
|
||||
var random = new Random();
|
||||
var pixelData = new byte[width * height * 4];
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
byte gray = (byte)random.Next(0, 256);
|
||||
pixelData[i * 4 + 0] = gray; // B
|
||||
pixelData[i * 4 + 1] = gray; // G
|
||||
pixelData[i * 4 + 2] = gray; // R
|
||||
pixelData[i * 4 + 3] = 255; // A
|
||||
}
|
||||
return pixelData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成单色灰阶随机噪声
|
||||
/// </summary>
|
||||
/// <param name="outputPath">输出文件路径</param>
|
||||
/// <param name="width">图片宽度</param>
|
||||
/// <param name="height">图片高度</param>
|
||||
public static BitmapFile GenerateNoiseBitmap(int width, int height)
|
||||
{
|
||||
const uint NumOfGrayscale = 16;
|
||||
uint bitCount = NextPowerOfTwo((uint)Math.Round(Math.Sqrt(NumOfGrayscale)));
|
||||
|
||||
var palette = BitmapFileCreator.CreateGrayscalePalette(16);
|
||||
var pixelData = GenerateRandomNoise(width, height, bitCount);
|
||||
|
||||
var fileHeader = BitmapFileCreator.CreateFileHeader(palette, pixelData);
|
||||
var infoHeader = BitmapFileCreator.CreateInfoHeader(width, height, bitCount);
|
||||
|
||||
return new BitmapFile(fileHeader, infoHeader, palette, pixelData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取噪声图片的位头信息
|
||||
/// </summary>
|
||||
public static BitmapFileCreator.WINBMPINFOHEADER ReadBitmapInfoHeaders(string? FilePath)
|
||||
{
|
||||
var _filePath = FilePath ?? NoiseOverlayFilePath;
|
||||
using var fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read);
|
||||
using var br = new BinaryReader(fs);
|
||||
|
||||
// 跳过文件头
|
||||
fs.Seek(Marshal.SizeOf<BitmapFileCreator.BITMAPFILEHEADER>(), SeekOrigin.Begin);
|
||||
|
||||
// 读取信息头
|
||||
byte[] infoHeaderBytes = br.ReadBytes(Marshal.SizeOf<BitmapFileCreator.WINBMPINFOHEADER>());
|
||||
return MemoryMarshal.Read<BitmapFileCreator.WINBMPINFOHEADER>(infoHeaderBytes);
|
||||
}
|
||||
|
||||
public static BitmapFileCreator.WINBMPINFOHEADER ReadBitmapInfoHeaders(byte[] FileBytes)
|
||||
{
|
||||
// 跳过文件头
|
||||
var offset = Marshal.SizeOf<BitmapFileCreator.BITMAPFILEHEADER>();
|
||||
|
||||
// 读取信息头
|
||||
var infoHeaderLength = Marshal.SizeOf<BitmapFileCreator.WINBMPINFOHEADER>();
|
||||
Span<byte> infoHeaderBytes = new(FileBytes, offset, infoHeaderLength);
|
||||
return MemoryMarshal.Read<BitmapFileCreator.WINBMPINFOHEADER>(infoHeaderBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// safe 的写入 struct 到流
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值 strcut</typeparam>
|
||||
/// <param name="stream">要写入的字节流</param>
|
||||
/// <param name="structure">要被写入的结构</param>
|
||||
public static void WriteStruct<T>(Stream stream, in T structure) where T : struct
|
||||
{
|
||||
int size = Unsafe.SizeOf<T>();
|
||||
byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
|
||||
try
|
||||
{
|
||||
MemoryMarshal.Write(buffer, in structure);
|
||||
stream.Write(buffer, 0, size);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 随机填充位图内容
|
||||
/// </summary>
|
||||
/// <param name="width">填充宽度</param>
|
||||
/// <param name="height">填充高度</param>
|
||||
/// <param name="bitCount">单个调色盘索引所占比特位数</param>
|
||||
/// <returns>字节数据</returns>
|
||||
private static byte[] GenerateRandomNoise(int width, int height, uint bitCount)
|
||||
{
|
||||
// 创建位图行字节数,4K 对齐
|
||||
int rowSize = ((width * (int)bitCount + 31) >> 5) << 2;
|
||||
|
||||
// 创建随机位图数据
|
||||
Random rnd = new();
|
||||
return Enumerable.Range(0, rowSize * height)
|
||||
.Select(i => (byte)rnd.Next(0x00, 0xFF))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static uint NextPowerOfTwo(uint value)
|
||||
{
|
||||
value--;
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
return value + 1;
|
||||
}
|
||||
public static class BitmapFileCreator
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 创建BMP文件头
|
||||
/// </summary>
|
||||
/// <param name="palette">调色盘数据</param>
|
||||
/// <param name="pixelData">像素数据</param>
|
||||
/// <returns>文件头结构</returns>
|
||||
public static BITMAPFILEHEADER CreateFileHeader(byte[] palette, byte[] pixelData)
|
||||
{
|
||||
return new BITMAPFILEHEADER
|
||||
{
|
||||
bfType = 0x4D42,
|
||||
bfSize = (uint)(Marshal.SizeOf<BITMAPFILEHEADER>() +
|
||||
Marshal.SizeOf<WINBMPINFOHEADER>() +
|
||||
palette.Length +
|
||||
pixelData.Length),
|
||||
bfReserved1 = 0,
|
||||
bfReserved2 = 0,
|
||||
bfOffBits = (uint)(Marshal.SizeOf<BITMAPFILEHEADER>() +
|
||||
Marshal.SizeOf<WINBMPINFOHEADER>() +
|
||||
palette.Length)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值填充到为最接近的2的幂数
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 生成灰阶调色盘
|
||||
/// </summary>
|
||||
/// <param name="colors">灰阶数量</param>
|
||||
/// <returns>调色盘byte数组</returns>
|
||||
public static byte[] CreateGrayscalePalette(int colors)
|
||||
{
|
||||
return Enumerable.Range(0, colors)
|
||||
.SelectMany(i => Enumerable.Repeat((byte)(i * 0x10), 4))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建BMP信息头
|
||||
/// </summary>
|
||||
/// <param name="width">宽度</param>
|
||||
/// <param name="height">高度</param>
|
||||
/// <param name="bitCount">单个像素(调色盘索引)位数</param>
|
||||
/// <returns>BMP信息头结构</returns>
|
||||
public static WINBMPINFOHEADER CreateInfoHeader(int width, int height, uint bitCount)
|
||||
{
|
||||
return new WINBMPINFOHEADER
|
||||
{
|
||||
biSize = (uint)Marshal.SizeOf<WINBMPINFOHEADER>(),
|
||||
biWidth = (uint)width,
|
||||
biHeight = (uint)height,
|
||||
biPlanes = 1,
|
||||
biBitCount = (ushort)bitCount,
|
||||
biCompression = 0,
|
||||
biSizeImage = 0,
|
||||
biXPelsPerMeter = 0,
|
||||
biYPelsPerMeter = 0,
|
||||
biClrUsed = 0,
|
||||
biClrImportant = 0
|
||||
};
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
/// <summary>
|
||||
/// BMP 位图文件头
|
||||
/// </summary>
|
||||
public struct BITMAPFILEHEADER
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件类型标识,用于指定文件格式
|
||||
/// </summary>
|
||||
public ushort bfType;
|
||||
/// <summary>
|
||||
/// 文件大小,以字节为单位
|
||||
/// </summary>
|
||||
public uint bfSize;
|
||||
/// <summary>
|
||||
/// 保留字段,未使用
|
||||
/// </summary>
|
||||
public ushort bfReserved1;
|
||||
/// <summary>
|
||||
/// 保留字段,未使用
|
||||
/// </summary>
|
||||
public ushort bfReserved2;
|
||||
/// <summary>
|
||||
/// 像素数据的起始位置,以字节为单位,从文件头开始计算
|
||||
/// </summary>
|
||||
public uint bfOffBits;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
/// <summary>
|
||||
/// BMP 位图信息头
|
||||
/// </summary>
|
||||
public struct WINBMPINFOHEADER
|
||||
{
|
||||
/// <summary>
|
||||
/// 指定此结构体的字节大小。
|
||||
/// </summary>
|
||||
public uint biSize;
|
||||
|
||||
/// <summary>
|
||||
/// 以像素为单位的位图宽度。
|
||||
/// </summary>
|
||||
public uint biWidth;
|
||||
|
||||
/// <summary>
|
||||
/// 以像素为单位的位图高度。
|
||||
/// </summary>
|
||||
public uint biHeight;
|
||||
|
||||
/// <summary>
|
||||
/// 目标设备的平面数,通常为1。
|
||||
/// </summary>
|
||||
public ushort biPlanes;
|
||||
|
||||
/// <summary>
|
||||
/// 每个像素的位数,表示颜色深度,如1、4、8、16、24、32等。
|
||||
/// </summary>
|
||||
public ushort biBitCount;
|
||||
|
||||
/// <summary>
|
||||
/// 压缩类型,0表示不压缩。
|
||||
/// </summary>
|
||||
public uint biCompression;
|
||||
|
||||
/// <summary>
|
||||
/// 位图图像数据的大小(以字节为单位),若图像未压缩,该值可设为0。
|
||||
/// </summary>
|
||||
public uint biSizeImage;
|
||||
|
||||
/// <summary>
|
||||
/// 每米X轴方向的像素数(水平分辨率),通常设为0。
|
||||
/// </summary>
|
||||
public uint biXPelsPerMeter;
|
||||
|
||||
/// <summary>
|
||||
/// 每米Y轴方向的像素数(垂直分辨率),通常设为0。
|
||||
/// </summary>
|
||||
public uint biYPelsPerMeter;
|
||||
|
||||
/// <summary>
|
||||
/// 实际使用的颜色索引数,若为0,则使用位图中实际出现的颜色数。
|
||||
/// </summary>
|
||||
public uint biClrUsed;
|
||||
|
||||
/// <summary>
|
||||
/// 重要颜色索引数,0表示所有颜色都重要。
|
||||
/// </summary>
|
||||
public uint biClrImportant;
|
||||
}
|
||||
}
|
||||
|
||||
public class BitmapFile(BitmapFileCreator.BITMAPFILEHEADER fileHeader, BitmapFileCreator.WINBMPINFOHEADER infoHeader, byte[] palette, byte[] pixelData)
|
||||
{
|
||||
public BITMAPFILEHEADER FileHeader = fileHeader;
|
||||
public WINBMPINFOHEADER InfoHeader = infoHeader;
|
||||
public byte[] Palette = palette;
|
||||
public byte[] PixelData = pixelData;
|
||||
|
||||
/// <summary>
|
||||
/// 转换为byte[]
|
||||
/// </summary>
|
||||
/// <param name="bf"></param>
|
||||
public static explicit operator byte[](BitmapFile bf)
|
||||
{
|
||||
var result = new byte[bf.FileHeader.bfSize];
|
||||
var ms = new MemoryStream(result);
|
||||
bf.WriteToStream(ms);
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] ToByteArray() => (byte[])this;
|
||||
|
||||
public Task<byte[]> ToByteArrayAsync() { return Task.FromResult(ToByteArray());}
|
||||
|
||||
/// <summary>
|
||||
/// 写入此结构到流中
|
||||
/// </summary>
|
||||
public void WriteToStream(Stream stream)
|
||||
{
|
||||
NoiseOverlayHelper.WriteStruct(stream, FileHeader);
|
||||
NoiseOverlayHelper.WriteStruct(stream, InfoHeader);
|
||||
stream.Write(Palette);
|
||||
stream.Write(PixelData);
|
||||
stream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class ObjectHelper
|
||||
{
|
||||
// Credit/Copyright to https://gist.github.com/tcartwright/dab50ebaff7c59f05013de0fb349cabd
|
||||
public static bool IsDisposed(this IDisposable obj)
|
||||
{
|
||||
/*
|
||||
TIM C: This hacky code is because MSFT does not provide a standard way to interrogate if an object is disposed or not.
|
||||
I wrote this based upon streams, but it should work for many other types of MSFT objects (maybe).
|
||||
*/
|
||||
if (obj == null) { return true; }
|
||||
|
||||
var objType = obj.GetType();
|
||||
//var foo = new System.IO.BufferedStream();
|
||||
|
||||
// the _disposed pattern should catch a lot of msft objects.... hopefully
|
||||
var isDisposedField = objType.GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance) ??
|
||||
objType.GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (isDisposedField != null) { return Convert.ToBoolean(isDisposedField.GetValue(obj)); }
|
||||
|
||||
isDisposedField = objType.GetField("_isOpen", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (isDisposedField != null) { return !Convert.ToBoolean(isDisposedField.GetValue(obj)); }
|
||||
|
||||
// System.IO.FileStream
|
||||
var strategyField = objType.GetField("_strategy", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (strategyField != null)
|
||||
{
|
||||
var strategy = strategyField.GetValue(obj);
|
||||
var isClosedField = strategy.GetType().GetProperty("IsClosed", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (isClosedField != null) { return Convert.ToBoolean(isClosedField.GetValue(strategy)); }
|
||||
}
|
||||
|
||||
// other streams that use this pattern to determine if they are disposed
|
||||
if (obj is Stream stream) { return !stream.CanRead && !stream.CanWrite; }
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string CacheFolder => ApplicationData.Current.LocalCacheFolder.Path;
|
||||
public static string AssetsFolder => Path.Combine(Package.Current.InstalledPath, "Assets");
|
||||
|
||||
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Core14.profile.xml");
|
||||
//public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Core14.profile.xml");
|
||||
public static string LanguageProfilePath => Path.Combine(AssetsFolder, "Wiki82.profile.xml");
|
||||
public static string LogoPath => Path.Combine(AssetsFolder, "Logo.ico");
|
||||
|
||||
public static string LogDirectory => Path.Combine(CacheFolder, "logs");
|
||||
public static string LogFilePattern => Path.Combine(LogDirectory, "log-.txt");
|
||||
@@ -33,6 +35,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string TranslationCacheDirectory => Path.Combine(CacheFolder, "translations");
|
||||
|
||||
public static string QQTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "qq");
|
||||
public static string NeteaseTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "netease");
|
||||
|
||||
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
|
||||
|
||||
@@ -49,6 +52,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(QQTranslationCacheDirectory);
|
||||
Directory.CreateDirectory(NeteaseTranslationCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
var data = pNotify.ToStructure<AUDIO_VOLUME_NOTIFICATION_DATA>();
|
||||
_masterVolume = (int)(data.fMasterVolume * 100);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
VolumeChanged?.Invoke(_masterVolume);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ThrottleHelper
|
||||
{
|
||||
private DateTime _lastTriggerTime = DateTime.MinValue;
|
||||
private readonly TimeSpan _interval;
|
||||
|
||||
public ThrottleHelper(TimeSpan interval)
|
||||
{
|
||||
_interval = interval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否可以触发(距离上次触发已超过设定间隔),如果可以则更新时间戳并返回 true,否则返回 false。
|
||||
/// </summary>
|
||||
public bool CanTrigger()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
if ((now - _lastTriggerTime) >= _interval)
|
||||
{
|
||||
_lastTriggerTime = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置触发时间
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_lastTriggerTime = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,18 +26,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExitAllWindows()
|
||||
{
|
||||
while (_activeWindows.Count > 0)
|
||||
{
|
||||
var window = (Window)_activeWindows[0];
|
||||
DockModeHelper.Disable(window);
|
||||
window.Close();
|
||||
_activeWindows.Remove(window);
|
||||
}
|
||||
App.Current.Exit();
|
||||
}
|
||||
|
||||
public static T? GetWindowByWindowType<T>()
|
||||
{
|
||||
foreach (var window in _activeWindows)
|
||||
@@ -49,33 +37,33 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
return default;
|
||||
}
|
||||
public static void OpenOrShowWindow<T>()
|
||||
public static void OpenWindow<T>()
|
||||
{
|
||||
var window = _activeWindows.Find(w => w is T);
|
||||
if (window != null)
|
||||
if (window == null)
|
||||
{
|
||||
var castedWindow = (Window)window;
|
||||
castedWindow.Restore();
|
||||
}
|
||||
else
|
||||
{
|
||||
object newWindow;
|
||||
if (typeof(T) == typeof(LyricsWindow))
|
||||
{
|
||||
newWindow = new LyricsWindow();
|
||||
((LyricsWindow)newWindow).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
window = new LyricsWindow();
|
||||
((LyricsWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
|
||||
}
|
||||
else if (typeof(T) == typeof(SettingsWindow))
|
||||
{
|
||||
newWindow = new SettingsWindow();
|
||||
window = new SettingsWindow();
|
||||
}
|
||||
else if (typeof(T) == typeof(MusicGalleryWindow))
|
||||
{
|
||||
window = new MusicGalleryWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Unsupported window type", nameof(T));
|
||||
}
|
||||
((Window)newWindow).Activate();
|
||||
TrackWindow(newWindow);
|
||||
TrackWindow(window);
|
||||
}
|
||||
var castedWindow = (Window)window;
|
||||
castedWindow.Restore();
|
||||
castedWindow.Activate();
|
||||
}
|
||||
|
||||
public static void RestartApp(string args = "")
|
||||
@@ -101,7 +89,19 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static void TrackWindow(object window)
|
||||
{
|
||||
if (!_activeWindows.Contains(window))
|
||||
{
|
||||
_activeWindows.Add(window);
|
||||
var castedWindow = (Window)window;
|
||||
castedWindow.Closed += WindowHelper_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WindowHelper_Closed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (_activeWindows.Contains(sender))
|
||||
{
|
||||
_activeWindows.Remove(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class GroupInfoList : List<object>
|
||||
{
|
||||
public required object Key { get; set; }
|
||||
|
||||
public GroupInfoList(IEnumerable<object> items, Func<object, object>? orderSelector = null)
|
||||
: base(orderSelector != null
|
||||
? items.OrderBy(orderSelector)
|
||||
: items)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Key}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public partial class LocalLyricsFolder : ObservableObject
|
||||
public partial class LocalMediaFolder : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial bool IsEnabled { get; set; }
|
||||
@@ -12,9 +12,9 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty]
|
||||
public partial string Path { get; set; }
|
||||
|
||||
public LocalLyricsFolder() { }
|
||||
public LocalMediaFolder() { }
|
||||
|
||||
public LocalLyricsFolder(string path, bool isEnabled)
|
||||
public LocalMediaFolder(string path, bool isEnabled)
|
||||
{
|
||||
Path = path;
|
||||
IsEnabled = isEnabled;
|
||||
|
||||
@@ -26,36 +26,39 @@ namespace BetterLyrics.WinUI3.Models
|
||||
LyricsLines = lyricsLines;
|
||||
}
|
||||
|
||||
public void SetDisplayedTextAlongWith(LyricsData translationData)
|
||||
public void SetDisplayedTextAlongWith(LyricsData translationData, int toleranceMs = 0)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var line in LyricsLines)
|
||||
{
|
||||
if (i >= translationData.LyricsLines.Count)
|
||||
// 在翻译歌词中查找与当前行开始时间最接近且在容忍范围内的行
|
||||
var transLine = translationData.LyricsLines
|
||||
.FirstOrDefault(t => Math.Abs(t.StartMs - line.StartMs) <= toleranceMs);
|
||||
|
||||
if (transLine != null)
|
||||
{
|
||||
line.DisplayedText = line.OriginalText; // No translation available, keep original text
|
||||
}
|
||||
else
|
||||
{
|
||||
if (translationData.LanguageCode?.Substring(0, 2) == "zh")
|
||||
if (translationData.LanguageCode?.StartsWith("zh") == true)
|
||||
{
|
||||
string tmp = "";
|
||||
if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hant")
|
||||
{
|
||||
tmp = ChineseConverter.ConvertToTraditionalChinese(translationData.LyricsLines[i].OriginalText);
|
||||
tmp = ChineseConverter.ConvertToTraditionalChinese(transLine.OriginalText);
|
||||
}
|
||||
else if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hans")
|
||||
{
|
||||
tmp = ChineseConverter.ConvertToSimplifiedChinese(translationData.LyricsLines[i].OriginalText);
|
||||
tmp = ChineseConverter.ConvertToSimplifiedChinese(transLine.OriginalText);
|
||||
}
|
||||
line.DisplayedText = $"{line.OriginalText}\n{tmp}";
|
||||
}
|
||||
else
|
||||
{
|
||||
line.DisplayedText = $"{line.OriginalText}\n{translationData.LyricsLines[i].OriginalText}";
|
||||
line.DisplayedText = $"{line.OriginalText}\n{transLine.OriginalText}";
|
||||
}
|
||||
}
|
||||
i++;
|
||||
else
|
||||
{
|
||||
// 没有匹配的翻译,翻译部分留空
|
||||
line.DisplayedText = $"{line.OriginalText}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +74,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
else
|
||||
{
|
||||
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}({translationArr[i]})";
|
||||
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}{translationArr[i]}";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -85,6 +88,30 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
}
|
||||
|
||||
public LyricsData CreateLyricsDataFrom(string translation)
|
||||
{
|
||||
var result = new LyricsData(LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
}).ToList());
|
||||
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
||||
int i = 0;
|
||||
foreach (var line in result.LyricsLines)
|
||||
{
|
||||
if (i >= translationArr.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
line.OriginalText = translationArr[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LyricsData GetNotfoundPlaceholder(int durationMs)
|
||||
{
|
||||
return new LyricsData([new LyricsLine
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using ATL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class PlayQueueItem
|
||||
{
|
||||
public Track Track { get; set; }
|
||||
|
||||
public PlayQueueItem(Track track)
|
||||
{
|
||||
Track = track;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty]
|
||||
public partial string Artist { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int? Duration { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial double? DurationMs { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class SongsTabInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Icon { get; set; }
|
||||
|
||||
public bool IsClosable { get; set; }
|
||||
|
||||
public CommonSongProperty FilterProperty { get; set; }
|
||||
|
||||
public string FilterValue { get; set; }
|
||||
|
||||
public SongsTabInfo()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Icon = string.Empty;
|
||||
IsClosable = true;
|
||||
FilterProperty = CommonSongProperty.Title;
|
||||
FilterValue = string.Empty;
|
||||
}
|
||||
|
||||
public SongsTabInfo(string name, string icon, bool isClosable, CommonSongProperty filterProperty, string filterValue)
|
||||
{
|
||||
Name = name;
|
||||
Icon = icon;
|
||||
IsClosable = isClosable;
|
||||
FilterProperty = filterProperty;
|
||||
FilterValue = filterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class TrimmedTrack
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Artist { get; set; }
|
||||
public string Album { get; set; }
|
||||
public int? Year { get; set; }
|
||||
public string Genre { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public int Duration { get; set; }
|
||||
public byte[]? AlbumArt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace BetterLyrics.WinUI3.Serialization
|
||||
[JsonSerializable(typeof(List<AlbumArtSearchProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<LyricsSearchProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<MediaSourceProviderInfo>))]
|
||||
[JsonSerializable(typeof(List<LocalLyricsFolder>))]
|
||||
[JsonSerializable(typeof(List<LocalMediaFolder>))]
|
||||
[JsonSerializable(typeof(List<string>))]
|
||||
[JsonSerializable(typeof(TranslateResponse))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private byte[]? SearchFile(string artist, string album)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
foreach (var folder in _settingsService.LocalMediaFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
@@ -105,7 +105,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
string url = $"https://itunes.apple.com/search?term=" + WebUtility.UrlEncode($"{artist} {album}").Replace("%20", "+") + "&country=" + countryCode + "&entity=album&media=music&limit=1";
|
||||
|
||||
// Make a request to the API
|
||||
HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
using HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
@@ -123,7 +123,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (fetched != null && fetched.Length > 0)
|
||||
{
|
||||
fetched = ImageHelper.MakeSquareWithThemeColor(fetched);
|
||||
// Write to cache
|
||||
FileHelper.WriteAlbumArtCache(artist, album, fetched, format, PathHelper.iTunesAlbumArtCacheDirectory);
|
||||
return fetched;
|
||||
|
||||
@@ -14,6 +14,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged;
|
||||
|
||||
public void UpdateWatchers(List<LocalLyricsFolder> folders);
|
||||
public void UpdateWatchers(List<LocalMediaFolder> folders);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
public interface IPlaybackService
|
||||
{
|
||||
event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
@@ -19,6 +19,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
Task PauseAsync();
|
||||
Task PreviousAsync();
|
||||
Task NextAsync();
|
||||
|
||||
Task ChangePosition(double seconds);
|
||||
|
||||
bool IsPlaying { get; }
|
||||
SongInfo? SongInfo { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
int CoverOverlayBlurAmount { get; set; }
|
||||
int CoverOverlayOpacity { get; set; }
|
||||
bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
int CoverAcrylicEffectAmount { get; set; }
|
||||
bool IsFanLyricsEnabled { get; set; }
|
||||
bool IsFirstRun { get; set; }
|
||||
bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
@@ -41,7 +42,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
int PositionOffset { get; set; }
|
||||
// Lyrics lib
|
||||
|
||||
List<LocalLyricsFolder> LocalLyricsFolders { get; set; }
|
||||
List<LocalMediaFolder> LocalMediaFolders { get; set; }
|
||||
|
||||
// Lyrics style and effetc
|
||||
|
||||
@@ -60,7 +61,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
|
||||
int LyricsFontSize { get; set; }
|
||||
int LyricsStandardFontSize { get; set; }
|
||||
int LyricsDockFontSize { get; set; }
|
||||
int LyricsDesktopFontSize { get; set; }
|
||||
|
||||
ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
|
||||
@@ -96,5 +99,13 @@ namespace BetterLyrics.WinUI3.Services
|
||||
bool IsImmersiveMode { get; set; }
|
||||
string LXMusicServer { get; set; }
|
||||
DockPlacement DockPlacement { get; set; }
|
||||
bool HideWindowWhenNotPlaying { get; set; }
|
||||
int DockWindowHeight { get; set; }
|
||||
int SelectedFontFamilyIndex { get; set; }
|
||||
string LyricsFontFamily { get; set; }
|
||||
bool IsDragEverywhereEnabled { get; set; }
|
||||
PlaybackOrder PlaybackOrder { get; set; }
|
||||
bool IsLibreTranslateEnabled { get; set; }
|
||||
string DockMonitorDeviceName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,18 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public class LibWatcherService : IDisposable, ILibWatcherService
|
||||
public class LibWatcherService : BaseViewModel, IDisposable, ILibWatcherService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
|
||||
private readonly Dictionary<string, FileSystemWatcher> _watchers = [];
|
||||
|
||||
public LibWatcherService(ISettingsService settingsService)
|
||||
public LibWatcherService(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
UpdateWatchers(_settingsService.LocalLyricsFolders);
|
||||
UpdateWatchers(_settingsService.LocalMediaFolders);
|
||||
}
|
||||
|
||||
public event EventHandler<LibChangedEventArgs>? MusicLibraryFilesChanged;
|
||||
@@ -32,7 +31,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
_watchers.Clear();
|
||||
}
|
||||
|
||||
public void UpdateWatchers(List<LocalLyricsFolder> folders)
|
||||
public void UpdateWatchers(List<LocalMediaFolder> folders)
|
||||
{
|
||||
// 移除不再监听的
|
||||
foreach (var key in _watchers.Keys.ToList())
|
||||
@@ -69,16 +68,13 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private void OnChanged(string folder, FileSystemEventArgs e)
|
||||
{
|
||||
App.DispatcherQueue!.TryEnqueue(
|
||||
Microsoft.UI.Dispatching.DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
MusicLibraryFilesChanged?.Invoke(
|
||||
this,
|
||||
new LibChangedEventArgs(folder, e.FullPath, e.ChangeType)
|
||||
);
|
||||
}
|
||||
);
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
MusicLibraryFilesChanged?.Invoke(
|
||||
this,
|
||||
new LibChangedEventArgs(folder, e.FullPath, e.ChangeType)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Lyricify.Lyrics.Providers.Web.Kugou;
|
||||
using Lyricify.Lyrics.Searchers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -89,82 +90,86 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
|
||||
|
||||
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
|
||||
try
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string? cachedLyrics;
|
||||
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
|
||||
|
||||
// Check cache first
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
if (!provider.IsEnabled)
|
||||
{
|
||||
return (cachedLyrics, provider.Provider);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
string? searchedLyrics = null;
|
||||
string? cachedLyrics;
|
||||
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
|
||||
|
||||
if (provider.Provider.IsLocal())
|
||||
{
|
||||
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
|
||||
// Check cache first
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
searchedLyrics = SearchEmbedded(title, artist);
|
||||
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
{
|
||||
return (cachedLyrics, provider.Provider);
|
||||
}
|
||||
}
|
||||
|
||||
string? searchedLyrics = null;
|
||||
|
||||
if (provider.Provider.IsLocal())
|
||||
{
|
||||
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
|
||||
{
|
||||
searchedLyrics = SearchEmbedded(title, artist);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
|
||||
switch (provider.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
|
||||
break;
|
||||
case LyricsSearchProvider.QQ:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (provider.Provider)
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchedLyrics))
|
||||
{
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
|
||||
break;
|
||||
case LyricsSearchProvider.QQ:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
}
|
||||
|
||||
return (searchedLyrics, provider.Provider);
|
||||
}
|
||||
}
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchedLyrics))
|
||||
{
|
||||
if (provider.Provider.IsRemote())
|
||||
{
|
||||
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
}
|
||||
|
||||
return (searchedLyrics, provider.Provider);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
private async Task<string?> SearchFile(string title, string artist, LyricsFormat format)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
foreach (var folder in _settingsService.LocalMediaFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
@@ -186,7 +191,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private string? SearchEmbedded(string title, string artist)
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalLyricsFolders)
|
||||
foreach (var folder in _settingsService.LocalMediaFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
@@ -266,7 +271,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var url = $"https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/refs/heads/main/raw-lyrics/{rawLyricFile}";
|
||||
try
|
||||
{
|
||||
var response = await _amllTtmlDbHttpClient.GetAsync(url);
|
||||
using var response = await _amllTtmlDbHttpClient.GetAsync(url);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return null;
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
@@ -287,7 +292,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
$"&album_name={Uri.EscapeDataString(album)}" +
|
||||
$"&durationMs={Uri.EscapeDataString(duration.ToString())}";
|
||||
|
||||
var response = await _lrcLibHttpClient.GetAsync(url);
|
||||
using var response = await _lrcLibHttpClient.GetAsync(url);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return null;
|
||||
|
||||
@@ -343,6 +348,17 @@ namespace BetterLyrics.WinUI3.Services
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
|
||||
var translated = response?.Tlyric?.Lyric;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.NeteaseTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
return response?.Lrc.Lyric;
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
|
||||
@@ -10,9 +10,11 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using EvtSource;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.Json;
|
||||
@@ -21,7 +23,9 @@ using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Media.Control;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Shell;
|
||||
using WindowsMediaController;
|
||||
using static WindowsMediaController.MediaManager;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
@@ -33,21 +37,24 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private readonly ILogger<PlaybackService> _logger;
|
||||
|
||||
private readonly string _lxMusicId = "cn.toside.music.desktop";
|
||||
private double _lxMusicPositionSeconds = 0;
|
||||
private double _lxMusicDurationSeconds = 0;
|
||||
|
||||
private bool _cachedIsPlaying = false;
|
||||
|
||||
private EventSourceReader? _sse = null;
|
||||
|
||||
private readonly MediaManager _mediaManager = new();
|
||||
|
||||
private readonly LatestOnlyTaskRunner _AlbumArtRefreshRunner = new();
|
||||
private readonly LatestOnlyTaskRunner _OnAnyMediaPropertyChangedRunner = new();
|
||||
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
|
||||
private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new();
|
||||
|
||||
private SongInfo? _cachedSongInfo;
|
||||
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
|
||||
private byte[]? _SMTCAlbumArtBytes = null;
|
||||
private AlbumArtChangedEventArgs _albumArtChangedEventArgs = new();
|
||||
|
||||
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
public event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
public event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
|
||||
public event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
public event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
@@ -61,6 +68,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
InitMediaManager();
|
||||
}
|
||||
|
||||
public bool IsPlaying => _cachedIsPlaying;
|
||||
public SongInfo? SongInfo => _cachedSongInfo;
|
||||
|
||||
private bool IsMediaSourceEnabled(string id)
|
||||
{
|
||||
return _mediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true;
|
||||
@@ -68,8 +78,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private void InitMediaManager()
|
||||
{
|
||||
_mediaManager.Start();
|
||||
|
||||
_mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened;
|
||||
_mediaManager.OnAnySessionClosed += MediaManager_OnAnySessionClosed;
|
||||
_mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged;
|
||||
@@ -77,77 +85,85 @@ namespace BetterLyrics.WinUI3.Services
|
||||
_mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged;
|
||||
_mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged;
|
||||
|
||||
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
|
||||
_mediaManager.Start();
|
||||
Task.Run(() =>
|
||||
{
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession mediaSession)
|
||||
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
|
||||
{
|
||||
if (mediaSession == null || !IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId))
|
||||
{
|
||||
SendNullMessages();
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var props = await mediaSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
MediaManager_OnAnyMediaPropertyChanged(mediaSession, props);
|
||||
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "TryGetMediaPropertiesAsync failed");
|
||||
SendNullMessages();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
|
||||
SendFocusedMessagesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
|
||||
{
|
||||
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
_dispatcherQueue.TryEnqueue(
|
||||
DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(timelineProperties.Position));
|
||||
}
|
||||
);
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != focusedSession) return;
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(timelineProperties.Position, timelineProperties.EndTime));
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
|
||||
{
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
}));
|
||||
}
|
||||
);
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != focusedSession) return;
|
||||
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
});
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
{
|
||||
_ = _OnAnyMediaPropertyChangedRunner.RunAsync(async token =>
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
string id = mediaSession.Id;
|
||||
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(id) || mediaSession != focusedSession) return;
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
|
||||
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
|
||||
|
||||
_onAnyMediaPropertyChangedRunner.RunAsync(async token =>
|
||||
{
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
string id = mediaSession.ControlSession.SourceAppUserModelId;
|
||||
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
if (id == _lxMusicId)
|
||||
{
|
||||
StartSSE();
|
||||
@@ -157,39 +173,36 @@ namespace BetterLyrics.WinUI3.Services
|
||||
StopSSE();
|
||||
}
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
|
||||
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
_SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference);
|
||||
token.ThrowIfCancellationRequested();
|
||||
_SMTCAlbumArtBytes = ImageHelper.Resize(_SMTCAlbumArtBytes, 800);
|
||||
}
|
||||
else
|
||||
{
|
||||
_SMTCAlbumArtBytes = null;
|
||||
}
|
||||
|
||||
_ = _AlbumArtRefreshRunner.RunAsync(async tokne =>
|
||||
await _albumArtRefreshRunner.RunAsync(async tokne =>
|
||||
{
|
||||
await UpdateAlbumArtRelated(tokne);
|
||||
});
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
});
|
||||
}
|
||||
});
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
if (_mediaManager.CurrentMediaSessions.Count == 0)
|
||||
{
|
||||
SendNullMessages();
|
||||
@@ -198,12 +211,19 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
SendFocusedMessagesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void RecordMediaSourceProviderInfo(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
var id = mediaSession?.ControlSession?.SourceAppUserModelId;
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (mediaSession == null) return;
|
||||
|
||||
var id = mediaSession?.Id;
|
||||
if (string.IsNullOrEmpty(id)) return;
|
||||
|
||||
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
|
||||
@@ -211,8 +231,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
_mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id, true));
|
||||
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
MediaSourceProvidersInfoChanged?.Invoke(this, new MediaSourceProvidersInfoEventArgs(_mediaSourceProvidersInfo));
|
||||
});
|
||||
@@ -221,16 +240,27 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private void SendNullMessages()
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedIsPlaying = false;
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(false));
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.Zero));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.Zero, TimeSpan.Zero));
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SendFocusedMessagesAsync()
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession == null || focusedSession.ControlSession == null) return;
|
||||
|
||||
var mediaProps = await focusedSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
MediaManager_OnAnyMediaPropertyChanged(focusedSession, mediaProps);
|
||||
MediaManager_OnAnyPlaybackStateChanged(focusedSession, focusedSession.ControlSession.GetPlaybackInfo());
|
||||
MediaManager_OnAnyTimelinePropertyChanged(focusedSession, focusedSession.ControlSession.GetTimelineProperties());
|
||||
}
|
||||
|
||||
private async Task UpdateAlbumArtRelated(CancellationToken token)
|
||||
{
|
||||
if (_cachedSongInfo == null)
|
||||
@@ -253,6 +283,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
bytes = ImageHelper.MakeSquareWithThemeColor(bytes);
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
token.ThrowIfCancellationRequested();
|
||||
@@ -260,23 +292,35 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_albumArtChangedEventArgs.AlbumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
var _albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_albumArtChangedEventArgs.AlbumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault();
|
||||
var _albumArtLightAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, false).FirstOrDefault();
|
||||
var _albumArtDarkAccentColor = ImageHelper.GetAccentColorsFromByte(bytes, 1, true).FirstOrDefault();
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
AlbumArtChangedChanged?.Invoke(this, _albumArtChangedEventArgs);
|
||||
AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(_albumArtSwBitmap, _albumArtLightAccentColor, _albumArtDarkAccentColor));
|
||||
});
|
||||
}
|
||||
|
||||
private void StartSSE()
|
||||
{
|
||||
_sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}/subscribe-player-status?filter=progress")).Start();
|
||||
_sse.MessageReceived += Sse_MessageReceived;
|
||||
_sse.Disconnected += Sse_Disconnected;
|
||||
try
|
||||
{
|
||||
_sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}/subscribe-player-status?filter=progress,duration")).Start();
|
||||
_sse.MessageReceived += Sse_MessageReceived;
|
||||
_sse.Disconnected += Sse_Disconnected;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.LogError("Failed to start SSE connection for LX Music.");
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("FailToStartLXMusicServer"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
});
|
||||
StopSSE();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopSSE()
|
||||
@@ -301,13 +345,23 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private void Sse_MessageReceived(object sender, EventSourceMessageEventArgs e)
|
||||
{
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
|
||||
if (data.TryGetDouble(out double positionSeconds))
|
||||
if (_cachedSongInfo?.SourceAppUserModelId == _lxMusicId)
|
||||
{
|
||||
if (_cachedSongInfo?.SourceAppUserModelId == _lxMusicId)
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
if (data.ValueKind == JsonValueKind.Number)
|
||||
{
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.FromSeconds(positionSeconds)));
|
||||
if (e.Event == "progress")
|
||||
{
|
||||
_lxMusicPositionSeconds = data.GetDouble();
|
||||
}
|
||||
else if (e.Event == "duration")
|
||||
{
|
||||
_lxMusicDurationSeconds = data.GetDouble();
|
||||
}
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.FromSeconds(_lxMusicPositionSeconds), TimeSpan.FromSeconds(_lxMusicDurationSeconds)));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,7 +371,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TryPlayAsync();
|
||||
await focusedSession.ControlSession?.TryPlayAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +380,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TryPauseAsync();
|
||||
await focusedSession.ControlSession?.TryPauseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +389,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TrySkipPreviousAsync();
|
||||
await focusedSession.ControlSession?.TrySkipPreviousAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +398,16 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TrySkipNextAsync();
|
||||
await focusedSession.ControlSession?.TrySkipNextAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangePosition(double seconds)
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession?.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,12 +419,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
_mediaSourceProvidersInfo = [.. message.NewValue];
|
||||
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
|
||||
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
|
||||
MediaManager_OnFocusedSessionChanged(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>> message)
|
||||
public async void Receive(PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
@@ -369,7 +432,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
// Album art search providers info changed, re-fetch album art
|
||||
_logger.LogInformation("Album art search providers info changed, refreshing album art.");
|
||||
_ = _AlbumArtRefreshRunner.RunAsync(async tokne =>
|
||||
await _albumArtRefreshRunner.RunAsync(async tokne =>
|
||||
{
|
||||
await UpdateAlbumArtRelated(tokne);
|
||||
});
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string CoverOverlayOpacityKey = "CoverOverlayOpacity";
|
||||
private const string IsCoverOverlayEnabledKey = "IsCoverOverlayEnabled";
|
||||
|
||||
private const string CoverAcrylicEffectAmountKey = "CoverAcrylicEffectAmount";
|
||||
|
||||
private const string DesktopWindowLeftKey = "DesktopWindowLeft";
|
||||
private const string DesktopWindowTopKey = "DesktopWindowTop";
|
||||
private const string DesktopWindowWidthKey = "DesktopWindowWidth";
|
||||
@@ -60,7 +62,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private const string LyricsFontStrokeWidthKey = "LyricsFontStrokeWidth";
|
||||
|
||||
private const string LyricsFontSizeKey = "LyricsFontSize";
|
||||
// Lyrics font size
|
||||
private const string LyricsStandardFontSizeKey = "LyricsStandardFontSize";
|
||||
private const string LyricsDockFontSizeKey = "LyricsDockFontSize";
|
||||
private const string LyricsDesktopFontSizeKey = "LyricsDesktopFontSize";
|
||||
|
||||
private const string LyricsFontWeightKey = "LyricsFontWeightKey";
|
||||
private const string LyricsGlowEffectScopeKey = "LyricsGlowEffectScope";
|
||||
private const string LyricsHighlightSopeKey = "LyricsHighlightSope";
|
||||
@@ -71,11 +77,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo";
|
||||
|
||||
// Translation
|
||||
private const string IsTranslationEnabledKey = "IsTranslationEnabled";
|
||||
private const string ShowTranslationOnlyKey = "ShowTranslationOnly";
|
||||
private const string IsLibreTranslateEnabledKey = "IsLibreTranslateEnabled";
|
||||
private const string LibreTranslateServerKey = "LibreTranslateServer";
|
||||
private const string SelectedTargetLanguageIndexKey = "SelectedTargetLanguageIndex";
|
||||
|
||||
// LX Music
|
||||
private const string LXMusicServerKey = "LXMusicServer";
|
||||
|
||||
private const string LyricsBackgroundThemeKey = "LyricsBackgroundTheme";
|
||||
@@ -90,12 +99,21 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string IsLyricsFloatAnimationEnabledKey = "IsLyricsFloatAnimationEnabled";
|
||||
|
||||
private const string ResetPositionOffsetOnSongChangedKey = "ResetPositionOffsetOnSongChanged";
|
||||
private const string PlaybackOrderKey = "PlaybackOrder";
|
||||
|
||||
private const string PositionOffsetKey = "PositionOffset";
|
||||
|
||||
private const string LockHotKeyIndexKey = "LockHotKeyIndex";
|
||||
private const string DockPlacementKey = "DockPlacement";
|
||||
private const string LyricsBgFontOpacityKey = "LyricsBgFontOpacity";
|
||||
private const string HideWindowWhenNotPlayingKey = "HideWindowWhenNotPlaying";
|
||||
private const string DockWindowHeightKey = "DockWindowHeight";
|
||||
|
||||
private const string SelectedFontFamilyIndexKey = "SelectedFontFamilyIndex";
|
||||
private const string LyricsFontFamilyKey = "LyricsFontFamily";
|
||||
private const string IsDragEverywhereEnabledKey = "IsDragEverywhereEnabled";
|
||||
|
||||
private const string DockMonitorDeviceNameKey = "DockMonitorDeviceName";
|
||||
|
||||
private readonly ApplicationDataContainer _localSettings;
|
||||
|
||||
@@ -175,13 +193,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(CoverOverlayOpacityKey, 100); // 100 % = 1.0
|
||||
SetDefault(CoverOverlayBlurAmountKey, 100);
|
||||
SetDefault(CoverImageRadiusKey, 12); // 12 %
|
||||
SetDefault(CoverAcrylicEffectAmountKey, 0);
|
||||
// Lyrics
|
||||
SetDefault(LyricsAlignmentTypeKey, (int)TextAlignmentType.Center);
|
||||
SetDefault(SongInfoAlignmentTypeKey, (int)TextAlignmentType.Left);
|
||||
SetDefault(LyricsFontWeightKey, (int)LyricsFontWeight.Bold);
|
||||
SetDefault(LyricsBlurAmountKey, 5);
|
||||
|
||||
SetDefault(LyricsBackgroundThemeKey, (int)ElementTheme.Default);
|
||||
SetDefault(LyricsBackgroundThemeKey, (int)ElementTheme.Dark);
|
||||
|
||||
SetDefault(LyricsBgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
SetDefault(LyricsFgFontColorTypeKey, (int)LyricsFontColorType.AdaptiveGrayed);
|
||||
@@ -191,7 +210,10 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(LyricsCustomFgFontColorKey, Colors.White.ToInt());
|
||||
SetDefault(LyricsCustomStrokeFontColorKey, Colors.White.ToInt());
|
||||
|
||||
SetDefault(LyricsFontSizeKey, 28);
|
||||
SetDefault(LyricsStandardFontSizeKey, 28);
|
||||
SetDefault(LyricsDockFontSizeKey, 20);
|
||||
SetDefault(LyricsDesktopFontSizeKey, 28);
|
||||
|
||||
SetDefault(LyricsLineSpacingFactorKey, 0.5f);
|
||||
SetDefault(LyricsVerticalEdgeOpacityKey, 0);
|
||||
SetDefault(IsLyricsGlowEffectEnabledKey, true);
|
||||
@@ -200,6 +222,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(IsFanLyricsEnabledKey, false);
|
||||
|
||||
SetDefault(LibreTranslateServerKey, "");
|
||||
SetDefault(IsLibreTranslateEnabledKey, false);
|
||||
SetDefault(IsTranslationEnabledKey, true);
|
||||
SetDefault(ShowTranslationOnlyKey, false);
|
||||
SetDefault(SelectedTargetLanguageIndexKey, LanguageHelper.GetDefaultTargetLanguageIndex());
|
||||
@@ -221,6 +244,42 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(LockHotKeyIndexKey, 'U' - 'A');
|
||||
SetDefault(DockPlacementKey, (int)DockPlacement.Top);
|
||||
SetDefault(LyricsBgFontOpacityKey, 30); // 30%
|
||||
SetDefault(HideWindowWhenNotPlayingKey, false);
|
||||
SetDefault(DockWindowHeightKey, 64); // 64px
|
||||
SetDefault(SelectedFontFamilyIndexKey, 0);
|
||||
SetDefault(LyricsFontFamilyKey, FontHelper.SystemFontFamilies.ElementAtOrDefault(0));
|
||||
SetDefault(IsDragEverywhereEnabledKey, false);
|
||||
SetDefault(DockMonitorDeviceNameKey, MonitorHelper.GetPrimaryMonitorDeviceName());
|
||||
}
|
||||
|
||||
public bool IsDragEverywhereEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsDragEverywhereEnabledKey);
|
||||
set => SetValue(IsDragEverywhereEnabledKey, value);
|
||||
}
|
||||
|
||||
public string LyricsFontFamily
|
||||
{
|
||||
get => GetValue<string>(LyricsFontFamilyKey)!;
|
||||
set => SetValue(LyricsFontFamilyKey, value);
|
||||
}
|
||||
|
||||
public int SelectedFontFamilyIndex
|
||||
{
|
||||
get => GetValue<int>(SelectedFontFamilyIndexKey);
|
||||
set => SetValue(SelectedFontFamilyIndexKey, value);
|
||||
}
|
||||
|
||||
public bool HideWindowWhenNotPlaying
|
||||
{
|
||||
get => GetValue<bool>(HideWindowWhenNotPlayingKey);
|
||||
set => SetValue(HideWindowWhenNotPlayingKey, value);
|
||||
}
|
||||
|
||||
public int DockWindowHeight
|
||||
{
|
||||
get => GetValue<int>(DockWindowHeightKey);
|
||||
set => SetValue(DockWindowHeightKey, value);
|
||||
}
|
||||
|
||||
public int LyricsBgFontOpacity
|
||||
@@ -355,6 +414,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(IsDynamicCoverOverlayEnabledKey, value);
|
||||
}
|
||||
|
||||
public int CoverAcrylicEffectAmount
|
||||
{
|
||||
get => GetValue<int>(CoverAcrylicEffectAmountKey);
|
||||
set => SetValue(CoverAcrylicEffectAmountKey, value);
|
||||
}
|
||||
|
||||
public bool IsFanLyricsEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsFanLyricsEnabledKey);
|
||||
@@ -379,19 +444,19 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(LanguageKey, (int)value);
|
||||
}
|
||||
|
||||
public List<LocalLyricsFolder> LocalLyricsFolders
|
||||
public List<LocalMediaFolder> LocalMediaFolders
|
||||
{
|
||||
get =>
|
||||
System.Text.Json.JsonSerializer.Deserialize(
|
||||
GetValue<string>(LocalLyricsFoldersKey) ?? "[]",
|
||||
SourceGenerationContext.Default.ListLocalLyricsFolder
|
||||
SourceGenerationContext.Default.ListLocalMediaFolder
|
||||
)!;
|
||||
set =>
|
||||
SetValue(
|
||||
LocalLyricsFoldersKey,
|
||||
System.Text.Json.JsonSerializer.Serialize(
|
||||
value,
|
||||
SourceGenerationContext.Default.ListLocalLyricsFolder
|
||||
SourceGenerationContext.Default.ListLocalMediaFolder
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -456,10 +521,22 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(LyricsFontStrokeWidthKey, value);
|
||||
}
|
||||
|
||||
public int LyricsFontSize
|
||||
public int LyricsStandardFontSize
|
||||
{
|
||||
get => GetValue<int>(LyricsFontSizeKey);
|
||||
set => SetValue(LyricsFontSizeKey, value);
|
||||
get => GetValue<int>(LyricsStandardFontSizeKey);
|
||||
set => SetValue(LyricsStandardFontSizeKey, value);
|
||||
}
|
||||
|
||||
public int LyricsDockFontSize
|
||||
{
|
||||
get => GetValue<int>(LyricsDockFontSizeKey);
|
||||
set => SetValue(LyricsDockFontSizeKey, value);
|
||||
}
|
||||
|
||||
public int LyricsDesktopFontSize
|
||||
{
|
||||
get => GetValue<int>(LyricsDesktopFontSizeKey);
|
||||
set => SetValue(LyricsDesktopFontSizeKey, value);
|
||||
}
|
||||
|
||||
public LyricsFontWeight LyricsFontWeight
|
||||
@@ -555,6 +632,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(IsTranslationEnabledKey, value);
|
||||
}
|
||||
|
||||
public bool IsLibreTranslateEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsLibreTranslateEnabledKey);
|
||||
set => SetValue(IsLibreTranslateEnabledKey, value);
|
||||
}
|
||||
|
||||
public int SelectedTargetLanguageIndex
|
||||
{
|
||||
get => GetValue<int>(SelectedTargetLanguageIndexKey);
|
||||
@@ -591,6 +674,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(ResetPositionOffsetOnSongChangedKey, value);
|
||||
}
|
||||
|
||||
public PlaybackOrder PlaybackOrder
|
||||
{
|
||||
get => (PlaybackOrder)GetValue<int>(PlaybackOrderKey);
|
||||
set => SetValue(PlaybackOrderKey, (int)value);
|
||||
}
|
||||
|
||||
public int PositionOffset
|
||||
{
|
||||
get => GetValue<int>(PositionOffsetKey);
|
||||
@@ -603,6 +692,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(IsImmersiveModeKey, value);
|
||||
}
|
||||
|
||||
public string DockMonitorDeviceName
|
||||
{
|
||||
get => GetValue<string>(DockMonitorDeviceNameKey)!;
|
||||
set => SetValue(DockMonitorDeviceNameKey, value);
|
||||
}
|
||||
|
||||
private T? GetValue<T>(string key)
|
||||
{
|
||||
if (_localSettings.Values.TryGetValue(key, out object? value))
|
||||
|
||||
@@ -3,6 +3,7 @@ using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -27,7 +28,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
throw new ArgumentException("Text and target language must be provided.");
|
||||
throw new Exception(text + " is empty or null.");
|
||||
}
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(text);
|
||||
@@ -46,15 +47,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString("TranslateServerNotSet"),
|
||||
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
|
||||
);
|
||||
});
|
||||
|
||||
throw new InvalidOperationException("LibreTranslate server URL is not configured.");
|
||||
throw new Exception("LibreTranslate server URL is not set in settings.");
|
||||
}
|
||||
|
||||
var url = $"{_settingsService.LibreTranslateServer}/translate";
|
||||
|
||||
@@ -264,8 +264,14 @@
|
||||
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
|
||||
<value>x line height</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
|
||||
<value>Font size</value>
|
||||
<data name="SettingsPageLyricsStandardFontSize.Header" xml:space="preserve">
|
||||
<value>Font size (Standard mode)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDockFontSize.Header" xml:space="preserve">
|
||||
<value>Font size (Dock mode)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDesktopFontSize.Header" xml:space="preserve">
|
||||
<value>Font size (Desktop mode)</value>
|
||||
</data>
|
||||
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
|
||||
<value>Lyrics only</value>
|
||||
@@ -481,9 +487,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
|
||||
<value>Local music files</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
|
||||
<value>LRCLIB</value>
|
||||
</data>
|
||||
<data name="SettingsPageJA.Content" xml:space="preserve">
|
||||
<value>日本語</value>
|
||||
</data>
|
||||
@@ -499,18 +502,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>This folder contains added folders, please delete these folders to add the folder</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
|
||||
<value>amll-ttml-db</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderQQ" xml:space="preserve">
|
||||
<value>QQ</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderNetease" xml:space="preserve">
|
||||
<value>Netease</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderKugou" xml:space="preserve">
|
||||
<value>Kugou</value>
|
||||
</data>
|
||||
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
|
||||
<value>Show debug overlay</value>
|
||||
</data>
|
||||
@@ -593,7 +584,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Lyrics timeline offset (ms)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>Configure translation services</value>
|
||||
<value>LibreTranslate translation service</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>Server address</value>
|
||||
@@ -772,4 +763,157 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>Frequently asked questions</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>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>Auto-hide window</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
|
||||
<value>Automatically hide the window when no songs are playing in dock mode or desktop mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
|
||||
<value>Window height</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
|
||||
<value>Lyrics font family</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
|
||||
<value>Global drag</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
|
||||
<value>Extend the title bar to the entire page so that the window can be dragged in any non-interactive area</value>
|
||||
</data>
|
||||
<data name="SettingsPageBackgroundAcrylicEffectAmount.Header" xml:space="preserve">
|
||||
<value>Fluid acrylic effect roughness</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageTitle" xml:space="preserve">
|
||||
<value>Music gallery - BetterLyrics</value>
|
||||
</data>
|
||||
<data name="LibreTranslateFailed" xml:space="preserve">
|
||||
<value>Requesting translation from LibreTranslate failed, please check the settings or the native LibreTranslate configuration</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>File info</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<value>Year</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<value>Bitrate</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<value>Sample rate</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<value>Bit depth</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<value>Encoder</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<value>Path</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>Search songs</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
|
||||
<value>Sort type</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>No songs were found in the media library</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>Play queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
|
||||
<value>Clear play queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
|
||||
<value>Scroll to playing item</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
|
||||
<value>List loop</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
|
||||
<value>Single loop</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
|
||||
<value>Random</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
|
||||
<value>Remove from play queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
|
||||
<value>Play queue is empty</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
|
||||
<value>Select all</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
|
||||
<value>Add to play queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
|
||||
<value>Next items</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
|
||||
<value>End of the list</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
|
||||
<value>Add to playlist</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>Create a playlist</value>
|
||||
</data>
|
||||
<data name="SystemTrayMusicGallery.Text" xml:space="preserve">
|
||||
<value>Open music gallery</value>
|
||||
</data>
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics is already running</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>All songs</value>
|
||||
</data>
|
||||
<data name="SettingsPageTelegram.Header" xml:space="preserve">
|
||||
<value>Telegram</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<value>Lyrics provider</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<value>Translation provider</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockMonitor.Header" xml:space="preserve">
|
||||
<value>Target monitor</value>
|
||||
</data>
|
||||
<data name="SystemTrayRestart.Text" xml:space="preserve">
|
||||
<value>Resart</value>
|
||||
</data>
|
||||
<data name="SystemTrayResetWindowPosition.Text" xml:space="preserve">
|
||||
<value>Reset window position</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -250,7 +250,7 @@
|
||||
<value>この値を調整すると、アルバム画像のバックグラウンドブラー強度も増加します。</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>現在の値:</value>
|
||||
<value>現在の値: </value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>ぼかしが有効になっている場合のGPU使用量が大幅に高くなります(> 0)</value>
|
||||
@@ -264,8 +264,14 @@
|
||||
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
|
||||
<value>x線の高さ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
|
||||
<value>フォントサイズ</value>
|
||||
<data name="SettingsPageLyricsStandardFontSize.Header" xml:space="preserve">
|
||||
<value>フォントサイズ(標準モード)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDockFontSize.Header" xml:space="preserve">
|
||||
<value>フォントサイズ(ドックモード)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDesktopFontSize.Header" xml:space="preserve">
|
||||
<value>フォントサイズ(デスクトップモード)</value>
|
||||
</data>
|
||||
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
|
||||
<value>歌詞のみ</value>
|
||||
@@ -481,9 +487,6 @@
|
||||
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
|
||||
<value>ローカル音楽ファイル</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
|
||||
<value>LRCLIB</value>
|
||||
</data>
|
||||
<data name="SettingsPageJA.Content" xml:space="preserve">
|
||||
<value>日本語</value>
|
||||
</data>
|
||||
@@ -499,18 +502,6 @@
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>このフォルダーには追加されたフォルダーが含まれています。これらのフォルダを削除してフォルダーを追加してください</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
|
||||
<value>amll-ttml-db</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderQQ" xml:space="preserve">
|
||||
<value>QQ</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderNetease" xml:space="preserve">
|
||||
<value>Netease</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderKugou" xml:space="preserve">
|
||||
<value>Kugou</value>
|
||||
</data>
|
||||
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
|
||||
<value>デバッグオーバーレイを表示します</value>
|
||||
</data>
|
||||
@@ -593,7 +584,7 @@
|
||||
<value>歌詞タイムラインオフセット(ミリ秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>翻訳サービスを構成します</value>
|
||||
<value>リブレットランレート翻訳サービス</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>サーバーアドレス</value>
|
||||
@@ -772,4 +763,157 @@
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>よくある質問</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Serverに接続できない場合は、設定に移動してください。リンクが正しく入力されているかどうかを確認するための高度なオプションに移動してください</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>自動ハイドウィンドウ</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
|
||||
<value>ドックモードやデスクトップモードで再生されている曲がないときにウィンドウを自動的に非表示にする</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
|
||||
<value>窓の高さ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
|
||||
<value>歌詞フォントファミリー</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
|
||||
<value>グローバルドラッグ</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
|
||||
<value>タイトルバーをページ全体に拡張して、ウィンドウを非対話領域でドラッグできるようにします</value>
|
||||
</data>
|
||||
<data name="SettingsPageBackgroundAcrylicEffectAmount.Header" xml:space="preserve">
|
||||
<value>仿亚克力效果粗糙度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageTitle" xml:space="preserve">
|
||||
<value>音楽ギャラリー - BetterLyrics</value>
|
||||
</data>
|
||||
<data name="LibreTranslateFailed" xml:space="preserve">
|
||||
<value>リブレットランスレートからの翻訳のリクエストが失敗しました。設定またはネイティブリブレットランレート構成を確認してください</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>ファイル情報</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<value>タイトル</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<value>アルバム</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<value>間隔</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<value>ビットレート</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<value>サンプルレート</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<value>ビットの深さ</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<value>形式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<value>エンコーダー</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<value>パス</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>曲を検索します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
|
||||
<value>並べ替えタイプ</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
|
||||
<value>タイトル</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
|
||||
<value>アルバム</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>メディアライブラリには歌が見つかりませんでした</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>キューを再生します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
|
||||
<value>クリアプレイキュー</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
|
||||
<value>アイテムを再生するための位置</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
|
||||
<value>ループをリストします</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
|
||||
<value>シングルループ</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
|
||||
<value>ランダム</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
|
||||
<value>プレイリストから取り外します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
|
||||
<value>キューを再生するのは空です</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
|
||||
<value>すべてを選択します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
|
||||
<value>キューを再生するために追加します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
|
||||
<value>次のアイテム</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
|
||||
<value>リストの終わり</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
|
||||
<value>プレイリストに追加します</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>プレイリストを作成します</value>
|
||||
</data>
|
||||
<data name="SystemTrayMusicGallery.Text" xml:space="preserve">
|
||||
<value>オープンミュージックギャラリー</value>
|
||||
</data>
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics はすでに実行されています!</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>すべての曲</value>
|
||||
</data>
|
||||
<data name="SettingsPageTelegram.Header" xml:space="preserve">
|
||||
<value>Telegram</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<value>歌詞プロバイダー</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<value>翻訳プロバイダー</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockMonitor.Header" xml:space="preserve">
|
||||
<value>ターゲットモニター</value>
|
||||
</data>
|
||||
<data name="SystemTrayRestart.Text" xml:space="preserve">
|
||||
<value>再アート</value>
|
||||
</data>
|
||||
<data name="SystemTrayResetWindowPosition.Text" xml:space="preserve">
|
||||
<value>ウィンドウの位置をリセットします</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -250,7 +250,7 @@
|
||||
<value>이 값을 조정하면 앨범 이미지의 배경 흐림 강도가 증가합니다.</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>현재 가치 :</value>
|
||||
<value>현재 가치 : </value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>Blur가 활성화 될 때 상당히 높은 GPU 사용량 (> 0)</value>
|
||||
@@ -264,8 +264,14 @@
|
||||
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
|
||||
<value>X 라인 높이</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
|
||||
<value>글꼴 크기</value>
|
||||
<data name="SettingsPageLyricsStandardFontSize.Header" xml:space="preserve">
|
||||
<value>글꼴 크기 (표준 모드)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDockFontSize.Header" xml:space="preserve">
|
||||
<value>글꼴 크기 (도크 모드)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDesktopFontSize.Header" xml:space="preserve">
|
||||
<value>글꼴 크기 (데스크탑 모드)</value>
|
||||
</data>
|
||||
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
|
||||
<value>가사 만</value>
|
||||
@@ -481,9 +487,6 @@
|
||||
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
|
||||
<value>로컬 음악 파일</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
|
||||
<value>LRCLIB</value>
|
||||
</data>
|
||||
<data name="SettingsPageJA.Content" xml:space="preserve">
|
||||
<value>日本語</value>
|
||||
</data>
|
||||
@@ -499,18 +502,6 @@
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>이 폴더에는 추가 된 폴더가 포함되어 있습니다. 폴더를 추가하려면이 폴더를 삭제하십시오.</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
|
||||
<value>amll-ttml-db</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderQQ" xml:space="preserve">
|
||||
<value>QQ</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderNetease" xml:space="preserve">
|
||||
<value>Netease</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderKugou" xml:space="preserve">
|
||||
<value>Kugou</value>
|
||||
</data>
|
||||
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
|
||||
<value>디버그 오버레이를 표시하십시오</value>
|
||||
</data>
|
||||
@@ -593,7 +584,7 @@
|
||||
<value>가사 타임 라인 오프셋 (밀리초)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>번역 서비스를 구성하십시오</value>
|
||||
<value>libretranslate 번역 서비스</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>서버 주소</value>
|
||||
@@ -772,4 +763,157 @@
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>자주 묻는 질문</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Server에 연결할 수 없습니다. 설정으로 이동하십시오 - 고급 옵션이 링크가 올바르게 입력되었는지 확인하십시오.</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>자동 가죽 창</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
|
||||
<value>도크 모드 또는 데스크탑 모드에서 재생되지 않을 때는 창을 자동으로 숨기고 있습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
|
||||
<value>창 높이</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
|
||||
<value>가사 글꼴 가족</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
|
||||
<value>글로벌 드래그</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
|
||||
<value>비 중과 영역에서 창을 드래그 할 수 있도록 제목 표시 줄을 전체 페이지로 확장하십시오.</value>
|
||||
</data>
|
||||
<data name="SettingsPageBackgroundAcrylicEffectAmount.Header" xml:space="preserve">
|
||||
<value>仿亚克力效果粗糙度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageTitle" xml:space="preserve">
|
||||
<value>음악 갤러리 - BetterLyrics</value>
|
||||
</data>
|
||||
<data name="LibreTranslateFailed" xml:space="preserve">
|
||||
<value>LibreTranslate에서 번역 요청 실패, 설정 또는 기본 LibreTranslate 구성을 확인하십시오.</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>파일 정보</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<value>제목</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<value>앨범</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<value>년도</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<value>지속</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<value>비트 레이트</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<value>샘플 속도</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<value>비트 깊이</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<value>체재</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<value>인코더</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<value>길</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>노래 검색</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
|
||||
<value>정렬 유형</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
|
||||
<value>제목</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
|
||||
<value>앨범</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>미디어 라이브러리에는 노래가 없습니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>대기열을 재생하십시오</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
|
||||
<value>플레이 대기열을 클리어합니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
|
||||
<value>아이템을 재생하기위한 포지셔닝</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
|
||||
<value>목록 루프</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
|
||||
<value>단일 루프</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
|
||||
<value>무작위의</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
|
||||
<value>재생 목록에서 제거하십시오</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
|
||||
<value>플레이 대기열이 비어 있습니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
|
||||
<value>모두를 선택하십시오</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
|
||||
<value>재생 큐에 추가하십시오</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
|
||||
<value>다음 항목</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
|
||||
<value>목록의 끝</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
|
||||
<value>재생 목록에 추가하십시오</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>재생 목록을 만듭니다</value>
|
||||
</data>
|
||||
<data name="SystemTrayMusicGallery.Text" xml:space="preserve">
|
||||
<value>오픈 음악 갤러리</value>
|
||||
</data>
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 가 이미 실행 중입니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>모든 노래</value>
|
||||
</data>
|
||||
<data name="SettingsPageTelegram.Header" xml:space="preserve">
|
||||
<value>Telegram</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<value>가사 제공자</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<value>번역 제공자</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockMonitor.Header" xml:space="preserve">
|
||||
<value>대상 모니터</value>
|
||||
</data>
|
||||
<data name="SystemTrayRestart.Text" xml:space="preserve">
|
||||
<value>resart</value>
|
||||
</data>
|
||||
<data name="SystemTrayResetWindowPosition.Text" xml:space="preserve">
|
||||
<value>창 위치를 재설정합니다</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -250,7 +250,7 @@
|
||||
<value>调整该数值将同步提高专辑图片背景模糊强度</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>当前值:</value>
|
||||
<value>当前值: </value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>启用模糊(> 0)时将显著提升 GPU 占用率</value>
|
||||
@@ -264,8 +264,14 @@
|
||||
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
|
||||
<value> 倍行高</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
|
||||
<value>字体大小</value>
|
||||
<data name="SettingsPageLyricsStandardFontSize.Header" xml:space="preserve">
|
||||
<value>字体尺寸(标准模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDockFontSize.Header" xml:space="preserve">
|
||||
<value>字体大小(停靠模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDesktopFontSize.Header" xml:space="preserve">
|
||||
<value>字体尺寸(桌面模式)</value>
|
||||
</data>
|
||||
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
|
||||
<value>仅显示歌词</value>
|
||||
@@ -481,9 +487,6 @@
|
||||
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
|
||||
<value>本地音乐文件</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
|
||||
<value>LRCLIB</value>
|
||||
</data>
|
||||
<data name="SettingsPageJA.Content" xml:space="preserve">
|
||||
<value>日本語</value>
|
||||
</data>
|
||||
@@ -499,18 +502,6 @@
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>该文件夹包含已添加文件夹,请删除这些文件夹以添加该文件夹</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
|
||||
<value>amll-ttml-db</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderQQ" xml:space="preserve">
|
||||
<value>QQ</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderNetease" xml:space="preserve">
|
||||
<value>Netease</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderKugou" xml:space="preserve">
|
||||
<value>Kugou</value>
|
||||
</data>
|
||||
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
|
||||
<value>显示调试覆盖层</value>
|
||||
</data>
|
||||
@@ -593,7 +584,7 @@
|
||||
<value>歌词时间轴偏移(毫秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>配置翻译服务</value>
|
||||
<value>LibreTranslate 翻译服务</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>服务器地址</value>
|
||||
@@ -650,7 +641,7 @@
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="TranslateServerNotSet" xml:space="preserve">
|
||||
<value>未设置翻译服务器,请先在设置中进行配置</value>
|
||||
<value>未设置Translate服务器,请先在设置中进行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>切换歌曲时重置为 0</value>
|
||||
@@ -772,4 +763,157 @@
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>常见问题与解答</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>无法连接到 LX 音乐服务器,请转到设置 - 高级选项以检查是否正确输入链接</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>自动隐藏窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
|
||||
<value>停靠模式或桌面模式下,无歌曲正在播放时,自动隐藏窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
|
||||
<value>窗口高度</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
|
||||
<value>歌词字体</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
|
||||
<value>全局拖拽</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
|
||||
<value>将标题栏扩展至整个页面使得在任意非交互区域均可拖拽窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageBackgroundAcrylicEffectAmount.Header" xml:space="preserve">
|
||||
<value>仿亚克力效果粗糙度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageTitle" xml:space="preserve">
|
||||
<value>音乐库 - BetterLyrics</value>
|
||||
</data>
|
||||
<data name="LibreTranslateFailed" xml:space="preserve">
|
||||
<value>向 LibreTranslate 请求翻译失败,请检查设置或本机 LibreTranslate 配置</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>文件信息</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<value>标题</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<value>期间</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<value>比特率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<value>样本率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<value>位深度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<value>格式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<value>编码器</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<value>路径</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>搜索歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
|
||||
<value>排序类型</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
|
||||
<value>标题</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>未在媒体库内找到任何歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>播放队列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
|
||||
<value>清除播放队列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
|
||||
<value>定位到播放项</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
|
||||
<value>列表循环</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
|
||||
<value>单曲循环</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
|
||||
<value>随机</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
|
||||
<value>从播放列表移除</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
|
||||
<value>播放队列是空的</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
|
||||
<value>选择全部</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
|
||||
<value>添加到播放队列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
|
||||
<value>下一个项目</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
|
||||
<value>列表的结尾</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
|
||||
<value>添加到歌单</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>创建歌单</value>
|
||||
</data>
|
||||
<data name="SystemTrayMusicGallery.Text" xml:space="preserve">
|
||||
<value>打开音乐库</value>
|
||||
</data>
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 已经在运行</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>所有歌曲</value>
|
||||
</data>
|
||||
<data name="SettingsPageTelegram.Header" xml:space="preserve">
|
||||
<value>Telegram</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<value>歌词来源</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<value>翻译来源</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockMonitor.Header" xml:space="preserve">
|
||||
<value>目标显示器</value>
|
||||
</data>
|
||||
<data name="SystemTrayRestart.Text" xml:space="preserve">
|
||||
<value>重新启动</value>
|
||||
</data>
|
||||
<data name="SystemTrayResetWindowPosition.Text" xml:space="preserve">
|
||||
<value>重置窗口位置</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="SettingsPageMusicLib.Header" xml:space="preserve">
|
||||
<value>本地媒體庫</value>
|
||||
<value>本地音樂資料庫</value>
|
||||
</data>
|
||||
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
|
||||
<value>新增存放音樂或歌詞的資料夾</value>
|
||||
@@ -127,7 +127,7 @@
|
||||
<value>在檔案總管中開啟</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
|
||||
<value>從應用程式中移除</value>
|
||||
<value>從應用程序中移除</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Title" xml:space="preserve">
|
||||
<value>您可以安全刪除以下項目</value>
|
||||
@@ -250,7 +250,7 @@
|
||||
<value>調整此數值將同步提升專輯圖片背景模糊強度</value>
|
||||
</data>
|
||||
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
|
||||
<value>目前值:</value>
|
||||
<value>目前值: </value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
|
||||
<value>啟用模糊(> 0)時將顯著提升 GPU 佔用率</value>
|
||||
@@ -264,8 +264,14 @@
|
||||
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
|
||||
<value> 倍行高</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
|
||||
<value>字體大小</value>
|
||||
<data name="SettingsPageLyricsStandardFontSize.Header" xml:space="preserve">
|
||||
<value>字體尺寸(標準模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDockFontSize.Header" xml:space="preserve">
|
||||
<value>字體大小(停靠模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsDesktopFontSize.Header" xml:space="preserve">
|
||||
<value>字體尺寸(桌面模式)</value>
|
||||
</data>
|
||||
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
|
||||
<value>僅顯示歌詞</value>
|
||||
@@ -481,9 +487,6 @@
|
||||
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
|
||||
<value>本地音樂文件</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
|
||||
<value>LRCLIB</value>
|
||||
</data>
|
||||
<data name="SettingsPageJA.Content" xml:space="preserve">
|
||||
<value>日本語</value>
|
||||
</data>
|
||||
@@ -499,18 +502,6 @@
|
||||
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
|
||||
<value>該文件夾包含已添加文件夾,請刪除這些文件夾以添加該文件夾</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
|
||||
<value>amll-ttml-db</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderQQ" xml:space="preserve">
|
||||
<value>QQ</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderNetease" xml:space="preserve">
|
||||
<value>Netease</value>
|
||||
</data>
|
||||
<data name="LyricsSearchProviderKugou" xml:space="preserve">
|
||||
<value>Kugou</value>
|
||||
</data>
|
||||
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
|
||||
<value>顯示調試覆蓋層</value>
|
||||
</data>
|
||||
@@ -572,7 +563,7 @@
|
||||
<value>歌曲標題和藝術家</value>
|
||||
</data>
|
||||
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
|
||||
<value>缓动动画类型</value>
|
||||
<value>緩動動畫類型</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
|
||||
<value>播放來源</value>
|
||||
@@ -590,10 +581,10 @@
|
||||
<value>歌詞翻譯</value>
|
||||
</data>
|
||||
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
|
||||
<value>歌詞時間軸偏移(毫秒)</value>
|
||||
<value>歌詞時間軌偏移(毫秒)</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
|
||||
<value>配置翻譯服務</value>
|
||||
<value>LibreTranslate 翻譯服務</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
|
||||
<value>服務器地址</value>
|
||||
@@ -605,7 +596,7 @@
|
||||
<value>目標語言</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
|
||||
<value>翻译服务由 LibreTranslate 驱动</value>
|
||||
<value>翻譯服務由 LibreTranslate 驅動</value>
|
||||
</data>
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>造訪 https://github.com/LibreTranslate/LibreTranslate 以取得安裝教學及更多資訊(本軟體與此翻譯服務無任何關聯)</value>
|
||||
@@ -620,13 +611,13 @@
|
||||
<value>歌詞描邊寬度(僅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
|
||||
<value>跟随系统</value>
|
||||
<value>跟隨系統</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
|
||||
<value>自動啟動</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
|
||||
<value>歌词描边颜色(仅桌面模式)</value>
|
||||
<value>歌詞描邊顏色(僅桌面模式)</value>
|
||||
</data>
|
||||
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
|
||||
<value>始終顯示在全螢幕應用程式上方</value>
|
||||
@@ -725,7 +716,7 @@
|
||||
<value>高亮顯示範圍</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
|
||||
<value>歌詞時間軸同步閾值</value>
|
||||
<value>歌詞時間軌同步閾值</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
|
||||
<value>當歌詞進度抖動時,請嘗試增加該閾值;更改此值會導致歌詞同步偏差</value>
|
||||
@@ -772,4 +763,157 @@
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>常見問題與解答</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>無法連接到 LX 音樂服務器,請轉到設置 - 高級選項以檢查是否正確輸入鏈接</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>自動隱藏窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
|
||||
<value>停靠模式或桌面模式下,無歌曲正在播放時,自動隱藏窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
|
||||
<value>窗口高度</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
|
||||
<value>歌詞字體</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
|
||||
<value>全域拖曳</value>
|
||||
</data>
|
||||
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
|
||||
<value>將標題列擴展至整個頁面使得在任意非互動區域均可拖曳窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageBackgroundAcrylicEffectAmount.Header" xml:space="preserve">
|
||||
<value>仿亚克力效果粗糙度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageTitle" xml:space="preserve">
|
||||
<value>音樂庫 - BetterLyrics</value>
|
||||
</data>
|
||||
<data name="LibreTranslateFailed" xml:space="preserve">
|
||||
<value>向 LibreTranslate 請求翻譯失敗,請檢查設置或本機 LibreTranslate 配置</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>文件信息</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<value>標題</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<value>期間</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<value>比特率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<value>樣本率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<value>位深度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<value>格式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<value>編碼器</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<value>路徑</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
|
||||
<value>搜索歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortType.Text" xml:space="preserve">
|
||||
<value>排序類型</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByTitle.Content" xml:space="preserve">
|
||||
<value>標題</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSortByArtist.Content" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
<value>未在媒體庫內找到任何歌曲</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueue.Text" xml:space="preserve">
|
||||
<value>清除播放</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Content" xml:space="preserve">
|
||||
<value>清除播放隊列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageScrollToPlayingItem.Content" xml:space="preserve">
|
||||
<value>定位到播放項</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueLoop.Content" xml:space="preserve">
|
||||
<value>列表循環</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSingleLoop.Content" xml:space="preserve">
|
||||
<value>單曲循環</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageQueueRandom.Content" xml:space="preserve">
|
||||
<value>隨機</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageRemoveFromPlayingQueue.Text" xml:space="preserve">
|
||||
<value>從播放列表移除</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPagePlayingQueueEmpty.Text" xml:space="preserve">
|
||||
<value>播放隊列是空的</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageSelectAll.Content" xml:space="preserve">
|
||||
<value>選擇全部</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToPlayingQueue.Content" xml:space="preserve">
|
||||
<value>新增到播放隊列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToNext.Text" xml:space="preserve">
|
||||
<value>下一個項目</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToEnd.Text" xml:space="preserve">
|
||||
<value>列表的結尾</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAddToCustomList.Content" xml:space="preserve">
|
||||
<value>添加到歌單</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageNewPlaylist.Text" xml:space="preserve">
|
||||
<value>建立歌單</value>
|
||||
</data>
|
||||
<data name="SystemTrayMusicGallery.Text" xml:space="preserve">
|
||||
<value>開啟音樂庫</value>
|
||||
</data>
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 已經在運作</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageAllSongs" xml:space="preserve">
|
||||
<value>所有歌曲</value>
|
||||
</data>
|
||||
<data name="SettingsPageTelegram.Header" xml:space="preserve">
|
||||
<value>Telegram</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<value>歌詞來源</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<value>翻譯來源</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockMonitor.Header" xml:space="preserve">
|
||||
<value>目標顯示器</value>
|
||||
</data>
|
||||
<data name="SystemTrayRestart.Text" xml:space="preserve">
|
||||
<value>重新啟動</value>
|
||||
</data>
|
||||
<data name="SystemTrayResetWindowPosition.Text" xml:space="preserve">
|
||||
<value>重置窗口位置</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,25 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace BetterLyrics.WinUI3.TemplateSelector;
|
||||
|
||||
public class SongOrderTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate ByTitleTemplate { get; set; }
|
||||
public DataTemplate ByAlbumTemplate { get; set; }
|
||||
public DataTemplate ByArtistTemplate { get; set; }
|
||||
|
||||
public CommonSongProperty SongOrderType { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
return SongOrderType switch
|
||||
{
|
||||
CommonSongProperty.Title => ByTitleTemplate,
|
||||
CommonSongProperty.Album => ByAlbumTemplate,
|
||||
CommonSongProperty.Artist => ByArtistTemplate,
|
||||
_ => ByTitleTemplate
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,26 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class BaseViewModel : ObservableRecipient, IDisposable
|
||||
public partial class BaseViewModel : ObservableRecipient
|
||||
{
|
||||
private protected readonly DispatcherQueue _dispatcherQueue =
|
||||
DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
private protected readonly DispatcherQueue _dispatcherQueue;
|
||||
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
|
||||
private protected readonly ISettingsService _settingsService;
|
||||
|
||||
public BaseViewModel(ISettingsService settingsService)
|
||||
{
|
||||
IsActive = true;
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
|
||||
_settingsService = settingsService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,33 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsPageViewModel : BaseViewModel, IRecipient<PropertyChangedMessage<bool>>
|
||||
public partial class LyricsPageViewModel : BaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<TimeSpan>>,
|
||||
IRecipient<PropertyChangedMessage<LyricsSearchProvider?>>,
|
||||
IRecipient<PropertyChangedMessage<TranslationSearchProvider?>>
|
||||
{
|
||||
private readonly IPlaybackService _playbackService;
|
||||
private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1));
|
||||
|
||||
private bool _isDockMode = false;
|
||||
private bool _isDesktopMode = false;
|
||||
|
||||
private int _lyricsStandardFontSize = 8;
|
||||
private int _lyricsDockFontSize = 8;
|
||||
private int _lyricsDesktopFontSize = 8;
|
||||
|
||||
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
|
||||
{
|
||||
@@ -28,6 +47,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
ShowTranslationOnly = _settingsService.ShowTranslationOnly;
|
||||
|
||||
UpdateHintMessageFontSize();
|
||||
|
||||
LyricsFontFamily = _settingsService.LyricsFontFamily;
|
||||
|
||||
OnIsImmersiveModeChanged(IsImmersiveMode);
|
||||
|
||||
//Volume = SystemVolumeHelper.GetMasterVolume();
|
||||
@@ -36,6 +59,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_playbackService = playbackService;
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
_playbackService.TimelineChanged += PlaybackService_TimelineChanged;
|
||||
|
||||
IsSongPlaying = _playbackService.IsPlaying;
|
||||
}
|
||||
|
||||
private void PlaybackService_TimelineChanged(object? sender, Events.TimelineChangedEventArgs e)
|
||||
{
|
||||
SongDurationSeconds = (int)e.End.TotalSeconds;
|
||||
}
|
||||
|
||||
//private void SystemVolumeHelper_VolumeChanged(int volume)
|
||||
@@ -51,14 +82,27 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
|
||||
{
|
||||
SongInfo = e.SongInfo;
|
||||
SongDurationSeconds = SongInfo?.Duration ?? 0;
|
||||
if (ResetPositionOffsetOnSongChanged)
|
||||
{
|
||||
PositionOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//[ObservableProperty]
|
||||
//public partial int Volume { get; set; }
|
||||
[ObservableProperty]
|
||||
public partial double TimelinePositionSeconds { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int SongDurationSeconds { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int Volume { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string LyricsFontFamily { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int HintMessageFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsImmersiveMode { get; set; }
|
||||
@@ -67,7 +111,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial float BottomCommandGridOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Thickness BottomCommandGridMargin { get; set; } = new Thickness(12);
|
||||
public partial float BottomCommandFlyoutTriggerOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
@@ -101,12 +145,35 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial bool IsSongPlaying { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
|
||||
|
||||
private void UpdateHintMessageFontSize()
|
||||
{
|
||||
if (_isDockMode)
|
||||
{
|
||||
HintMessageFontSize = _settingsService.LyricsDockFontSize;
|
||||
}
|
||||
else if (_isDesktopMode)
|
||||
{
|
||||
HintMessageFontSize = _settingsService.LyricsDesktopFontSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
HintMessageFontSize = _settingsService.LyricsStandardFontSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is LyricsWindowViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.IsDockMode))
|
||||
{
|
||||
_isDockMode = message.NewValue;
|
||||
if (message.NewValue)
|
||||
{
|
||||
DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
@@ -115,10 +182,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
DisplayType = _settingsService.DisplayType;
|
||||
}
|
||||
BottomCommandGridMargin = message.NewValue ? new Thickness(0) : new Thickness(12);
|
||||
UpdateHintMessageFontSize();
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
|
||||
{
|
||||
_isDesktopMode = message.NewValue;
|
||||
if (message.NewValue)
|
||||
{
|
||||
DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
@@ -127,6 +195,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
DisplayType = _settingsService.DisplayType;
|
||||
}
|
||||
UpdateHintMessageFontSize();
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsImmersiveMode))
|
||||
{
|
||||
@@ -138,7 +207,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private static void OpenSettingsWindow()
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
WindowHelper.OpenWindow<SettingsWindow>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -186,10 +255,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (value)
|
||||
{
|
||||
BottomCommandGridOpacity = 0f;
|
||||
BottomCommandFlyoutTriggerOpacity = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
BottomCommandGridOpacity = 1f;
|
||||
BottomCommandFlyoutTriggerOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,9 +269,78 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_settingsService.ShowTranslationOnly = value;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<int> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStandardFontSize))
|
||||
{
|
||||
UpdateHintMessageFontSize();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDockFontSize))
|
||||
{
|
||||
UpdateHintMessageFontSize();
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDesktopFontSize))
|
||||
{
|
||||
UpdateHintMessageFontSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontFamily))
|
||||
{
|
||||
LyricsFontFamily = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//partial void OnVolumeChanged(int value)
|
||||
//{
|
||||
// SystemVolumeHelper.SetMasterVolume(value);
|
||||
//}
|
||||
|
||||
public void Receive(PropertyChangedMessage<TimeSpan> message)
|
||||
{
|
||||
if (message.Sender is LyricsRendererViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsRendererViewModel.TotalTime))
|
||||
{
|
||||
if (_timelineThrottle.CanTrigger())
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TimelinePositionSeconds = message.NewValue.TotalSeconds;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
|
||||
{
|
||||
if (message.Sender is LyricsRendererViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsRendererViewModel.LyricsSearchProvider))
|
||||
{
|
||||
LyricsSearchProvider = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<TranslationSearchProvider?> message)
|
||||
{
|
||||
if (message.Sender is LyricsRendererViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsRendererViewModel.TranslationSearchProvider))
|
||||
{
|
||||
TranslationSearchProvider = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -20,16 +21,23 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
_albumArtBgOpacity = _settingsService.CoverOverlayOpacity;
|
||||
_albumArtBgBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
_coverAcrylicEffectAmount = _settingsService.CoverAcrylicEffectAmount;
|
||||
|
||||
_lyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
_lyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
|
||||
_lyricsTextFormat.FontWeight = _settingsService.LyricsFontWeight.ToFontWeight();
|
||||
|
||||
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = _settingsService.LyricsFontFamily;
|
||||
|
||||
_lyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
_lyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
_lyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
_lyricsFontSize = _settingsService.LyricsFontSize;
|
||||
|
||||
_lyricsStandardFontSize = _settingsService.LyricsStandardFontSize;
|
||||
_lyricsDockFontSize = _settingsService.LyricsDockFontSize;
|
||||
_lyricsDesktopFontSize = _settingsService.LyricsDesktopFontSize;
|
||||
|
||||
_lyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
_isLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
_lyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
@@ -44,6 +52,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
_showTranslationOnly = _settingsService.ShowTranslationOnly;
|
||||
_isLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled;
|
||||
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
@@ -62,7 +71,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged;
|
||||
_playbackService.PositionChanged += PlaybackService_PositionChanged;
|
||||
_playbackService.TimelineChanged += PlaybackService_TimelineChanged;
|
||||
|
||||
_isPlaying = _playbackService.IsPlaying;
|
||||
|
||||
UpdateColorConfig();
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Effects;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
@@ -27,28 +30,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
DrawBlurredLyrics(control, blurredLyricsDs);
|
||||
}
|
||||
|
||||
if (_lastAlbumArtSwBitmap != null && _lastAlbumArtCanvasBitmap == null)
|
||||
{
|
||||
_lastAlbumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _lastAlbumArtSwBitmap);
|
||||
}
|
||||
if (_albumArtSwBitmap != null && _albumArtCanvasBitmap == null)
|
||||
{
|
||||
_albumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _albumArtSwBitmap);
|
||||
}
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
if (_isDockMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, combinedDs, 0f);
|
||||
FillBackground(control, combinedDs, _immersiveBgTransition.Value, 0f, _immersiveBgOpacityTransition.Value);
|
||||
}
|
||||
else if (_isDesktopMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, combinedDs, 0f);
|
||||
FillBackground(control, combinedDs, _immersiveBgTransition.Value, 0f, _immersiveBgOpacityTransition.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
FillBackground(control, combinedDs, _albumArtAccentColorTransition.Value, 0f, _albumArtBgOpacity / 100f);
|
||||
DrawAlbumArtBackground(control, combinedDs);
|
||||
}
|
||||
|
||||
@@ -78,7 +73,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
$"[DEBUG]\n" +
|
||||
$"Cur playing {_playingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" +
|
||||
$"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
|
||||
$"Cur time {_totalTime + _positionOffset}\n" +
|
||||
$"Cur time {TotalTime + _positionOffset}\n" +
|
||||
$"Lang size {_lyricsDataArr.Count}\n" +
|
||||
$"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
|
||||
new Vector2(10, 10),
|
||||
@@ -106,7 +101,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float imageWidth = (float)canvasBitmap.Size.Width;
|
||||
float imageHeight = (float)canvasBitmap.Size.Height;
|
||||
|
||||
float targetSize = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2)) * 1.4f;
|
||||
float targetSize = MathF.Sqrt(MathF.Pow(_canvasWidth, 2) + MathF.Pow(_canvasHeight, 2));
|
||||
float scaleFactor = targetSize / MathF.Min(imageWidth, imageHeight);
|
||||
|
||||
float x = _canvasWidth / 2 - imageWidth * scaleFactor / 2;
|
||||
@@ -133,8 +128,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
BlackPoint = new Vector2(blackX, blackY),
|
||||
},
|
||||
Opacity = opacity,
|
||||
}, new Vector2(x, y)
|
||||
);
|
||||
}, new Vector2(x, y));
|
||||
}
|
||||
|
||||
private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasBitmap canvasBitmap, float opacity)
|
||||
@@ -168,16 +162,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
AlphaMask = cornerRadiusMask,
|
||||
},
|
||||
Opacity = opacity,
|
||||
}, new Vector2(_albumArtXTransition.Value, _albumArtY)
|
||||
);
|
||||
}, new Vector2(_albumArtXTransition.Value, _albumArtYTransition.Value));
|
||||
}
|
||||
|
||||
private void DrawAlbumArtBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
|
||||
//ds.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
|
||||
|
||||
using var overlappedCovers = new CanvasCommandList(control.Device);
|
||||
using var overlappedCoversDs = overlappedCovers.CreateDrawingSession();
|
||||
overlappedCoversDs.Transform = Matrix3x2.CreateRotation(_rotateAngle, control.Size.ToVector2() * 0.5f);
|
||||
|
||||
if (_lastAlbumArtCanvasBitmap != null)
|
||||
{
|
||||
@@ -188,20 +182,40 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
DrawBackgroundImgae(control, overlappedCoversDs, _albumArtCanvasBitmap, _albumArtBgTransition.Value);
|
||||
}
|
||||
|
||||
using var coverOverlayEffect = new OpacityEffect
|
||||
overlappedCoversDs.Transform = Matrix3x2.Identity;
|
||||
|
||||
IGraphicsEffectSource blurredCover = new GaussianBlurEffect
|
||||
{
|
||||
BlurAmount = _albumArtBgBlurAmount,
|
||||
Source = overlappedCovers,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
};
|
||||
|
||||
// 应用亚克力噪点效果
|
||||
// TODO: 没有写_coverAcrylicNoiseCanvasBitmap加载的代码
|
||||
if (_coverAcrylicEffectAmount > 0 && _coverAcrylicNoiseCanvasBitmap != null)
|
||||
{
|
||||
blurredCover = new BlendEffect
|
||||
{
|
||||
Mode = BlendEffectMode.SoftLight,
|
||||
Background = blurredCover,
|
||||
Foreground = new OpacityEffect
|
||||
{
|
||||
Source = _coverAcrylicNoiseCanvasBitmap,
|
||||
Opacity = _coverAcrylicEffectAmount / 100f,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
var coverOverlayEffect = new OpacityEffect
|
||||
{
|
||||
Opacity = _albumArtBgOpacity / 100f,
|
||||
Source = new GaussianBlurEffect
|
||||
{
|
||||
BlurAmount = _albumArtBgBlurAmount,
|
||||
Source = overlappedCovers,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
},
|
||||
Source = blurredCover,
|
||||
};
|
||||
ds.DrawImage(coverOverlayEffect);
|
||||
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
//ds.Transform = Matrix3x2.Identity;
|
||||
}
|
||||
|
||||
private void DrawAlbumArt(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
@@ -223,7 +237,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
Source = albumArt,
|
||||
BlurAmount = 12f,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
});
|
||||
opacityDs.DrawImage(albumArt);
|
||||
|
||||
@@ -248,21 +262,27 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, float opacity)
|
||||
{
|
||||
CanvasTextLayout titleLayout = new(
|
||||
var maxWidth = _lyricsLayoutOrientation switch
|
||||
{
|
||||
LyricsLayoutOrientation.Horizontal => _albumArtSize,
|
||||
LyricsLayoutOrientation.Vertical => _canvasWidth - _leftMargin - _albumArtSize - _rightMargin,
|
||||
_ => 0f
|
||||
};
|
||||
using CanvasTextLayout titleLayout = new(
|
||||
control, title ?? string.Empty,
|
||||
_titleTextFormat, _albumArtSize, _canvasHeight
|
||||
_titleTextFormat, maxWidth, _canvasHeight
|
||||
);
|
||||
CanvasTextLayout artistLayout = new(
|
||||
using CanvasTextLayout artistLayout = new(
|
||||
control, artist ?? string.Empty,
|
||||
_artistTextFormat, _albumArtSize, _canvasHeight
|
||||
_artistTextFormat, maxWidth, _canvasHeight
|
||||
);
|
||||
ds.DrawTextLayout(
|
||||
titleLayout,
|
||||
new Vector2(_albumArtXTransition.Value, _titleY),
|
||||
new Vector2(_titleXTransition.Value, _titleYTransition.Value),
|
||||
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity)));
|
||||
ds.DrawTextLayout(
|
||||
artistLayout,
|
||||
new Vector2(_albumArtXTransition.Value, _titleY + (float)titleLayout.LayoutBounds.Height),
|
||||
new Vector2(_titleXTransition.Value, _titleYTransition.Value + (float)titleLayout.LayoutBounds.Height),
|
||||
_bgFontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity)));
|
||||
}
|
||||
|
||||
@@ -313,7 +333,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
float xOffset = _lyricsXTransition.Value;
|
||||
float yOffset = _canvasYScrollTransition.Value + _canvasHeight / 2;
|
||||
float yOffset = _canvasYScrollTransition.Value + _canvasHeight / 2 + _lyricsYTransition.Value;
|
||||
|
||||
// 组合变换:缩放 -> 旋转 -> 平移
|
||||
ds.Transform =
|
||||
@@ -351,8 +371,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
Source = new OpacityEffect { Source = bgLyrics, Opacity = line.OpacityTransition.Value * _lyricsOpacityTransition.Value },
|
||||
BlurAmount = line.BlurAmountTransition.Value,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -443,7 +463,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
else
|
||||
{
|
||||
float height = 0f;
|
||||
var regions = textLayout.GetCharacterRegions(0, string.Join("", line.LyricsChars.Select(x => x.Text)).Length);
|
||||
//var regions = textLayout.GetCharacterRegions(0, string.Join("", line.LyricsChars.Select(x => x.Text)).Length);
|
||||
var regions = textLayout.GetCharacterRegions(0, line.OriginalText.Length);
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top;
|
||||
@@ -479,7 +500,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
},
|
||||
},
|
||||
BlurAmount = _lyricsGlowEffectAmount,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
}
|
||||
: new CanvasCommandList(control.Device),
|
||||
Foreground = new AlphaMaskEffect
|
||||
@@ -509,7 +530,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
Displacement = mask,
|
||||
XChannelSelect = EffectChannelSelect.Red,
|
||||
YChannelSelect = EffectChannelSelect.Alpha,
|
||||
Amount = 1f
|
||||
Amount = 1f,
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -532,7 +553,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawImmersiveBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, float radius)
|
||||
private void FillBackground(ICanvasAnimatedControl control, CanvasDrawingSession ds, Color color, float radius, float opacity)
|
||||
{
|
||||
CanvasCommandList list = new(control.Device);
|
||||
using var listDs = list.CreateDrawingSession();
|
||||
@@ -540,12 +561,12 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
new Rect(0, 0, _canvasWidth, _canvasHeight),
|
||||
radius,
|
||||
radius,
|
||||
_immersiveBgTransition.Value
|
||||
color
|
||||
);
|
||||
ds.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = list,
|
||||
Opacity = _immersiveBgOpacityTransition.Value
|
||||
Opacity = opacity
|
||||
});
|
||||
}
|
||||
|
||||
@@ -556,16 +577,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
float width
|
||||
)
|
||||
{
|
||||
return new CanvasLinearGradientBrush(
|
||||
control,
|
||||
stops
|
||||
.Select(stops => new CanvasGradientStop
|
||||
{
|
||||
Position = stops.position,
|
||||
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
|
||||
})
|
||||
.ToArray()
|
||||
)
|
||||
return new CanvasLinearGradientBrush(control, stops.Select(stops => new CanvasGradientStop
|
||||
{
|
||||
Position = stops.position,
|
||||
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
|
||||
}).ToArray())
|
||||
{
|
||||
StartPoint = new Vector2(startX, 0),
|
||||
EndPoint = new Vector2(startX + width, 0),
|
||||
|
||||
@@ -6,12 +6,14 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class LyricsRendererViewModel
|
||||
: IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<float>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<Color>>,
|
||||
@@ -23,13 +25,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<EasingType>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>>>,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>>>
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalMediaFolder>>>
|
||||
{
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message)
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LocalLyricsFolders))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders))
|
||||
{
|
||||
// Music lib changed, re-fetch lyrics
|
||||
_logger.LogInformation("Local lyrics folders changed, refreshing lyrics.");
|
||||
@@ -82,6 +84,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_isLyricsFloatAnimationEnabled = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsLibreTranslateEnabled))
|
||||
{
|
||||
_isLibreTranslateEnabled = message.NewValue;
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsWindowViewModel)
|
||||
{
|
||||
@@ -89,20 +96,25 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_isDockMode = message.NewValue;
|
||||
UpdateColorConfig();
|
||||
UpdateImmersiveBackgroundOpacity();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
|
||||
{
|
||||
_isDesktopMode = message.NewValue;
|
||||
UpdateColorConfig();
|
||||
UpdateImmersiveBackgroundOpacity();
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsLyricsWindowLocked))
|
||||
{
|
||||
_isLyricsWindowLocked = message.NewValue;
|
||||
UpdateImmersiveBackgroundOpacity();
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsMouseWithinWindow))
|
||||
{
|
||||
_isMouseWithinWindow = message.NewValue;
|
||||
_immersiveBgOpacityTransition.StartTransition(_isDesktopMode ? (_isMouseWithinWindow ? 1f : 0f) : 1f);
|
||||
UpdateImmersiveBackgroundOpacity();
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
@@ -180,6 +192,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_albumArtBgBlurAmount = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.CoverAcrylicEffectAmount))
|
||||
{
|
||||
_coverAcrylicEffectAmount = message.NewValue;
|
||||
_isCoverAcrylicEffectAmountChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsVerticalEdgeOpacity))
|
||||
{
|
||||
_lyricsVerticalEdgeOpacity = message.NewValue;
|
||||
@@ -190,9 +207,19 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_lyricsBlurAmount = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsStandardFontSize))
|
||||
{
|
||||
_lyricsFontSize = message.NewValue;
|
||||
_lyricsStandardFontSize = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDockFontSize))
|
||||
{
|
||||
_lyricsDockFontSize = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsDesktopFontSize))
|
||||
{
|
||||
_lyricsDesktopFontSize = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.SelectedTargetLanguageIndex))
|
||||
@@ -320,5 +347,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontFamily))
|
||||
{
|
||||
_lyricsTextFormat.FontFamily = _artistTextFormat.FontFamily = _titleTextFormat.FontFamily = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,37 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||
);
|
||||
|
||||
private readonly ValueTransition<Color> _albumArtAccentColorTransition = new(
|
||||
initialValue: Colors.Transparent,
|
||||
durationSeconds: 0.3f,
|
||||
interpolator: (from, to, progress) => Helper.ColorHelper.GetInterpolatedColor(progress, from, to)
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _immersiveBgOpacityTransition = new(
|
||||
initialValue: 1f,
|
||||
durationSeconds: 0.2f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _titleXTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _titleYTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsXTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsYTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _lyricsOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
@@ -54,6 +75,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _albumArtYTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 0.3f
|
||||
);
|
||||
|
||||
private readonly ValueTransition<float> _songInfoOpacityTransition = new(
|
||||
initialValue: 0f,
|
||||
durationSeconds: 1f
|
||||
|
||||
@@ -7,8 +7,12 @@ using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
@@ -19,6 +23,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private bool _isCanvasHeightChanged = false;
|
||||
|
||||
private bool _isDisplayTypeChanged = false;
|
||||
private bool _isLyricsLayoutOrientationChanged = false;
|
||||
|
||||
private bool _isPlayingLineChanged = false;
|
||||
private bool _isVisibleLinesBoundaryChanged = false;
|
||||
@@ -29,7 +34,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (_isPlaying)
|
||||
{
|
||||
_totalTime += _elapsedTime;
|
||||
TotalTime += _elapsedTime;
|
||||
}
|
||||
|
||||
var playingLineIndex = GetCurrentPlayingLineIndex();
|
||||
@@ -44,11 +49,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_displayType = _displayTypeReceived;
|
||||
_playingLineIndex = playingLineIndex;
|
||||
|
||||
_immersiveBgOpacityTransition.Update(_elapsedTime);
|
||||
_immersiveBgTransition.Update(_elapsedTime);
|
||||
_albumArtBgTransition.Update(_elapsedTime);
|
||||
_lyricsBgBrightnessTransition.Update(_elapsedTime);
|
||||
_songInfoOpacityTransition.Update(_elapsedTime);
|
||||
if (_albumArtChanged)
|
||||
{
|
||||
if (_lastAlbumArtSwBitmap != null)
|
||||
{
|
||||
_lastAlbumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _lastAlbumArtSwBitmap);
|
||||
}
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
if (_albumArtSwBitmap != null)
|
||||
{
|
||||
_albumArtCanvasBitmap = CanvasBitmap.CreateFromSoftwareBitmap(control, _albumArtSwBitmap);
|
||||
}
|
||||
_albumArtChanged = false;
|
||||
}
|
||||
|
||||
if (_isDynamicCoverOverlayEnabled)
|
||||
{
|
||||
@@ -56,52 +70,97 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_rotateAngle %= MathF.PI * 2;
|
||||
}
|
||||
|
||||
if (_isCanvasHeightChanged)
|
||||
if (_isCanvasWidthChanged)
|
||||
{
|
||||
_albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16f;
|
||||
if (_canvasWidth < 450)
|
||||
{
|
||||
_lyricsLayoutOrientation = LyricsLayoutOrientation.Vertical;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsLayoutOrientation = LyricsLayoutOrientation.Horizontal;
|
||||
}
|
||||
}
|
||||
|
||||
if (_isCanvasWidthChanged || _isCanvasHeightChanged)
|
||||
{
|
||||
_albumArtSize = MathF.Min(
|
||||
(_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16,
|
||||
(_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2);
|
||||
_albumArtSize = MathF.Max(0, _albumArtSize);
|
||||
_isCoverAcrylicEffectAmountChanged = true;
|
||||
}
|
||||
|
||||
_titleY = _albumArtY + _albumArtSize * 1.05f;
|
||||
if (_isCoverAcrylicEffectAmountChanged)
|
||||
{
|
||||
UpdateCoverAcrylicOverlay(control);
|
||||
}
|
||||
|
||||
if (_isDisplayTypeChanged || _isCanvasWidthChanged)
|
||||
{
|
||||
bool jumpTo = !_isDisplayTypeChanged && _isCanvasWidthChanged;
|
||||
switch (_displayType)
|
||||
switch (_lyricsLayoutOrientation)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_canvasWidth / 2 - _albumArtSize / 2, jumpTo);
|
||||
case LyricsLayoutOrientation.Horizontal:
|
||||
_albumArtSize = MathF.Min((_canvasHeight - _topMargin - _bottomMargin) * 8.5f / 16, (_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2);
|
||||
_albumArtSize = MathF.Max(0, _albumArtSize);
|
||||
_albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize * 1.05f - _titleTextFormat.FontSize - _artistTextFormat.FontSize) / 2, jumpTo);
|
||||
_titleYTransition.StartTransition(_albumArtYTransition.TargetValue + _albumArtSize * 1.05f, jumpTo);
|
||||
_lyricsYTransition.StartTransition(0, jumpTo);
|
||||
switch (_displayType)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_canvasWidth / 2 - _albumArtSize / 2, jumpTo);
|
||||
_titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_lyricsXTransition.StartTransition(_leftMargin, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo);
|
||||
_titleXTransition.StartTransition(_albumArtXTransition.TargetValue, jumpTo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(0f, jumpTo);
|
||||
case LyricsLayoutOrientation.Vertical:
|
||||
_albumArtSize = 64;
|
||||
_lyricsXTransition.StartTransition(_leftMargin, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_leftMargin, jumpTo);
|
||||
_titleXTransition.StartTransition(_leftMargin + _albumArtSize * 1.2f, jumpTo);
|
||||
switch (_displayType)
|
||||
{
|
||||
case LyricsDisplayType.AlbumArtOnly:
|
||||
_lyricsOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtYTransition.StartTransition((_canvasHeight - _albumArtSize) / 2, jumpTo);
|
||||
_titleYTransition.StartTransition(_albumArtYTransition.TargetValue, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.LyricsOnly:
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(0f, jumpTo);
|
||||
_lyricsYTransition.StartTransition(0, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.SplitView:
|
||||
_albumArtYTransition.StartTransition(_topMargin, jumpTo);
|
||||
_titleYTransition.StartTransition(_topMargin, jumpTo);
|
||||
_lyricsOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_albumArtOpacityTransition.StartTransition(1f, jumpTo);
|
||||
_lyricsYTransition.StartTransition(_albumArtSize, jumpTo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_lyricsXTransition.Update(_elapsedTime);
|
||||
_albumArtXTransition.Update(_elapsedTime);
|
||||
_lyricsOpacityTransition.Update(_elapsedTime);
|
||||
_albumArtOpacityTransition.Update(_elapsedTime);
|
||||
|
||||
if (_isCanvasWidthChanged || _lyricsXTransition.IsTransitioning)
|
||||
{
|
||||
_maxLyricsWidth = _canvasWidth - _lyricsXTransition.Value - _rightMargin;
|
||||
@@ -122,6 +181,21 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
UpdateLinesProps();
|
||||
|
||||
_isLayoutChanged = false;
|
||||
|
||||
_titleXTransition.Update(_elapsedTime);
|
||||
_titleYTransition.Update(_elapsedTime);
|
||||
_lyricsXTransition.Update(_elapsedTime);
|
||||
_lyricsYTransition.Update(_elapsedTime);
|
||||
_albumArtXTransition.Update(_elapsedTime);
|
||||
_albumArtYTransition.Update(_elapsedTime);
|
||||
_lyricsOpacityTransition.Update(_elapsedTime);
|
||||
_albumArtOpacityTransition.Update(_elapsedTime);
|
||||
_immersiveBgOpacityTransition.Update(_elapsedTime);
|
||||
_immersiveBgTransition.Update(_elapsedTime);
|
||||
_albumArtAccentColorTransition.Update(_elapsedTime);
|
||||
_albumArtBgTransition.Update(_elapsedTime);
|
||||
_lyricsBgBrightnessTransition.Update(_elapsedTime);
|
||||
_songInfoOpacityTransition.Update(_elapsedTime);
|
||||
}
|
||||
|
||||
private void ReLayout(ICanvasAnimatedControl control)
|
||||
@@ -129,7 +203,18 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (control == null)
|
||||
return;
|
||||
|
||||
_lyricsTextFormat.FontSize = _lyricsFontSize;
|
||||
if (_isDockMode)
|
||||
{
|
||||
_lyricsTextFormat.FontSize = _lyricsDockFontSize;
|
||||
}
|
||||
else if (_isDesktopMode)
|
||||
{
|
||||
_lyricsTextFormat.FontSize = _lyricsDesktopFontSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsTextFormat.FontSize = _lyricsStandardFontSize;
|
||||
}
|
||||
|
||||
float y = 0;
|
||||
|
||||
@@ -194,48 +279,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
_canvasYScrollTransition.Update(_elapsedTime);
|
||||
|
||||
int startVisibleLineIndex = -1;
|
||||
int endVisibleLineIndex = -1;
|
||||
|
||||
// Update visible line indices
|
||||
for (int i = startLineIndex; i <= endLineIndex; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
var lines = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines;
|
||||
if (lines == null || lines.Count == 0) return;
|
||||
|
||||
if (line == null || line.CanvasTextLayout == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var textLayout = line.CanvasTextLayout;
|
||||
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= 0
|
||||
)
|
||||
{
|
||||
if (startVisibleLineIndex == -1)
|
||||
{
|
||||
startVisibleLineIndex = i;
|
||||
}
|
||||
}
|
||||
if (
|
||||
_canvasYScrollTransition.Value
|
||||
+ _canvasHeight / 2
|
||||
+ line.Position.Y
|
||||
+ textLayout.LayoutBounds.Height
|
||||
>= control.Size.Height
|
||||
)
|
||||
{
|
||||
if (endVisibleLineIndex == -1)
|
||||
{
|
||||
endVisibleLineIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
float offset = _canvasYScrollTransition.Value + _canvasHeight / 2;
|
||||
int startVisibleLineIndex = FindFirstVisibleLine(lines, offset);
|
||||
int endVisibleLineIndex = FindLastVisibleLine(lines, offset, _canvasHeight);
|
||||
|
||||
if (startVisibleLineIndex != -1 && endVisibleLineIndex == -1)
|
||||
{
|
||||
@@ -248,6 +298,52 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_endVisibleLineIndex = endVisibleLineIndex;
|
||||
}
|
||||
|
||||
private int FindFirstVisibleLine(IList<LyricsLine> lines, float offset)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
{
|
||||
int mid = (left + right) / 2;
|
||||
var line = lines[mid];
|
||||
var layout = line.CanvasTextLayout;
|
||||
if (layout == null) break;
|
||||
float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height;
|
||||
if (value >= 0)
|
||||
{
|
||||
result = mid;
|
||||
right = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int FindLastVisibleLine(IList<LyricsLine> lines, float offset, float canvasHeight)
|
||||
{
|
||||
int left = 0, right = lines.Count - 1, result = -1;
|
||||
while (left <= right)
|
||||
{
|
||||
int mid = (left + right) / 2;
|
||||
var line = lines[mid];
|
||||
var layout = line.CanvasTextLayout;
|
||||
if (layout == null) break;
|
||||
float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height;
|
||||
if (value >= canvasHeight)
|
||||
{
|
||||
result = mid;
|
||||
right = mid - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = mid + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void UpdateColorConfig()
|
||||
{
|
||||
if (_isDesktopMode || _isDockMode)
|
||||
@@ -259,46 +355,30 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
ThemeTypeSent = _lyricsBgTheme;
|
||||
}
|
||||
|
||||
float brightness = 0f;
|
||||
|
||||
float brightness;
|
||||
Color grayedEnvironmentalColor = Colors.Transparent;
|
||||
|
||||
switch (ThemeTypeSent)
|
||||
bool isLight = ThemeTypeSent switch
|
||||
{
|
||||
case ElementTheme.Default:
|
||||
switch (Application.Current.RequestedTheme)
|
||||
{
|
||||
case ApplicationTheme.Light:
|
||||
_adaptiveGrayedFontColor = _darkColor;
|
||||
brightness = 0.7f;
|
||||
break;
|
||||
case ApplicationTheme.Dark:
|
||||
_adaptiveGrayedFontColor = _lightColor;
|
||||
brightness = 0.3f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ElementTheme.Light:
|
||||
_adaptiveGrayedFontColor = _darkColor;
|
||||
brightness = 0.7f;
|
||||
break;
|
||||
case ElementTheme.Dark:
|
||||
_adaptiveGrayedFontColor = _lightColor;
|
||||
brightness = 0.3f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ElementTheme.Default => Application.Current.RequestedTheme == ApplicationTheme.Light,
|
||||
ElementTheme.Light => true,
|
||||
ElementTheme.Dark => false,
|
||||
_ => false
|
||||
};
|
||||
|
||||
if (_adaptiveGrayedFontColor == _lightColor)
|
||||
{
|
||||
grayedEnvironmentalColor = _darkColor;
|
||||
}
|
||||
else if (_adaptiveGrayedFontColor == _darkColor)
|
||||
if (isLight)
|
||||
{
|
||||
_adaptiveGrayedFontColor = _darkColor;
|
||||
brightness = 0.7f;
|
||||
grayedEnvironmentalColor = _lightColor;
|
||||
_albumArtAccentColorTransition.StartTransition(_albumArtLightAccentColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_adaptiveGrayedFontColor = _lightColor;
|
||||
brightness = 0.3f;
|
||||
grayedEnvironmentalColor = _darkColor;
|
||||
_albumArtAccentColorTransition.StartTransition(_albumArtDarkAccentColor);
|
||||
}
|
||||
|
||||
_lyricsBgBrightnessTransition.StartTransition(brightness);
|
||||
@@ -309,7 +389,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
_adaptiveColoredFontColor = Helper.ColorHelper.GetForegroundColor(_albumArtAccentColor?.WithBrightness(brightness) ?? Colors.Transparent);
|
||||
if (isLight)
|
||||
{
|
||||
_adaptiveColoredFontColor = _albumArtDarkAccentColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_adaptiveColoredFontColor = _albumArtLightAccentColor;
|
||||
}
|
||||
}
|
||||
|
||||
switch (_lyricsBgFontColorType)
|
||||
@@ -398,5 +485,49 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
line.HighlightOpacityTransition.Update(_elapsedTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateImmersiveBackgroundOpacity()
|
||||
{
|
||||
float targetOpacity;
|
||||
if (_isDesktopMode)
|
||||
{
|
||||
if (_isLyricsWindowLocked)
|
||||
{
|
||||
targetOpacity = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isMouseWithinWindow)
|
||||
{
|
||||
targetOpacity = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetOpacity = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetOpacity = 1f;
|
||||
}
|
||||
_immersiveBgOpacityTransition.StartTransition(targetOpacity);
|
||||
}
|
||||
|
||||
private void UpdateCoverAcrylicOverlay(ICanvasAnimatedControl control)
|
||||
{
|
||||
if (_coverAcrylicEffectAmount > 0)
|
||||
{
|
||||
var ret = NoiseOverlayHelper.GenerateNoiseBitmapBGRA((int)_canvasWidth, (int)_canvasHeight);
|
||||
_coverAcrylicNoiseCanvasBitmap = CanvasBitmap.CreateFromBytes(
|
||||
control,
|
||||
ret,
|
||||
(int)_canvasWidth,
|
||||
(int)_canvasHeight,
|
||||
Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized
|
||||
);
|
||||
}
|
||||
_isCoverAcrylicEffectAmountChanged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,11 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.Graphics.Display;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -31,7 +33,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public partial class LyricsRendererViewModel : BaseViewModel
|
||||
{
|
||||
private TimeSpan _elapsedTime = TimeSpan.Zero;
|
||||
private TimeSpan _totalTime = TimeSpan.Zero;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
|
||||
|
||||
private TimeSpan _positionOffset = TimeSpan.Zero;
|
||||
|
||||
private int _songDurationMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds;
|
||||
@@ -42,16 +48,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private CanvasBitmap? _lastAlbumArtCanvasBitmap = null;
|
||||
private CanvasBitmap? _albumArtCanvasBitmap = null;
|
||||
|
||||
private bool _albumArtChanged = false;
|
||||
|
||||
private CanvasBitmap? _coverAcrylicNoiseCanvasBitmap = null;
|
||||
private bool _isCoverAcrylicEffectAmountChanged = false;
|
||||
|
||||
private float _albumArtSize = 0f;
|
||||
private int _albumArtCornerRadius = 0;
|
||||
|
||||
private float _albumArtY = 0f;
|
||||
|
||||
private string? _lastSongTitle;
|
||||
private string? _songTitle;
|
||||
|
||||
private float _titleY = 0f;
|
||||
|
||||
private string? _lastSongArtist;
|
||||
private string? _songArtist;
|
||||
|
||||
@@ -67,6 +74,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private readonly float _coverRotateSpeed = 0.003f;
|
||||
private float _rotateAngle = 0f;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
|
||||
|
||||
private TextAlignmentType _lyricsAlignmentType;
|
||||
|
||||
private readonly float _lyricsGlowEffectAmount = 8f;
|
||||
@@ -78,7 +93,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private LineRenderingType _lyricsHighlightScope;
|
||||
|
||||
private int _lyricsFontStrokeWidth;
|
||||
private int _lyricsFontSize;
|
||||
private int _lyricsStandardFontSize;
|
||||
private int _lyricsDockFontSize;
|
||||
private int _lyricsDesktopFontSize;
|
||||
private float _lyricsLineSpacingFactor;
|
||||
|
||||
private LyricsFontColorType _lyricsBgFontColorType;
|
||||
@@ -102,7 +119,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private Color _adaptiveGrayedFontColor = Colors.Transparent;
|
||||
private Color? _adaptiveColoredFontColor = null;
|
||||
|
||||
private Color? _albumArtAccentColor = null;
|
||||
private Color _albumArtLightAccentColor = Colors.Transparent;
|
||||
private Color _albumArtDarkAccentColor = Colors.Transparent;
|
||||
private Color _environmentalColor = Colors.Transparent;
|
||||
|
||||
private Color _lightColor = Colors.White;
|
||||
@@ -141,10 +159,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private int _langIndex = 0;
|
||||
|
||||
private List<LyricsData> _lyricsDataArr = [];
|
||||
private List<string> _translationList = [];
|
||||
private bool _isTranslationEnabled;
|
||||
private bool _showTranslationOnly;
|
||||
private int _targetLanguageIndex;
|
||||
private bool _isLibreTranslateEnabled;
|
||||
|
||||
private int _timelineSyncThreshold;
|
||||
|
||||
@@ -178,9 +196,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private LyricsDisplayType _displayTypeReceived;
|
||||
private LyricsDisplayType _displayType;
|
||||
|
||||
private LyricsLayoutOrientation _lyricsLayoutOrientation;
|
||||
|
||||
private int _albumArtBgBlurAmount;
|
||||
private int _albumArtBgOpacity;
|
||||
|
||||
private int _coverAcrylicEffectAmount;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsTranslating { get; set; } = false;
|
||||
|
||||
@@ -193,12 +215,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private int GetCurrentPlayingLineIndex()
|
||||
{
|
||||
var totalMs = TotalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds;
|
||||
if (totalMs < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.FirstOrDefault()?.StartMs) return 0;
|
||||
|
||||
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[i];
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
var nextLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i + 1);
|
||||
var totalMs = _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds;
|
||||
if (nextLine != null && line.StartMs <= totalMs && totalMs < nextLine.StartMs)
|
||||
{
|
||||
return i;
|
||||
@@ -227,7 +251,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
else if (nextLine != null) lineEndMs = nextLine.StartMs;
|
||||
else lineEndMs = _songDurationMs;
|
||||
|
||||
float now = (float)_totalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds;
|
||||
float now = (float)TotalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds;
|
||||
|
||||
// 1. 还没到本句
|
||||
if (now < line.StartMs)
|
||||
@@ -283,11 +307,21 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有逐字时间轴,直接线性,模拟逐字高亮
|
||||
// 没有逐字时间轴,均匀分配每个字的高亮时间
|
||||
int textLength = line.OriginalText.Length;
|
||||
if (textLength == 0) return;
|
||||
|
||||
float lineProgress = (now - line.StartMs) / (lineEndMs - line.StartMs);
|
||||
charStartIndex = (int)(lineProgress * line.OriginalText.Length);
|
||||
charProgress = lineProgress - (int)(lineProgress);
|
||||
lineProgress = Math.Clamp(lineProgress, 0f, 1f);
|
||||
|
||||
// 计算当前高亮到第几个字
|
||||
float charFloatIndex = lineProgress * textLength;
|
||||
int charIndex = (int)charFloatIndex;
|
||||
charStartIndex = Math.Clamp(charIndex, 0, textLength - 1);
|
||||
charLength = 1;
|
||||
|
||||
// 当前字的进度(0~1)
|
||||
charProgress = charFloatIndex - charIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,11 +353,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isPlaying = e.IsPlaying;
|
||||
}
|
||||
|
||||
private void PlaybackService_PositionChanged(object? sender, PositionChangedEventArgs e)
|
||||
private void PlaybackService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
|
||||
{
|
||||
if (Math.Abs(_totalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold)
|
||||
if (Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold)
|
||||
{
|
||||
_totalTime = e.Position;
|
||||
TotalTime = e.Position;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +383,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
});
|
||||
_totalTime = TimeSpan.Zero;
|
||||
TotalTime = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,16 +391,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (e.AlbumArtSwBitmap != _albumArtSwBitmap)
|
||||
{
|
||||
//_lastAlbumArtSwBitmap?.Dispose();
|
||||
_lastAlbumArtSwBitmap = _albumArtSwBitmap;
|
||||
//_lastAlbumArtCanvasBitmap?.Dispose();
|
||||
_lastAlbumArtCanvasBitmap = null;
|
||||
|
||||
//_albumArtSwBitmap?.Dispose();
|
||||
_albumArtSwBitmap = e.AlbumArtSwBitmap;
|
||||
//_albumArtCanvasBitmap?.Dispose();
|
||||
_albumArtCanvasBitmap = null;
|
||||
|
||||
_albumArtAccentColor = e.AlbumArtAccentColor;
|
||||
_albumArtChanged = true;
|
||||
|
||||
_albumArtBgTransition.Reset(0f);
|
||||
_albumArtBgTransition.StartTransition(1f);
|
||||
_albumArtLightAccentColor = e.AlbumArtLightAccentColor ?? Colors.Transparent;
|
||||
_albumArtDarkAccentColor = e.AlbumArtDarkAccentColor ?? Colors.Transparent;
|
||||
|
||||
UpdateColorConfig();
|
||||
}
|
||||
@@ -374,6 +412,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void UpdateTranslations()
|
||||
{
|
||||
TranslationSearchProvider = null;
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_isLayoutChanged = true;
|
||||
|
||||
@@ -400,7 +439,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_logger.LogInformation("Showing translation for lyrics...");
|
||||
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode();
|
||||
string originalText = _lyricsDataArr[0].WrappedOriginalText;
|
||||
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
|
||||
if (originalText == null) return;
|
||||
|
||||
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
|
||||
|
||||
if (originalLangCode == targetLangCode)
|
||||
@@ -421,56 +462,66 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found]);
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found], 50);
|
||||
_langIndex = 0;
|
||||
}
|
||||
TranslationSearchProvider = LyricsSearchProvider.ToTranslationSearchProvider();
|
||||
}
|
||||
else
|
||||
else if (_isLibreTranslateEnabled)
|
||||
{
|
||||
string translated = string.Empty;
|
||||
try
|
||||
{
|
||||
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
if (translated == string.Empty) return;
|
||||
|
||||
if (_showTranslationOnly)
|
||||
{
|
||||
// TODO
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
|
||||
_langIndex = 0;
|
||||
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
|
||||
_lyricsDataArr[^1].SetDisplayedTextInOriginalText();
|
||||
_langIndex = _lyricsDataArr.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
|
||||
_langIndex = 0;
|
||||
}
|
||||
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader?.GetString("LibreTranslateFailed")!, Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshLyricsAsync(CancellationToken token)
|
||||
{
|
||||
LyricsSearchProvider = null;
|
||||
_logger.LogInformation("Refreshing lyrics...");
|
||||
|
||||
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
|
||||
_isLayoutChanged = true;
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsSearchProvider? provider = null;
|
||||
LyricsSearchProvider? lyricsSearchProvider = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
(lyricsRaw, provider) = await _lyrcsSearchService.SearchAsync(
|
||||
(lyricsRaw, lyricsSearchProvider) = await _lyrcsSearchService.SearchAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
SongInfo.DurationMs ?? 0,
|
||||
token
|
||||
);
|
||||
LyricsSearchProvider = lyricsSearchProvider;
|
||||
_logger.LogInformation("Lyrics search result: {LyricsRaw}", lyricsRaw ?? "null");
|
||||
token.ThrowIfCancellationRequested();
|
||||
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
|
||||
FillTranslationFromCache(provider);
|
||||
FillTranslationFromCache(LyricsSearchProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -491,24 +542,25 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
string? translationRaw = null;
|
||||
switch (provider)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
case Enums.LyricsSearchProvider.QQ:
|
||||
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
case Enums.LyricsSearchProvider.Kugou:
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
case Enums.LyricsSearchProvider.Netease:
|
||||
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
case Enums.LyricsSearchProvider.LrcLib:
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
case Enums.LyricsSearchProvider.AmllTtmlDb:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalMusicFile:
|
||||
case Enums.LyricsSearchProvider.LocalMusicFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalLrcFile:
|
||||
case Enums.LyricsSearchProvider.LocalLrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalEslrcFile:
|
||||
case Enums.LyricsSearchProvider.LocalEslrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
case Enums.LyricsSearchProvider.LocalTtmlFile:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -516,10 +568,20 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (translationRaw != null)
|
||||
{
|
||||
var translationData = new LyricsParser().Parse(translationRaw, (int?)SongInfo?.DurationMs);
|
||||
foreach (var data in translationData)
|
||||
if (provider == Enums.LyricsSearchProvider.QQ)
|
||||
{
|
||||
data.LyricsLines = data.LyricsLines.Where(line => !string.IsNullOrWhiteSpace(line.OriginalText)).ToList();
|
||||
foreach (var data in translationData)
|
||||
{
|
||||
foreach (var item in data.LyricsLines)
|
||||
{
|
||||
if (item.OriginalText == "//")
|
||||
{
|
||||
item.OriginalText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lyricsDataArr = _lyricsDataArr.Concat(translationData).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -28,23 +30,35 @@ namespace BetterLyrics.WinUI3
|
||||
: BaseWindowViewModel,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<string>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<DockPlacement>>
|
||||
{
|
||||
private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService<IPlaybackService>();
|
||||
private ForegroundWindowWatcher? _windowWatcher = null;
|
||||
private bool _ignoreFullscreenWindow = false;
|
||||
private int _dockWindowMinHeight = 96;
|
||||
private bool _ignoreFullscreenWindow;
|
||||
private bool _hideWindowWhenNotPlaying;
|
||||
|
||||
private DockPlacement _dockPlacement;
|
||||
private int _lyricsFontSize;
|
||||
private int _dockWindowHeight;
|
||||
private string _dockMonitorDeviceName;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
_dockMonitorDeviceName = _settingsService.DockMonitorDeviceName;
|
||||
_ignoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
_hideWindowWhenNotPlaying = _settingsService.HideWindowWhenNotPlaying;
|
||||
IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
_dockPlacement = _settingsService.DockPlacement;
|
||||
_lyricsFontSize = _settingsService.LyricsFontSize;
|
||||
_dockWindowHeight = _settingsService.DockWindowHeight;
|
||||
OnIsImmersiveModeChanged(_settingsService.IsImmersiveMode);
|
||||
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
}
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
|
||||
{
|
||||
UpdateDockWindow();
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -88,7 +102,27 @@ namespace BetterLyrics.WinUI3
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DockModeHelper.UpdateAppBarHeight(WindowNative.GetWindowHandle(window), Math.Max(_dockWindowMinHeight, _lyricsFontSize * 4), _dockPlacement);
|
||||
var hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (IsDockMode || IsDesktopMode)
|
||||
{
|
||||
if (_hideWindowWhenNotPlaying && !_playbackService.IsPlaying)
|
||||
{
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockModeHelper.UpdateAppBarHeight(hwnd, _dockMonitorDeviceName, 0, _dockPlacement);
|
||||
}
|
||||
window.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockModeHelper.UpdateAppBarHeight(hwnd, _dockMonitorDeviceName, _dockWindowHeight, _dockPlacement);
|
||||
}
|
||||
window.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnIsImmersiveModeChanged(bool value)
|
||||
@@ -121,6 +155,11 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
_ignoreFullscreenWindow = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.HideWindowWhenNotPlaying))
|
||||
{
|
||||
_hideWindowWhenNotPlaying = message.NewValue;
|
||||
UpdateDockWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,9 +178,9 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.DockWindowHeight))
|
||||
{
|
||||
_lyricsFontSize = message.NewValue;
|
||||
_dockWindowHeight = message.NewValue;
|
||||
UpdateDockWindow();
|
||||
}
|
||||
else if (message.Sender is SettingsPageViewModel)
|
||||
@@ -185,11 +224,14 @@ namespace BetterLyrics.WinUI3
|
||||
hwnd,
|
||||
onWindowChanged =>
|
||||
{
|
||||
if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
{
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
}
|
||||
UpdateAccentColor(hwnd);
|
||||
if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
}
|
||||
UpdateAccentColor(hwnd);
|
||||
}, TimeSpan.FromMilliseconds(300));
|
||||
}
|
||||
);
|
||||
_windowWatcher.Start();
|
||||
@@ -205,7 +247,7 @@ namespace BetterLyrics.WinUI3
|
||||
public void UpdateAccentColor(nint hwnd)
|
||||
{
|
||||
WindowPixelSampleMode mode = IsDesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode();
|
||||
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, mode).ToColor();
|
||||
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, _settingsService.DockMonitorDeviceName, mode).ToColor();
|
||||
}
|
||||
|
||||
public void InitLockHotKey()
|
||||
@@ -231,6 +273,8 @@ namespace BetterLyrics.WinUI3
|
||||
IsLyricsWindowLocked = true;
|
||||
IsImmersiveMode = true;
|
||||
}
|
||||
|
||||
UpdateDockWindow();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -264,13 +308,16 @@ namespace BetterLyrics.WinUI3
|
||||
IsDockMode = !IsDockMode;
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockModeHelper.Enable(window, Math.Max(_dockWindowMinHeight, _lyricsFontSize * 4), _dockPlacement);
|
||||
window.Restore();
|
||||
DockModeHelper.Enable(window, _dockMonitorDeviceName, _dockWindowHeight, _dockPlacement);
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockModeHelper.Disable(window);
|
||||
}
|
||||
|
||||
UpdateDockWindow();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -290,5 +337,17 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<string> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.SelectedDockMonitorDeviceName))
|
||||
{
|
||||
_dockMonitorDeviceName = message.NewValue;
|
||||
UpdateDockWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Media;
|
||||
using Windows.Media.Core;
|
||||
using Windows.Media.Playback;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class MusicGalleryViewModel : BaseViewModel,
|
||||
IRecipient<PropertyChangedMessage<ObservableCollection<LocalMediaFolder>>>
|
||||
{
|
||||
private readonly ILibWatcherService _libWatcherService;
|
||||
private readonly MediaPlayer _mediaPlayer = new();
|
||||
private readonly MediaTimelineController _timelineController = new();
|
||||
private readonly SystemMediaTransportControls _smtc;
|
||||
// All songs
|
||||
private List<Track> _tracks = [];
|
||||
// Songs in current playlist
|
||||
private List<Track> _playlistTracks = [];
|
||||
// Filtered songs based on search query for current playlist
|
||||
private List<Track> _filteredTracks = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLocalMediaNotFound { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Grouped tracks after filtering and sorting for current playlist
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<GroupInfoList> GroupedTracks { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial List<Track> SelectedTracks { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<PlayQueueItem> TrackPlayingQueue { get; set; } = [];
|
||||
|
||||
public PlayQueueItem? PlayingQueueItem => TrackPlayingQueue.ElementAtOrDefault(PlayingSongIndex);
|
||||
|
||||
[ObservableProperty]
|
||||
public partial PlaybackOrder PlaybackOrder { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial CommonSongProperty SongOrderType { get; set; } = CommonSongProperty.Title;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<SongsTabInfo> SongsTabInfoList { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int SelectedSongsTabInfoIndex { get; set; } = 0;
|
||||
|
||||
public SongsTabInfo? SelectedSongsTabInfo => SongsTabInfoList.ElementAtOrDefault(SelectedSongsTabInfoIndex);
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsDataLoading { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Track TrackRightTapped { get; set; } = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int PlayingSongIndex { get; set; } = -1;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial int DisplayedPlayingSongIndex { get; set; } = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string SongSearchQuery { get; set; } = string.Empty;
|
||||
|
||||
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) : base(settingsService)
|
||||
{
|
||||
SongsTabInfoList.Add(new SongsTabInfo(App.ResourceLoader!.GetString("MusicGalleryPageAllSongs"), "\uE8A9", false, CommonSongProperty.Title, string.Empty));
|
||||
|
||||
RefreshSongs();
|
||||
|
||||
PlaybackOrder = _settingsService.PlaybackOrder;
|
||||
|
||||
_mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
|
||||
_mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;
|
||||
_timelineController = _mediaPlayer.TimelineController = new();
|
||||
_timelineController.PositionChanged += TimelineController_PositionChanged;
|
||||
_smtc = _mediaPlayer.SystemMediaTransportControls;
|
||||
_mediaPlayer.CommandManager.IsEnabled = false;
|
||||
_smtc.IsPlayEnabled = true;
|
||||
_smtc.IsPauseEnabled = true;
|
||||
_smtc.IsNextEnabled = true;
|
||||
_smtc.IsPreviousEnabled = true;
|
||||
_smtc.ButtonPressed += Smtc_ButtonPressed;
|
||||
_smtc.PlaybackPositionChangeRequested += Smtc_PlaybackPositionChangeRequested;
|
||||
|
||||
_libWatcherService = libWatcherService;
|
||||
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
|
||||
}
|
||||
|
||||
private void MediaPlayer_MediaEnded(MediaPlayer sender, object args)
|
||||
{
|
||||
PlayNextTrack();
|
||||
}
|
||||
|
||||
public void PlayNextTrack()
|
||||
{
|
||||
switch (PlaybackOrder)
|
||||
{
|
||||
case PlaybackOrder.RepeatAll:
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
if (PlayingSongIndex < TrackPlayingQueue.Count - 1)
|
||||
{
|
||||
PlayingSongIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayingSongIndex = 0;
|
||||
}
|
||||
PlayTrack(PlayingQueueItem);
|
||||
});
|
||||
break;
|
||||
case PlaybackOrder.RepeatOne:
|
||||
_timelineController.Position = TimeSpan.Zero;
|
||||
break;
|
||||
case PlaybackOrder.Shuffle:
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
if (TrackPlayingQueue.Count > 0)
|
||||
{
|
||||
PlayingSongIndex = new Random().Next(0, TrackPlayingQueue.Count);
|
||||
}
|
||||
PlayTrack(PlayingQueueItem);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayPreviousTrack()
|
||||
{
|
||||
switch (PlaybackOrder)
|
||||
{
|
||||
case PlaybackOrder.RepeatAll:
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
if (PlayingSongIndex > 0)
|
||||
{
|
||||
PlayingSongIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayingSongIndex = TrackPlayingQueue.Count - 1;
|
||||
}
|
||||
PlayTrack(PlayingQueueItem);
|
||||
});
|
||||
break;
|
||||
case PlaybackOrder.RepeatOne:
|
||||
_timelineController.Position = TimeSpan.Zero;
|
||||
break;
|
||||
case PlaybackOrder.Shuffle:
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
if (TrackPlayingQueue.Count > 0)
|
||||
{
|
||||
PlayingSongIndex = new Random().Next(0, TrackPlayingQueue.Count);
|
||||
}
|
||||
PlayTrack(PlayingQueueItem);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Smtc_PlaybackPositionChangeRequested(SystemMediaTransportControls sender, PlaybackPositionChangeRequestedEventArgs args)
|
||||
{
|
||||
_timelineController.Position = args.RequestedPlaybackPosition;
|
||||
}
|
||||
|
||||
private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
|
||||
{
|
||||
_timelineController.Start();
|
||||
_smtc.PlaybackStatus = MediaPlaybackStatus.Playing;
|
||||
}
|
||||
|
||||
private void TimelineController_PositionChanged(MediaTimelineController sender, object args)
|
||||
{
|
||||
_smtc.UpdateTimelineProperties(new SystemMediaTransportControlsTimelineProperties()
|
||||
{
|
||||
Position = sender.Position,
|
||||
EndTime = _mediaPlayer.PlaybackSession.NaturalDuration
|
||||
});
|
||||
}
|
||||
|
||||
private void Smtc_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
|
||||
{
|
||||
switch (args.Button)
|
||||
{
|
||||
case SystemMediaTransportControlsButton.Play:
|
||||
_smtc.PlaybackStatus = MediaPlaybackStatus.Playing;
|
||||
_timelineController.Resume();
|
||||
break;
|
||||
case SystemMediaTransportControlsButton.Pause:
|
||||
_smtc.PlaybackStatus = MediaPlaybackStatus.Paused;
|
||||
_timelineController.Pause();
|
||||
break;
|
||||
case SystemMediaTransportControlsButton.Next:
|
||||
PlayNextTrack();
|
||||
break;
|
||||
case SystemMediaTransportControlsButton.Previous:
|
||||
PlayPreviousTrack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, Events.LibChangedEventArgs e)
|
||||
{
|
||||
RefreshSongs();
|
||||
}
|
||||
|
||||
public void RefreshSongs()
|
||||
{
|
||||
_dispatcherQueueTimer.Debounce(() =>
|
||||
{
|
||||
IsDataLoading = true;
|
||||
_tracks.Clear();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
foreach (var folder in _settingsService.LocalMediaFolders)
|
||||
{
|
||||
if (Directory.Exists(folder.Path) && folder.IsEnabled)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
Track track = new(file);
|
||||
if (track.Duration <= 0) continue;
|
||||
_tracks.Add(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
ApplyPlaylist();
|
||||
ApplySongSearchQuery();
|
||||
IsLocalMediaNotFound = !_filteredTracks.Any();
|
||||
ApplySongOrderType();
|
||||
IsDataLoading = false;
|
||||
});
|
||||
});
|
||||
}, TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
|
||||
public void ApplyPlaylist()
|
||||
{
|
||||
if (SelectedSongsTabInfo?.FilterValue == string.Empty)
|
||||
{
|
||||
_playlistTracks = _tracks;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (SelectedSongsTabInfo?.FilterProperty)
|
||||
{
|
||||
case CommonSongProperty.Title:
|
||||
_playlistTracks = _tracks.Where(t => t.Title.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.Album:
|
||||
_playlistTracks = _tracks.Where(t => t.Album.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
case CommonSongProperty.Artist:
|
||||
_playlistTracks = _tracks.Where(t => t.Artist.Equals(SelectedSongsTabInfo.FilterValue, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ApplySongSearchQuery();
|
||||
IsLocalMediaNotFound = !_filteredTracks.Any();
|
||||
ApplySongOrderType();
|
||||
}
|
||||
|
||||
public void ApplySongSearchQuery()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SongSearchQuery))
|
||||
{
|
||||
_filteredTracks = _playlistTracks;
|
||||
return;
|
||||
}
|
||||
_filteredTracks = _playlistTracks.Where(t =>
|
||||
t.Title.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
|
||||
t.Artist.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
|
||||
t.Album.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
}
|
||||
|
||||
private void ApplySongOrderType()
|
||||
{
|
||||
switch (SongOrderType)
|
||||
{
|
||||
case CommonSongProperty.Title:
|
||||
GroupedTracks = _filteredTracks.GetGroupedBy(
|
||||
t => LanguageHelper.GetOrderChar(t.Title),
|
||||
o => ((Track)o).Title
|
||||
);
|
||||
break;
|
||||
case CommonSongProperty.Artist:
|
||||
GroupedTracks = _filteredTracks.GetGroupedBy(
|
||||
t => LanguageHelper.GetOrderChar(t.Artist),
|
||||
o => ((Track)o).Artist
|
||||
);
|
||||
break;
|
||||
case CommonSongProperty.Album:
|
||||
GroupedTracks = _filteredTracks.GetGroupedBy(
|
||||
t => LanguageHelper.GetOrderChar(t.Album),
|
||||
o => ((Track)o).Album
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSelectedPlaylist(SongsTabInfo playlist)
|
||||
{
|
||||
var found = SongsTabInfoList.Where(x => x.FilterProperty == playlist.FilterProperty && x.FilterValue == playlist.FilterValue)
|
||||
.ToList().FirstOrDefault();
|
||||
if (found == null)
|
||||
{
|
||||
SongsTabInfoList.Add(playlist);
|
||||
SelectedSongsTabInfoIndex = SongsTabInfoList.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedSongsTabInfoIndex = SongsTabInfoList.IndexOf(found);
|
||||
}
|
||||
ApplyPlaylist();
|
||||
}
|
||||
|
||||
public void PlayTrackAt(int index)
|
||||
{
|
||||
PlayTrack(TrackPlayingQueue.ElementAtOrDefault(index));
|
||||
}
|
||||
|
||||
public void PlayTrack(PlayQueueItem? playQueueItem)
|
||||
{
|
||||
_timelineController.Pause();
|
||||
_mediaPlayer.Source = null;
|
||||
if (playQueueItem == null)
|
||||
{
|
||||
_smtc.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var track = playQueueItem.Track;
|
||||
var updater = _smtc.DisplayUpdater;
|
||||
_smtc.IsEnabled = true;
|
||||
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));
|
||||
updater.AppMediaId = Package.Current.Id.FullName;
|
||||
updater.Type = MediaPlaybackType.Music;
|
||||
updater.MusicProperties.Title = track.Title;
|
||||
updater.MusicProperties.Artist = track.Artist;
|
||||
updater.MusicProperties.AlbumTitle = track.Album;
|
||||
if (track.EmbeddedPictures.FirstOrDefault()?.PictureData is byte[] pictureData)
|
||||
{
|
||||
updater.Thumbnail = ImageHelper.ByteArrayToRandomAccessStreamReference(pictureData);
|
||||
}
|
||||
else
|
||||
{
|
||||
updater.Thumbnail = null;
|
||||
}
|
||||
updater.Update();
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSongOrderTypeChanged(CommonSongProperty value)
|
||||
{
|
||||
ApplySongOrderType();
|
||||
IsLocalMediaNotFound = !_filteredTracks.Any();
|
||||
}
|
||||
|
||||
partial void OnSongSearchQueryChanged(string value)
|
||||
{
|
||||
ApplySongSearchQuery();
|
||||
IsLocalMediaNotFound = !_filteredTracks.Any();
|
||||
ApplySongOrderType();
|
||||
}
|
||||
|
||||
partial void OnPlayingSongIndexChanged(int value)
|
||||
{
|
||||
DisplayedPlayingSongIndex = value + 1;
|
||||
}
|
||||
|
||||
partial void OnPlaybackOrderChanged(PlaybackOrder value)
|
||||
{
|
||||
_settingsService.PlaybackOrder = value;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders))
|
||||
{
|
||||
RefreshSongs();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class SettingsPageViewModel
|
||||
{
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ITranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
_libreTranslateService = libreTranslateService;
|
||||
|
||||
IsLibreTranslateEnabled = _settingsService.IsLibreTranslateEnabled;
|
||||
LibreTranslateServer = _settingsService.LibreTranslateServer;
|
||||
SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
|
||||
LocalMediaFolders = [.. _settingsService.LocalMediaFolders];
|
||||
LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo];
|
||||
AlbumArtSearchProvidersInfo = [.. _settingsService.AlbumArtSearchProvidersInfo];
|
||||
|
||||
Language = _settingsService.Language;
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
|
||||
AutoStartWindowType = _settingsService.AutoStartWindowType;
|
||||
AutoLockOnDesktopMode = _settingsService.AutoLockOnDesktopMode;
|
||||
|
||||
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
CoverAcrylicEffectAmount = _settingsService.CoverAcrylicEffectAmount;
|
||||
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
SongInfoAlignmentType = _settingsService.SongInfoAlignmentType;
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
|
||||
// Font size
|
||||
LyricsStandardFontSize = _settingsService.LyricsStandardFontSize;
|
||||
LyricsDockFontSize = _settingsService.LyricsDockFontSize;
|
||||
LyricsDesktopFontSize = _settingsService.LyricsDesktopFontSize;
|
||||
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
LyricsHighlightScope = _settingsService.LyricsHighlightScope;
|
||||
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
|
||||
LyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
LyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
LyricsStrokeFontColorType = _settingsService.LyricsStrokeFontColorType;
|
||||
|
||||
LyricsCustomBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
LyricsCustomFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
LyricsCustomStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
|
||||
|
||||
LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme;
|
||||
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
|
||||
IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
|
||||
LyricsScrollEasingType = _settingsService.LyricsScrollEasingType;
|
||||
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
|
||||
TimelineSyncThreshold = _settingsService.TimelineSyncThreshold;
|
||||
|
||||
IsLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled;
|
||||
ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged;
|
||||
LockHotKeyIndex = _settingsService.LockHotKeyIndex;
|
||||
|
||||
LXMusicServer = _settingsService.LXMusicServer;
|
||||
DockPlacement = _settingsService.DockPlacement;
|
||||
LyricsBgFontOpacity = _settingsService.LyricsBgFontOpacity;
|
||||
HideWindowWhenNotPlaying = _settingsService.HideWindowWhenNotPlaying;
|
||||
DockWindowHeight = _settingsService.DockWindowHeight;
|
||||
|
||||
SystemFontNames = [.. FontHelper.SystemFontFamilies];
|
||||
SelectedFontFamilyIndex = _settingsService.SelectedFontFamilyIndex;
|
||||
LyricsFontFamily = _settingsService.LyricsFontFamily;
|
||||
IsDragEverywhereEnabled = _settingsService.IsDragEverywhereEnabled;
|
||||
|
||||
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
|
||||
SelectedDockMonitorDeviceName = _settingsService.DockMonitorDeviceName;
|
||||
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Globalization;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class SettingsPageViewModel
|
||||
{
|
||||
partial void OnDockPlacementChanged(DockPlacement value)
|
||||
{
|
||||
_settingsService.DockPlacement = value;
|
||||
}
|
||||
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
|
||||
{
|
||||
_settingsService.LyricsScrollEasingType = value;
|
||||
}
|
||||
partial void OnLyricsScrollDurationChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsScrollDuration = value;
|
||||
}
|
||||
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
|
||||
{
|
||||
_settingsService.LyricsBackgroundTheme = value;
|
||||
}
|
||||
partial void OnLyricsFontStrokeWidthChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontStrokeWidth = value;
|
||||
}
|
||||
partial void OnIgnoreFullscreenWindowChanged(bool value)
|
||||
{
|
||||
_settingsService.IgnoreFullscreenWindow = value;
|
||||
}
|
||||
partial void OnSelectedTargetLanguageIndexChanged(int value)
|
||||
{
|
||||
_settingsService.SelectedTargetLanguageIndex = value;
|
||||
}
|
||||
partial void OnLibreTranslateServerChanged(string value)
|
||||
{
|
||||
_settingsService.LibreTranslateServer = value;
|
||||
}
|
||||
partial void OnLXMusicServerChanged(string value)
|
||||
{
|
||||
_settingsService.LXMusicServer = value;
|
||||
}
|
||||
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
|
||||
{
|
||||
_settingsService.AutoStartWindowType = value;
|
||||
}
|
||||
partial void OnAutoLockOnDesktopModeChanged(bool value)
|
||||
{
|
||||
_settingsService.AutoLockOnDesktopMode = value;
|
||||
}
|
||||
partial void OnCoverImageRadiusChanged(int value)
|
||||
{
|
||||
_settingsService.CoverImageRadius = value;
|
||||
}
|
||||
partial void OnCoverOverlayBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.CoverOverlayBlurAmount = value;
|
||||
}
|
||||
partial void OnCoverAcrylicEffectAmountChanged(int value)
|
||||
{
|
||||
_settingsService.CoverAcrylicEffectAmount = value;
|
||||
}
|
||||
partial void OnCoverOverlayOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.CoverOverlayOpacity = value;
|
||||
}
|
||||
partial void OnIsDynamicCoverOverlayEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsDynamicCoverOverlayEnabled = value;
|
||||
}
|
||||
partial void OnLanguageChanged(Enums.Language value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case Enums.Language.FollowSystem:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "";
|
||||
break;
|
||||
case Enums.Language.English:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
|
||||
break;
|
||||
case Enums.Language.SimplifiedChinese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";
|
||||
break;
|
||||
case Enums.Language.TraditionalChinese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "zh-TW";
|
||||
break;
|
||||
case Enums.Language.Japanese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "ja-JP";
|
||||
break;
|
||||
case Enums.Language.Korean:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "ko-KR";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_settingsService.Language = Language;
|
||||
}
|
||||
partial void OnIsFanLyricsEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsFanLyricsEnabled = value;
|
||||
}
|
||||
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsGlowEffectEnabled = value;
|
||||
}
|
||||
partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.LyricsAlignmentType = value;
|
||||
}
|
||||
partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.SongInfoAlignmentType = value;
|
||||
}
|
||||
partial void OnLyricsBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBlurAmount = value;
|
||||
}
|
||||
partial void OnLyricsCustomBgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomBgFontColor = value;
|
||||
}
|
||||
partial void OnLyricsCustomFgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomFgFontColor = value;
|
||||
}
|
||||
partial void OnLyricsCustomStrokeFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomStrokeFontColor = value;
|
||||
}
|
||||
partial void OnLyricsBgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsBgFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsFgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsFgFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsStrokeFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsStrokeFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsStandardFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsStandardFontSize = value;
|
||||
}
|
||||
partial void OnLyricsDockFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsDockFontSize = value;
|
||||
}
|
||||
partial void OnLyricsDesktopFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsDesktopFontSize = value;
|
||||
}
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_settingsService.LyricsFontWeight = value;
|
||||
}
|
||||
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsGlowEffectScope = value;
|
||||
}
|
||||
partial void OnLyricsHighlightScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsHighlightScope = value;
|
||||
}
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_settingsService.LyricsLineSpacingFactor = value;
|
||||
}
|
||||
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsVerticalEdgeOpacity = value;
|
||||
}
|
||||
partial void OnTimelineSyncThresholdChanged(int value)
|
||||
{
|
||||
_settingsService.TimelineSyncThreshold = value;
|
||||
}
|
||||
partial void OnIsLyricsFloatAnimationEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsFloatAnimationEnabled = value;
|
||||
}
|
||||
partial void OnResetPositionOffsetOnSongChangedChanged(bool value)
|
||||
{
|
||||
_settingsService.ResetPositionOffsetOnSongChanged = value;
|
||||
}
|
||||
partial void OnLyricsBgFontOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBgFontOpacity = value;
|
||||
}
|
||||
partial void OnHideWindowWhenNotPlayingChanged(bool value)
|
||||
{
|
||||
_settingsService.HideWindowWhenNotPlaying = value;
|
||||
}
|
||||
partial void OnDockWindowHeightChanged(int value)
|
||||
{
|
||||
_settingsService.DockWindowHeight = value;
|
||||
}
|
||||
partial void OnSelectedFontFamilyIndexChanged(int value)
|
||||
{
|
||||
_settingsService.SelectedFontFamilyIndex = value;
|
||||
LyricsFontFamily = SystemFontNames[value];
|
||||
}
|
||||
partial void OnLyricsFontFamilyChanged(string value)
|
||||
{
|
||||
_settingsService.LyricsFontFamily = value;
|
||||
}
|
||||
partial void OnIsDragEverywhereEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsDragEverywhereEnabled = value;
|
||||
|
||||
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow != null)
|
||||
{
|
||||
lyricsWindow.UpdateTitleBarArea();
|
||||
}
|
||||
}
|
||||
partial void OnIsLibreTranslateEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLibreTranslateEnabled = value;
|
||||
}
|
||||
partial void OnSelectedDockMonitorDeviceNameChanged(string value)
|
||||
{
|
||||
_settingsService.DockMonitorDeviceName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
public partial class SettingsPageViewModel
|
||||
{
|
||||
public string Version { get; set; } = MetadataHelper.AppVersion;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLibreTranslateEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsDragEverywhereEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial string LyricsFontFamily { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<string> SystemFontNames { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int SelectedFontFamilyIndex { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial DockPlacement DockPlacement { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LockHotKeyIndex { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AutoStartWindowType AutoStartWindowType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool AutoLockOnDesktopMode { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverImageRadius { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverOverlayBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverOverlayOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDebugOverlayEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLogEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverAcrylicEffectAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Enums.Language Language { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<LocalMediaFolder> LocalMediaFolders { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsFanLyricsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType SongInfoAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomBgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomFgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBgFontOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsStandardFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsDockFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsDesktopFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsHighlightScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial object NavViewSelectedItemTag { get; set; } = "App";
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool ResetPositionOffsetOnSongChanged { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsFloatAnimationEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string LibreTranslateServer { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int SelectedTargetLanguageIndex { get; set; } = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLibreTranslateServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontStrokeWidth { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial EasingType LyricsScrollEasingType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsScrollDuration { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int TimelineSyncThreshold { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLXMusicServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial string LXMusicServer { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool HideWindowWhenNotPlaying { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int DockWindowHeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial string SelectedDockMonitorDeviceName { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<string> MonitorDeviceNames { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -7,24 +7,17 @@ using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using ShadowViewer.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Globalization;
|
||||
using Windows.Media.Playback;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Popups;
|
||||
using WinRT.Interop;
|
||||
using MetadataHelper = BetterLyrics.WinUI3.Helper.MetadataHelper;
|
||||
|
||||
@@ -38,262 +31,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private readonly string _autoStartupTaskId = "AutoStartup";
|
||||
|
||||
public SettingsPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IPlaybackService playbackService, ITranslateService libreTranslateService) : base(settingsService)
|
||||
{
|
||||
_libWatcherService = libWatcherService;
|
||||
_playbackService = playbackService;
|
||||
_libreTranslateService = libreTranslateService;
|
||||
|
||||
LibreTranslateServer = _settingsService.LibreTranslateServer;
|
||||
SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
|
||||
LocalLyricsFolders = [.. _settingsService.LocalLyricsFolders];
|
||||
LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo];
|
||||
AlbumArtSearchProvidersInfo = [.. _settingsService.AlbumArtSearchProvidersInfo];
|
||||
|
||||
Language = _settingsService.Language;
|
||||
CoverImageRadius = _settingsService.CoverImageRadius;
|
||||
|
||||
AutoStartWindowType = _settingsService.AutoStartWindowType;
|
||||
AutoLockOnDesktopMode = _settingsService.AutoLockOnDesktopMode;
|
||||
|
||||
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
|
||||
CoverOverlayOpacity = _settingsService.CoverOverlayOpacity;
|
||||
CoverOverlayBlurAmount = _settingsService.CoverOverlayBlurAmount;
|
||||
|
||||
LyricsAlignmentType = _settingsService.LyricsAlignmentType;
|
||||
SongInfoAlignmentType = _settingsService.SongInfoAlignmentType;
|
||||
LyricsFontWeight = _settingsService.LyricsFontWeight;
|
||||
LyricsBlurAmount = _settingsService.LyricsBlurAmount;
|
||||
LyricsVerticalEdgeOpacity = _settingsService.LyricsVerticalEdgeOpacity;
|
||||
LyricsLineSpacingFactor = _settingsService.LyricsLineSpacingFactor;
|
||||
LyricsFontSize = _settingsService.LyricsFontSize;
|
||||
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
|
||||
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
|
||||
LyricsHighlightScope = _settingsService.LyricsHighlightScope;
|
||||
IsFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
|
||||
LyricsBgFontColorType = _settingsService.LyricsBgFontColorType;
|
||||
LyricsFgFontColorType = _settingsService.LyricsFgFontColorType;
|
||||
LyricsStrokeFontColorType = _settingsService.LyricsStrokeFontColorType;
|
||||
|
||||
LyricsCustomBgFontColor = _settingsService.LyricsCustomBgFontColor;
|
||||
LyricsCustomFgFontColor = _settingsService.LyricsCustomFgFontColor;
|
||||
LyricsCustomStrokeFontColor = _settingsService.LyricsCustomStrokeFontColor;
|
||||
|
||||
LyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
LyricsBackgroundTheme = _settingsService.LyricsBackgroundTheme;
|
||||
MediaSourceProvidersInfo = [.. _settingsService.MediaSourceProvidersInfo];
|
||||
IgnoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
|
||||
LyricsScrollEasingType = _settingsService.LyricsScrollEasingType;
|
||||
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
|
||||
TimelineSyncThreshold = _settingsService.TimelineSyncThreshold;
|
||||
|
||||
IsLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled;
|
||||
ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged;
|
||||
LockHotKeyIndex = _settingsService.LockHotKeyIndex;
|
||||
|
||||
LXMusicServer = _settingsService.LXMusicServer;
|
||||
DockPlacement = _settingsService.DockPlacement;
|
||||
LyricsBgFontOpacity = _settingsService.LyricsBgFontOpacity;
|
||||
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
BuildDate = (await Helper.MetadataHelper.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
|
||||
});
|
||||
}
|
||||
|
||||
private void PlaybackService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
|
||||
{
|
||||
MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo];
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial DockPlacement DockPlacement { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LockHotKeyIndex { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AutoStartWindowType AutoStartWindowType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool AutoLockOnDesktopMode { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverImageRadius { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverOverlayBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int CoverOverlayOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDebugOverlayEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLogEnabled { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsDynamicCoverOverlayEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Enums.Language Language { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<LocalLyricsFolder> LocalLyricsFolders { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<MediaSourceProviderInfo> MediaSourceProvidersInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsFanLyricsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsGlowEffectEnabled { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType LyricsAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial TextAlignmentType SongInfoAlignmentType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBlurAmount { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomBgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomFgFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBgFontOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontSize { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontWeight LyricsFontWeight { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LineRenderingType LyricsHighlightScope { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsVerticalEdgeOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial object NavViewSelectedItemTag { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool ResetPositionOffsetOnSongChanged { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsFloatAnimationEnabled { get; set; }
|
||||
|
||||
public string Version { get; set; } = MetadataHelper.AppVersion;
|
||||
|
||||
public string BuildDate { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string LibreTranslateServer { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int SelectedTargetLanguageIndex { get; set; } = 0;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLibreTranslateServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsFontStrokeWidth { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial EasingType LyricsScrollEasingType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsScrollDuration { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int TimelineSyncThreshold { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLXMusicServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial string LXMusicServer { get; set; }
|
||||
|
||||
public void OnLyricsSearchProvidersReordered()
|
||||
{
|
||||
_settingsService.LyricsSearchProvidersInfo = [.. LyricsSearchProvidersInfo];
|
||||
@@ -314,21 +56,21 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
}
|
||||
|
||||
public void RemoveFolderAsync(LocalLyricsFolder folder)
|
||||
public void RemoveFolderAsync(LocalMediaFolder folder)
|
||||
{
|
||||
LocalLyricsFolders.Remove(folder);
|
||||
_settingsService.LocalLyricsFolders = [.. LocalLyricsFolders];
|
||||
_libWatcherService.UpdateWatchers([.. LocalLyricsFolders]);
|
||||
Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders));
|
||||
LocalMediaFolders.Remove(folder);
|
||||
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
|
||||
_libWatcherService.UpdateWatchers([.. LocalMediaFolders]);
|
||||
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
|
||||
}
|
||||
|
||||
public void ToggleLocalLyricsFolder(LocalLyricsFolder folder)
|
||||
public void ToggleLocalLyricsFolder()
|
||||
{
|
||||
_settingsService.LocalLyricsFolders = [.. LocalLyricsFolders];
|
||||
Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders));
|
||||
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
|
||||
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
|
||||
}
|
||||
|
||||
public void ToggleLyricsSearchProvider(LyricsSearchProviderInfo providerInfo)
|
||||
public void ToggleLyricsSearchProvider()
|
||||
{
|
||||
_settingsService.LyricsSearchProvidersInfo = [.. LyricsSearchProvidersInfo];
|
||||
Broadcast(
|
||||
@@ -361,16 +103,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
|
||||
if (LocalLyricsFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
if (LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo"));
|
||||
}
|
||||
else if (LocalLyricsFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
else if (LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的子文件夹
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo"));
|
||||
}
|
||||
else if (LocalLyricsFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
|
||||
else if (LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的父文件夹
|
||||
@@ -378,23 +120,23 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalLyricsFolders.Add(new LocalLyricsFolder(path, true));
|
||||
_settingsService.LocalLyricsFolders = [.. LocalLyricsFolders];
|
||||
_libWatcherService.UpdateWatchers([.. LocalLyricsFolders]);
|
||||
Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders));
|
||||
LocalMediaFolders.Add(new LocalMediaFolder(path, true));
|
||||
_settingsService.LocalMediaFolders = [.. LocalMediaFolders];
|
||||
_libWatcherService.UpdateWatchers([.. LocalMediaFolders]);
|
||||
Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders));
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LaunchProjectGitHubPageAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl));
|
||||
await Windows.System.Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static async Task OpenCacheFolderAsync()
|
||||
{
|
||||
await Launcher.LaunchFolderPathAsync(PathHelper.CacheFolder);
|
||||
await Windows.System.Launcher.LaunchFolderPathAsync(PathHelper.CacheFolder);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -433,7 +175,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
string targetLangCode = LanguageHelper.SupportedTargetLanguages[SelectedTargetLanguageIndex].Code;
|
||||
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
@@ -441,7 +183,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
@@ -457,7 +199,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
Task.Run(async () =>
|
||||
{
|
||||
bool testResult = await NetHelper.CheckConnectivity($"{LXMusicServer}/status");
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString($"SettingsPageServerTest{(testResult ? "Success" : "Failed")}Info"),
|
||||
@@ -467,6 +209,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void RefreshMonitorDeviceNames()
|
||||
{
|
||||
MonitorDeviceNames = [.. MonitorHelper.GetAllMonitorDeviceNames()];
|
||||
SelectedDockMonitorDeviceName = MonitorHelper.GetPrimaryMonitorDeviceName();
|
||||
}
|
||||
|
||||
public async Task<bool> ToggleAutoStartupAsync(bool target)
|
||||
{
|
||||
StartupTask startupTask = await StartupTask.GetAsync(_autoStartupTaskId);
|
||||
@@ -498,177 +247,5 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
partial void OnDockPlacementChanged(DockPlacement value)
|
||||
{
|
||||
_settingsService.DockPlacement = value;
|
||||
}
|
||||
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
|
||||
{
|
||||
_settingsService.LyricsScrollEasingType = value;
|
||||
}
|
||||
partial void OnLyricsScrollDurationChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsScrollDuration = value;
|
||||
}
|
||||
partial void OnLyricsBackgroundThemeChanged(ElementTheme value)
|
||||
{
|
||||
_settingsService.LyricsBackgroundTheme = value;
|
||||
}
|
||||
partial void OnLyricsFontStrokeWidthChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontStrokeWidth = value;
|
||||
}
|
||||
partial void OnIgnoreFullscreenWindowChanged(bool value)
|
||||
{
|
||||
_settingsService.IgnoreFullscreenWindow = value;
|
||||
}
|
||||
partial void OnSelectedTargetLanguageIndexChanged(int value)
|
||||
{
|
||||
_settingsService.SelectedTargetLanguageIndex = value;
|
||||
}
|
||||
partial void OnLibreTranslateServerChanged(string value)
|
||||
{
|
||||
_settingsService.LibreTranslateServer = value;
|
||||
}
|
||||
partial void OnLXMusicServerChanged(string value)
|
||||
{
|
||||
_settingsService.LXMusicServer = value;
|
||||
}
|
||||
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
|
||||
{
|
||||
_settingsService.AutoStartWindowType = value;
|
||||
}
|
||||
partial void OnAutoLockOnDesktopModeChanged(bool value)
|
||||
{
|
||||
_settingsService.AutoLockOnDesktopMode = value;
|
||||
}
|
||||
partial void OnCoverImageRadiusChanged(int value)
|
||||
{
|
||||
_settingsService.CoverImageRadius = value;
|
||||
}
|
||||
partial void OnCoverOverlayBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.CoverOverlayBlurAmount = value;
|
||||
}
|
||||
partial void OnCoverOverlayOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.CoverOverlayOpacity = value;
|
||||
}
|
||||
partial void OnIsDynamicCoverOverlayEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsDynamicCoverOverlayEnabled = value;
|
||||
}
|
||||
partial void OnLanguageChanged(Enums.Language value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case Enums.Language.FollowSystem:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "";
|
||||
break;
|
||||
case Enums.Language.English:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "en-US";
|
||||
break;
|
||||
case Enums.Language.SimplifiedChinese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";
|
||||
break;
|
||||
case Enums.Language.TraditionalChinese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "zh-TW";
|
||||
break;
|
||||
case Enums.Language.Japanese:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "ja-JP";
|
||||
break;
|
||||
case Enums.Language.Korean:
|
||||
ApplicationLanguages.PrimaryLanguageOverride = "ko-KR";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_settingsService.Language = Language;
|
||||
}
|
||||
partial void OnIsFanLyricsEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsFanLyricsEnabled = value;
|
||||
}
|
||||
partial void OnIsLyricsGlowEffectEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsGlowEffectEnabled = value;
|
||||
}
|
||||
partial void OnLyricsAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.LyricsAlignmentType = value;
|
||||
}
|
||||
partial void OnSongInfoAlignmentTypeChanged(TextAlignmentType value)
|
||||
{
|
||||
_settingsService.SongInfoAlignmentType = value;
|
||||
}
|
||||
partial void OnLyricsBlurAmountChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBlurAmount = value;
|
||||
}
|
||||
partial void OnLyricsCustomBgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomBgFontColor = value;
|
||||
}
|
||||
partial void OnLyricsCustomFgFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomFgFontColor = value;
|
||||
}
|
||||
partial void OnLyricsCustomStrokeFontColorChanged(Color value)
|
||||
{
|
||||
_settingsService.LyricsCustomStrokeFontColor = value;
|
||||
}
|
||||
partial void OnLyricsBgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsBgFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsFgFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsFgFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsStrokeFontColorTypeChanged(LyricsFontColorType value)
|
||||
{
|
||||
_settingsService.LyricsStrokeFontColorType = value;
|
||||
}
|
||||
partial void OnLyricsFontSizeChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsFontSize = value;
|
||||
}
|
||||
partial void OnLyricsFontWeightChanged(LyricsFontWeight value)
|
||||
{
|
||||
_settingsService.LyricsFontWeight = value;
|
||||
}
|
||||
partial void OnLyricsGlowEffectScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsGlowEffectScope = value;
|
||||
}
|
||||
partial void OnLyricsHighlightScopeChanged(LineRenderingType value)
|
||||
{
|
||||
_settingsService.LyricsHighlightScope = value;
|
||||
}
|
||||
partial void OnLyricsLineSpacingFactorChanged(float value)
|
||||
{
|
||||
_settingsService.LyricsLineSpacingFactor = value;
|
||||
}
|
||||
partial void OnLyricsVerticalEdgeOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsVerticalEdgeOpacity = value;
|
||||
}
|
||||
partial void OnTimelineSyncThresholdChanged(int value)
|
||||
{
|
||||
_settingsService.TimelineSyncThreshold = value;
|
||||
}
|
||||
partial void OnIsLyricsFloatAnimationEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsFloatAnimationEnabled = value;
|
||||
}
|
||||
partial void OnResetPositionOffsetOnSongChangedChanged(bool value)
|
||||
{
|
||||
_settingsService.ResetPositionOffsetOnSongChanged = value;
|
||||
}
|
||||
partial void OnLyricsBgFontOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBgFontOpacity = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using WinUIEx;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -34,13 +37,46 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private static void ExitApp()
|
||||
{
|
||||
WindowHelper.ExitAllWindows();
|
||||
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow != null)
|
||||
{
|
||||
DockModeHelper.Disable(lyricsWindow);
|
||||
}
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void RestartApp()
|
||||
{
|
||||
WindowHelper.RestartApp();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void ResetWindowPosition()
|
||||
{
|
||||
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow != null)
|
||||
{
|
||||
lyricsWindow.MoveAndResize(0, 0, 800, 600);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void OpenSettings()
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
WindowHelper.OpenWindow<SettingsWindow>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void OpenMusicGallery()
|
||||
{
|
||||
WindowHelper.OpenWindow<MusicGalleryWindow>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private static void OpenLyricsWindow()
|
||||
{
|
||||
WindowHelper.OpenWindow<LyricsWindow>();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<Grid x:Name="RootGrid" SizeChanged="RootGrid_SizeChanged">
|
||||
<!-- Lyrics area -->
|
||||
<renderer:LyricsRenderer />
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
FontFamily="{x:Bind ViewModel.LyricsFontFamily, Mode=OneWay}"
|
||||
FontSize="{x:Bind ViewModel.HintMessageFontSize, Mode=OneWay}" />
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
@@ -51,8 +52,9 @@
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
Margin="{x:Bind ViewModel.BottomCommandGridMargin, Mode=OneWay}"
|
||||
Margin="12"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
|
||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||
PointerExited="BottomCommandGrid_PointerExited">
|
||||
@@ -60,270 +62,340 @@
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<Grid Padding="3" HorizontalAlignment="Left">
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<!--<Button Style="{StaticResource GhostButtonStyle}">
|
||||
<Grid>
|
||||
-->
|
||||
<!-- Volumn: 0 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 1-32 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel1"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 33-65 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel2"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 66-100 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel3"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.Volume, Mode=OneWay}" />
|
||||
<TextBlock Margin="0,0,14,0" VerticalAlignment="Center" />
|
||||
<Slider
|
||||
Width="150"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="None"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>-->
|
||||
</StackPanel>
|
||||
<Grid x:Name="BottomCommandContent">
|
||||
<Grid Padding="3" HorizontalAlignment="Left">
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
|
||||
<StackPanel
|
||||
Margin="0,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="2">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{Binding ElementName=TimelineSlider, Path=Value, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||
<TextBlock Text="{Binding ElementName=TimelineSlider, Path=Maximum, Converter={StaticResource SecondsToFormattedTimeConverter}}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Position offset -->
|
||||
<Button Click="TimelineOffsetButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="TimelineOffsetFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.ResetPositionOffsetOnSongChanged, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="LyricsPagePositionOffsetHint" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid Padding="3" HorizontalAlignment="Center">
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PauseSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PlaySongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.NextSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid Padding="3" HorizontalAlignment="Right">
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
|
||||
<!-- Volume -->
|
||||
<!--<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<Grid>
|
||||
|
||||
-->
|
||||
<!-- Volumn: 0 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
|
||||
-->
|
||||
<!-- Volumn: 1-32 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel1"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
|
||||
-->
|
||||
<!-- Volumn: 33-65 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel2"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
|
||||
-->
|
||||
<!-- Volumn: 66-100 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel3"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="VolumeFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.Volume, Mode=OneWay}" />
|
||||
<TextBlock Margin="0,0,14,0" VerticalAlignment="Center" />
|
||||
<Slider
|
||||
Width="150"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="None"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>-->
|
||||
|
||||
<!-- Lyrics & Translation -->
|
||||
<Button
|
||||
Click="TranslationButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="TranslationFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
<controls:SettingsExpander
|
||||
x:Uid="LyricsPageTranslationEnabled"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}" />
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="LyricsPageTranslationOnly" IsEnabled="{x:Bind ViewModel.IsTranslationEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.ShowTranslationOnly, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
|
||||
<!-- Display type -->
|
||||
<Button
|
||||
x:Name="DisplayTypeSwitchButton"
|
||||
x:Uid="MainPageDisplayTypeSwitcher"
|
||||
Click="DisplayTypeSwitchButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="PresentationTypeToolTip" x:Uid="LyricsPageDisplayTypeButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.ContextFlyout>
|
||||
<Flyout x:Name="DisplayTypeSwitchFlyout" ShouldConstrainToRootBounds="false">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="12,2,12,8" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<RadioButtons MaxColumns="1" SelectedIndex="{x:Bind ViewModel.DisplayType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<RadioButton x:Uid="MainPageAlbumArtOnly" Click="AlbumArtOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageLyriscOnly" Click="LyricsOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageSplitView" Click="SplitViewRadioButton_Click" />
|
||||
</RadioButtons>
|
||||
</Flyout>
|
||||
</Button.ContextFlyout>
|
||||
</Button>
|
||||
|
||||
<!-- Settings -->
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="SettingsToolTip" x:Uid="LyricsPageSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Slider
|
||||
x:Name="TimelineSlider"
|
||||
Margin="0,-32,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
Maximum="{x:Bind ViewModel.SongDurationSeconds, Mode=OneWay}"
|
||||
Minimum="0"
|
||||
Style="{StaticResource GhostSliderStyle}"
|
||||
Tapped="TimelineSliderOverlay_Tapped"
|
||||
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"
|
||||
Value="{x:Bind ViewModel.TimelinePositionSeconds, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom command flyout trigger -->
|
||||
<Grid
|
||||
x:Name="BottomCommandFlyoutTrigger"
|
||||
Height="12"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
CornerRadius="3,3,0,0"
|
||||
Opacity="{x:Bind ViewModel.BottomCommandFlyoutTriggerOpacity, Mode=OneWay}"
|
||||
PointerEntered="BottomCommandFlyoutTrigger_PointerEntered"
|
||||
PointerExited="BottomCommandFlyoutTrigger_PointerExited"
|
||||
Tapped="BottomCommandFlyoutTrigger_Tapped">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PauseSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PlaySongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.NextSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
x:Name="BottomCommandFlyoutTriggerHint"
|
||||
Width="150"
|
||||
Margin="4"
|
||||
Background="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
CornerRadius="2"
|
||||
Translation="0,0,0">
|
||||
<Grid.TranslationTransition>
|
||||
<Vector3Transition />
|
||||
</Grid.TranslationTransition>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Position offset -->
|
||||
<Button Click="TimelineOffsetButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="TimelineOffsetFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.ResetPositionOffsetOnSongChanged, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="LyricsPagePositionOffsetHint" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
<!-- Translation -->
|
||||
<Button
|
||||
Click="TranslationButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="TranslationFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<ToggleSwitch x:Uid="LyricsPageTranslationEnabled" IsOn="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
x:Uid="LyricsPageTranslationOnly"
|
||||
IsEnabled="{x:Bind ViewModel.IsTranslationEnabled, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.ShowTranslationOnly, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
<!-- Display type -->
|
||||
<Button
|
||||
x:Name="DisplayTypeSwitchButton"
|
||||
x:Uid="MainPageDisplayTypeSwitcher"
|
||||
Click="DisplayTypeSwitchButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="PresentationTypeToolTip" x:Uid="LyricsPageDisplayTypeButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="DisplayTypeSwitchFlyout" ShouldConstrainToRootBounds="false">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="12,2,12,8" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<RadioButtons MaxColumns="1" SelectedIndex="{x:Bind ViewModel.DisplayType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<RadioButton x:Uid="MainPageAlbumArtOnly" Click="AlbumArtOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageLyriscOnly" Click="LyricsOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageSplitView" Click="SplitViewRadioButton_Click" />
|
||||
</RadioButtons>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
<!-- Settings -->
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="SettingsToolTip" x:Uid="LyricsPageSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid.ContextFlyout>
|
||||
<Flyout x:Name="BottomCommandFlyout" ShouldConstrainToRootBounds="False">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="MinWidth" Value="450" />
|
||||
<Setter Property="MinHeight" Value="100" />
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<Grid x:Name="BottomCommandFlyoutContainer" VerticalAlignment="Bottom" />
|
||||
</Flyout>
|
||||
</Grid.ContextFlyout>
|
||||
</Grid>
|
||||
|
||||
<TeachingTip
|
||||
|
||||
@@ -6,6 +6,8 @@ using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -14,16 +16,17 @@ namespace BetterLyrics.WinUI3.Views
|
||||
public sealed partial class LyricsPage : Page
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService<IPlaybackService>();
|
||||
|
||||
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
|
||||
|
||||
public LyricsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
DataContext = Ioc.Default.GetService<LyricsPageViewModel>();
|
||||
DataContext = Ioc.Default.GetRequiredService<LyricsPageViewModel>();
|
||||
}
|
||||
|
||||
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
|
||||
|
||||
private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args)
|
||||
{
|
||||
ViewModel.IsFirstRun = false;
|
||||
@@ -54,18 +57,20 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
if (ViewModel.IsImmersiveMode && BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 1f;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
if (ViewModel.IsImmersiveMode && BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 0f;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void DisplayTypeSwitchButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -75,12 +80,69 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void TimelineOffsetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TimelineOffsetFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
TimelineOffsetFlyout.ShowAt(BottomLeftCommandStackPanel);
|
||||
}
|
||||
|
||||
private void TranslationButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TranslationFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (e.NewSize.Width < 450 || e.NewSize.Height < 100)
|
||||
{
|
||||
if (BottomCommandGrid.Children.Count != 0)
|
||||
{
|
||||
BottomCommandGrid.Children.Remove(BottomCommandContent);
|
||||
BottomCommandFlyoutContainer.Children.Add(BottomCommandContent);
|
||||
}
|
||||
BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
BottomCommandFlyout.Hide();
|
||||
BottomCommandFlyoutContainer.Children.Remove(BottomCommandContent);
|
||||
BottomCommandGrid.Children.Add(BottomCommandContent);
|
||||
}
|
||||
BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 12, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//private void VolumeButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
//}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsImmersiveMode && BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandFlyoutTriggerOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsImmersiveMode && BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
ViewModel.BottomCommandFlyoutTriggerOpacity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void BottomCommandFlyoutTrigger_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
if (BottomCommandFlyoutContainer.Children.Count != 0)
|
||||
{
|
||||
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
private void TimelineSliderOverlay_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
_playbackService.ChangePosition(TimelineSlider.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
<!-- Top command -->
|
||||
<Grid
|
||||
x:Name="TopCommandGrid"
|
||||
Margin="12"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
Opacity="{x:Bind ViewModel.TopCommandGridOpacity, Mode=OneWay}"
|
||||
PointerEntered="TopCommandGrid_PointerEntered"
|
||||
PointerExited="TopCommandGrid_PointerExited">
|
||||
@@ -34,10 +34,7 @@
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<Grid Padding="3" HorizontalAlignment="Left">
|
||||
<StackPanel
|
||||
x:Name="TopLeftCommandStackPanel"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -46,35 +43,11 @@
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<ToggleButton
|
||||
x:Name="ImmersiveButton"
|
||||
Command="{x:Bind ViewModel.ImmersiveToggleButtonEnabledChangedCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}"
|
||||
Style="{StaticResource TitleBarToggleButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize}"
|
||||
Glyph="" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="TopRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Look -->
|
||||
<Button
|
||||
x:Name="ClickThroughButton"
|
||||
Command="{x:Bind ViewModel.ToggleLockWindowCommand}"
|
||||
Click="ClickThroughButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
@@ -121,66 +94,50 @@
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DockFlyoutItem"
|
||||
x:Uid="HostWindowDockFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDockModeCommand}"
|
||||
Click="DockFlyoutItem_Click"
|
||||
IsChecked="{x:Bind ViewModel.IsDockMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DesktopFlyoutItem"
|
||||
x:Uid="HostWindowDesktopFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDesktopModeCommand}"
|
||||
Click="DesktopFlyoutItem_Click"
|
||||
IsChecked="{x:Bind ViewModel.IsDesktopMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="MiniFlyoutItem"
|
||||
x:Uid="BaseWindowMiniFlyoutItem"
|
||||
Click="MiniFlyoutItem_Click" />
|
||||
<MenuFlyoutItem
|
||||
x:Name="MusicGalleryFlyoutItem"
|
||||
x:Uid="SystemTrayMusicGallery"
|
||||
Click="MusicGalleryMenuFlyoutItem_Click" />
|
||||
<MenuFlyoutItem
|
||||
x:Name="SettingsFlyoutItem"
|
||||
x:Uid="HostWindowSettingsFlyoutItem"
|
||||
x:Uid="SystemTraySettings"
|
||||
Click="SettingsMenuFlyoutItem_Click" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<!-- Immersive mode -->
|
||||
<ToggleButton
|
||||
x:Name="ImmersiveButton"
|
||||
Command="{x:Bind ViewModel.ImmersiveToggleButtonEnabledChangedCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}"
|
||||
Style="{StaticResource TitleBarToggleButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize}"
|
||||
Glyph="" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Window Minimise -->
|
||||
<Button
|
||||
x:Name="MinimiseButton"
|
||||
Click="MinimiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Maximise -->
|
||||
<Button
|
||||
x:Name="MaximiseButton"
|
||||
Click="MaximiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Restore -->
|
||||
<Button
|
||||
x:Name="RestoreButton"
|
||||
Click="RestoreButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Close -->
|
||||
<Button
|
||||
x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<Grid Padding="3" HorizontalAlignment="Right">
|
||||
<StackPanel
|
||||
x:Name="TopRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -3,18 +3,14 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using System.Drawing;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.System;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
using WinUIEx.Messaging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
@@ -28,16 +24,37 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
AppWindow.SetIcons();
|
||||
|
||||
AppWindow.Changed += AppWindow_Changed;
|
||||
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
||||
UpdateTitleBarArea();
|
||||
|
||||
Title = App.ResourceLoader!.GetString("LyricsPageTitle");
|
||||
SetTitleBar(TopCommandGrid);
|
||||
//SetTitleBar(RootGrid);
|
||||
|
||||
_wmm = new WindowMessageMonitor(this);
|
||||
_wmm.WindowMessageReceived += Wmm_WindowMessageReceived;
|
||||
|
||||
AppWindow.Closing += AppWindow_Closing;
|
||||
}
|
||||
|
||||
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
|
||||
{
|
||||
DockModeHelper.Disable(this);
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
public void UpdateTitleBarArea()
|
||||
{
|
||||
if (_settingsService.IsDragEverywhereEnabled)
|
||||
{
|
||||
SetTitleBar(RootGrid);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetTitleBar(TopCommandGrid);
|
||||
}
|
||||
}
|
||||
|
||||
private void Wmm_WindowMessageReceived(object? sender, WindowMessageEventArgs e)
|
||||
@@ -61,8 +78,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
_settingsService.StandardWindowLeft,
|
||||
_settingsService.StandardWindowTop,
|
||||
_settingsService.StandardWindowWidth,
|
||||
_settingsService.StandardWindowHeight
|
||||
));
|
||||
_settingsService.StandardWindowHeight));
|
||||
break;
|
||||
case AutoStartWindowType.DockMode:
|
||||
DockFlyoutItem.IsChecked = true;
|
||||
@@ -94,28 +110,34 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
if (args.DidPositionChange || args.DidSizeChange)
|
||||
{
|
||||
var rect = AppWindow.Position;
|
||||
var size = AppWindow.Size;
|
||||
var rect = AppWindow.Position;
|
||||
|
||||
if (ViewModel.IsDesktopMode)
|
||||
if (rect.X < 0 && rect.Y < 0 && rect.X + size.Width < 0 && rect.Y + size.Height < 0)
|
||||
{
|
||||
_settingsService.DesktopWindowLeft = rect.X;
|
||||
_settingsService.DesktopWindowTop = rect.Y;
|
||||
_settingsService.DesktopWindowWidth = size.Width;
|
||||
_settingsService.DesktopWindowHeight = size.Height;
|
||||
}
|
||||
else if (ViewModel.IsDockMode)
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_settingsService.StandardWindowLeft = rect.X;
|
||||
_settingsService.StandardWindowTop = rect.Y;
|
||||
_settingsService.StandardWindowWidth = size.Width;
|
||||
_settingsService.StandardWindowHeight = size.Height;
|
||||
}
|
||||
if (ViewModel.IsDesktopMode)
|
||||
{
|
||||
_settingsService.DesktopWindowLeft = rect.X;
|
||||
_settingsService.DesktopWindowTop = rect.Y;
|
||||
_settingsService.DesktopWindowWidth = size.Width;
|
||||
_settingsService.DesktopWindowHeight = size.Height;
|
||||
}
|
||||
else if (ViewModel.IsDockMode)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_settingsService.StandardWindowLeft = rect.X;
|
||||
_settingsService.StandardWindowTop = rect.Y;
|
||||
_settingsService.StandardWindowWidth = size.Width;
|
||||
_settingsService.StandardWindowHeight = size.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,26 +174,22 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
WindowHelper.OpenWindow<SettingsWindow>();
|
||||
}
|
||||
|
||||
private void UpdateTitleBarWindowButtonsVisibility()
|
||||
{
|
||||
TopCommandGrid.Margin = new Thickness(12);
|
||||
|
||||
switch (AppWindow.Presenter.Kind)
|
||||
{
|
||||
case AppWindowPresenterKind.Default:
|
||||
break;
|
||||
case AppWindowPresenterKind.CompactOverlay:
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility = DesktopFlyoutItem.Visibility = FullScreenFlyoutItem.Visibility = DockFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
|
||||
ViewModel.IsImmersiveMode = true;
|
||||
break;
|
||||
case AppWindowPresenterKind.FullScreen:
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
@@ -188,7 +206,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = false;
|
||||
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility =
|
||||
@@ -197,14 +214,12 @@ namespace BetterLyrics.WinUI3.Views
|
||||
Visibility.Collapsed;
|
||||
|
||||
ViewModel.IsImmersiveMode = true;
|
||||
TopCommandGrid.Margin = new Thickness();
|
||||
}
|
||||
else if (DesktopFlyoutItem.IsChecked)
|
||||
{
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = false;
|
||||
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
FullScreenFlyoutItem.Visibility =
|
||||
@@ -218,7 +233,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
overlappedPresenter.IsMinimizable =
|
||||
overlappedPresenter.IsMaximizable = true;
|
||||
|
||||
MinimiseButton.Visibility =
|
||||
AOTFlyoutItem.Visibility =
|
||||
DesktopFlyoutItem.Visibility =
|
||||
DockFlyoutItem.Visibility =
|
||||
@@ -229,17 +243,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
|
||||
|
||||
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Collapsed;
|
||||
RestoreButton.Visibility = Visibility.Visible;
|
||||
}
|
||||
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
|
||||
{
|
||||
MaximiseButton.Visibility = Visibility.Visible;
|
||||
RestoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
ViewModel.IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
}
|
||||
break;
|
||||
@@ -248,35 +251,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.ExitAllWindows();
|
||||
}
|
||||
|
||||
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.Maximize();
|
||||
}
|
||||
}
|
||||
|
||||
private void MinimiseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.Minimize();
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.Restore();
|
||||
}
|
||||
}
|
||||
|
||||
private void TopCommandGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
@@ -301,27 +275,38 @@ namespace BetterLyrics.WinUI3.Views
|
||||
private void RootGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.IsMouseWithinWindow = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RootGrid_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.IsMouseWithinWindow = false;
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (ClickThroughButton == null) return;
|
||||
}
|
||||
|
||||
// <20><>ȡ<EFBFBD><C8A1><EFBFBD>ؼ<EFBFBD><D8BC>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD>е<EFBFBD>λ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻǣ<CFBD>
|
||||
var transform = ClickThroughButton.TransformToVisual(Content);
|
||||
var point = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
|
||||
var btnRect = new Rectangle(
|
||||
(int)point.X,
|
||||
(int)point.Y,
|
||||
(int)ClickThroughButton.ActualWidth,
|
||||
(int)ClickThroughButton.ActualHeight
|
||||
);
|
||||
DesktopModeHelper.SetInteractiveRects([btnRect]);
|
||||
private void ClickThroughButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.ToggleLockWindowCommand.Execute(null);
|
||||
}
|
||||
|
||||
private void DockFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.ToggleDockModeCommand.Execute(null);
|
||||
}
|
||||
|
||||
private void DesktopFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.ToggleDesktopModeCommand.Execute(null);
|
||||
UpdateTitleBarWindowButtonsVisibility();
|
||||
}
|
||||
|
||||
private void MusicGalleryMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
WindowHelper.OpenWindow<MusicGalleryWindow>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,425 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="BetterLyrics.WinUI3.Views.MusicGalleryPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:atl="using:ATL"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:media="using:CommunityToolkit.WinUI.Media"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:templateselector="using:BetterLyrics.WinUI3.TemplateSelector"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<CollectionViewSource
|
||||
x:Name="GroupedTracksCVS"
|
||||
IsSourceGrouped="True"
|
||||
Source="{x:Bind ViewModel.GroupedTracks, Mode=OneWay}" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="3*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
x:Name="SongViewer"
|
||||
Grid.Column="0"
|
||||
Padding="12">
|
||||
<Grid.Tag>
|
||||
<Flyout
|
||||
x:Name="SongFileInfoFlyout"
|
||||
Placement="Bottom"
|
||||
ShouldConstrainToRootBounds="False">
|
||||
<ScrollViewer Width="300" Height="300">
|
||||
<StackPanel Spacing="12">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoTitle" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileArtist" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileAlbum" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoYear" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoDuration" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoBitrate" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoSampleRate" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoBitDepth" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoFormat" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoEncoder" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoPath" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<HyperlinkButton
|
||||
Margin="-12,0,0,0"
|
||||
Click="SongPathHyperlinkButton_Click"
|
||||
Content="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Flyout>
|
||||
</Grid.Tag>
|
||||
|
||||
<ListView
|
||||
VerticalAlignment="Top"
|
||||
ItemsSource="{x:Bind ViewModel.SongsTabInfoList, Mode=OneWay}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled"
|
||||
SelectedIndex="{x:Bind ViewModel.SelectedSongsTabInfoIndex, Mode=TwoWay}">
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:SongsTabInfo">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Background="Transparent"
|
||||
ColumnSpacing="6"
|
||||
Tapped="PlaylistGrid_Tapped">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<FontIcon
|
||||
Grid.Column="0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph="{Binding Icon}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Name}" />
|
||||
</Grid>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Click="PlaylistCloseButton_Click"
|
||||
Content="{ui:FontIcon FontSize=10,
|
||||
FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource GhostButtonStyle}"
|
||||
Visibility="{Binding IsClosable, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Grid Margin="0,48,0,0" VerticalAlignment="Top">
|
||||
<AutoSuggestBox
|
||||
x:Name="SongSearchBox"
|
||||
x:Uid="MusicGalleryPageSongSearchBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
QueryIcon="Find"
|
||||
Text="{x:Bind ViewModel.SongSearchQuery, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Margin="0,96,0,0" VerticalAlignment="Top">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<ToggleButton
|
||||
x:Name="SelectAllToggleButton"
|
||||
x:Uid="MusicGalleryPageSelectAll"
|
||||
Click="SelectAllToggleButton_Click" />
|
||||
<Button x:Uid="MusicGalleryPageAddToPlayingQueue">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToNext" Click="AddSongToQueueNextMenuFlyoutItem_Click" />
|
||||
<MenuFlyoutItem x:Uid="MusicGalleryPageAddToEnd" Click="AddSongToQueueEndMenuFlyoutItem_Click" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Button x:Uid="MusicGalleryPageAddToCustomList" Visibility="Collapsed">
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem x:Uid="MusicGalleryPageNewPlaylist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPageSortType"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<controls:Segmented
|
||||
x:Name="Segmented"
|
||||
SelectedIndex="{x:Bind ViewModel.SongOrderType, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}"
|
||||
SelectionMode="Single">
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByTitle" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByAlbum" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSortByArtist" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" />
|
||||
</controls:Segmented>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<SemanticZoom Margin="0,140,0,0">
|
||||
<SemanticZoom.ZoomedInView>
|
||||
<ListView
|
||||
x:Name="SongListView"
|
||||
ItemsSource="{x:Bind GroupedTracksCVS.View, Mode=OneWay}"
|
||||
SelectionChanged="SongListView_SelectionChanged"
|
||||
SelectionMode="Multiple">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="atl:Track">
|
||||
<Grid Padding="12" RightTapped="SongListViewItemGrid_RightTapped">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="1.5*" />
|
||||
<ColumnDefinition Width="1.5*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 歌曲名 -->
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Title}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- 歌手名 -->
|
||||
<HyperlinkButton
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Click="ArtistHyperlibkButton_Click"
|
||||
Tag="{Binding Artist}">
|
||||
<TextBlock Text="{Binding Artist}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- 专辑名 -->
|
||||
<HyperlinkButton
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Click="AlbumHyperlibkButton_Click"
|
||||
Tag="{Binding Album}">
|
||||
<TextBlock Text="{Binding Album}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- 年份 -->
|
||||
<TextBlock
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Year}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- 歌曲时长 -->
|
||||
<TextBlock
|
||||
Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
<ListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListView.ItemsPanel>
|
||||
<ListView.GroupStyle>
|
||||
<GroupStyle>
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate x:DataType="models:GroupInfoList">
|
||||
<Border AutomationProperties.AccessibilityView="Raw">
|
||||
<TextBlock
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Style="{ThemeResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
</ListView>
|
||||
</SemanticZoom.ZoomedInView>
|
||||
|
||||
<SemanticZoom.ZoomedOutView>
|
||||
<GridView
|
||||
MaxWidth="500"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind GroupedTracksCVS.View.CollectionGroups, Mode=OneWay}"
|
||||
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
|
||||
SelectionMode="None">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:GroupInfoList">
|
||||
<TextBlock Style="{ThemeResource TitleTextBlockStyle}" Text="{Binding}" />
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</SemanticZoom.ZoomedOutView>
|
||||
</SemanticZoom>
|
||||
|
||||
<Grid Margin="0,140,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="12">
|
||||
<Image MaxWidth="200" Source="/Assets/EmptyState.png" />
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPageFileNotFound"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Grid
|
||||
x:Name="PlayQueue"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,12,0">
|
||||
|
||||
<StackPanel Margin="0,10,0,0" Spacing="12">
|
||||
<Grid Margin="0,6,0,0" VerticalAlignment="Top">
|
||||
<TextBlock x:Uid="MusicGalleryPagePlayingQueue" Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.DisplayedPlayingSongIndex, Mode=OneWay}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Button x:Uid="MusicGalleryPageEmptyPlayingQueue" Click="EmptyPlayingQueueButton_Click" />
|
||||
<Button x:Uid="MusicGalleryPageScrollToPlayingItem" Click="ScrollToPlayingItemButton_Click" />
|
||||
</StackPanel>
|
||||
<controls:Segmented HorizontalAlignment="Stretch" SelectedIndex="{x:Bind ViewModel.PlaybackOrder, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}">
|
||||
<controls:Segmented.Items>
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageQueueLoop" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageSingleLoop" />
|
||||
<controls:SegmentedItem x:Uid="MusicGalleryPageQueueRandom" />
|
||||
</controls:Segmented.Items>
|
||||
</controls:Segmented>
|
||||
</StackPanel>
|
||||
|
||||
<ListView
|
||||
x:Name="PlayingQueueListView"
|
||||
Margin="0,136,0,0"
|
||||
ItemsSource="{x:Bind ViewModel.TrackPlayingQueue, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.PlayingSongIndex, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Padding="0,6">
|
||||
<Grid Tapped="PlayingQueueListVireItemGrid_Tapped">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Track.Title}" TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
Foreground="{StaticResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Track.Artist}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid HorizontalAlignment="Right">
|
||||
<Button
|
||||
Click="RemoveFromPlayingQueueButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="MusicGalleryPageRemoveFromPlayingQueue" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<Grid Margin="0,136,0,0">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="12">
|
||||
<Image MaxWidth="100" Source="/Assets/EmptyBox.png" />
|
||||
<TextBlock
|
||||
x:Uid="MusicGalleryPagePlayingQueueEmpty"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Visibility="{x:Bind ViewModel.IsDataLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="12" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="12" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="12" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<labs:Shimmer Grid.Row="0" CornerRadius="6" />
|
||||
<labs:Shimmer Grid.Row="2" CornerRadius="6" />
|
||||
<labs:Shimmer Grid.Row="4" CornerRadius="6" />
|
||||
<labs:Shimmer Grid.Row="6" CornerRadius="6" />
|
||||
</Grid>
|
||||
<ProgressRing IsActive="{x:Bind ViewModel.IsDataLoading, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,171 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.System;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MusicGalleryPage : Page
|
||||
{
|
||||
public MusicGalleryViewModel ViewModel => (MusicGalleryViewModel)DataContext;
|
||||
|
||||
public MusicGalleryPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = Ioc.Default.GetRequiredService<MusicGalleryViewModel>();
|
||||
}
|
||||
|
||||
private void SongListViewItemGrid_RightTapped(object sender, RightTappedRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TrackRightTapped = (Track)((FrameworkElement)sender).DataContext;
|
||||
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
|
||||
}
|
||||
|
||||
private async void SongPathHyperlinkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await LauncherHelper.SelectAndShowFile($"{((HyperlinkButton)sender).Content}");
|
||||
}
|
||||
|
||||
private void PlayingQueueListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.PlayTrack(item);
|
||||
PlayingQueueListView.ScrollIntoView(item);
|
||||
}
|
||||
|
||||
private void EmptyPlayingQueueButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TrackPlayingQueue.Clear();
|
||||
ViewModel.PlayingSongIndex = -1;
|
||||
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
|
||||
}
|
||||
|
||||
private void ScrollToPlayingItemButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.PlayingQueueItem == null) return;
|
||||
PlayingQueueListView.ScrollIntoView(ViewModel.PlayingQueueItem);
|
||||
}
|
||||
|
||||
private void RemoveFromPlayingQueueButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool playNext = false;
|
||||
var item = (PlayQueueItem)((FrameworkElement)sender).DataContext;
|
||||
int index = ViewModel.TrackPlayingQueue.IndexOf(item);
|
||||
if (item == ViewModel.PlayingQueueItem)
|
||||
{
|
||||
playNext = true;
|
||||
}
|
||||
ViewModel.TrackPlayingQueue.Remove(item);
|
||||
if (playNext)
|
||||
{
|
||||
if (ViewModel.TrackPlayingQueue.Count == 0)
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
else if (index >= ViewModel.TrackPlayingQueue.Count)
|
||||
{
|
||||
index = ViewModel.TrackPlayingQueue.Count - 1;
|
||||
}
|
||||
ViewModel.PlayingSongIndex = index;
|
||||
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SongFileInfoMenuFlyoutSubItem_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
|
||||
}
|
||||
|
||||
private void AddSongToQueueNextMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool startPlaying = ViewModel.TrackPlayingQueue.Count == 0;
|
||||
ViewModel.TrackPlayingQueue.InsertRange(ViewModel.PlayingSongIndex + 1, SongListView.SelectedItems.Cast<Track>().Select(x => new PlayQueueItem(x)));
|
||||
if (startPlaying)
|
||||
{
|
||||
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
|
||||
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSongToQueueEndMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool startPlaying = ViewModel.TrackPlayingQueue.Count == 0;
|
||||
ViewModel.TrackPlayingQueue.AddRange(SongListView.SelectedItems.Cast<Track>().Select(x => new PlayQueueItem(x)));
|
||||
if (startPlaying)
|
||||
{
|
||||
ViewModel.PlayingSongIndex = ViewModel.PlayingSongIndex + 1;
|
||||
ViewModel.PlayTrackAt(ViewModel.PlayingSongIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SongListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
ViewModel.SelectedTracks = SongListView.SelectedItems.Cast<Track>().ToList();
|
||||
SelectAllToggleButton.IsChecked = SongListView.SelectedItems.Count == SongListView.Items.Count;
|
||||
}
|
||||
|
||||
private void SelectAllToggleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SelectAllToggleButton.IsChecked == true)
|
||||
{
|
||||
SongListView.SelectAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
SongListView.SelectedItems.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void ArtistHyperlibkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var artist = (string)((HyperlinkButton)sender).Tag;
|
||||
var playlist = new SongsTabInfo(artist, "\uEFA9", true, CommonSongProperty.Artist, artist);
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
}
|
||||
|
||||
private void AlbumHyperlibkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var album = (string)((HyperlinkButton)sender).Tag;
|
||||
var playlist = new SongsTabInfo(album, "\uE93C", true, CommonSongProperty.Album, album);
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
}
|
||||
|
||||
private void PlaylistGrid_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.UpdateSelectedPlaylist(playlist);
|
||||
}
|
||||
|
||||
private void PlaylistCloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var playlist = (SongsTabInfo)((FrameworkElement)sender).DataContext;
|
||||
ViewModel.SongsTabInfoList.Remove(playlist);
|
||||
ViewModel.SelectedSongsTabInfoIndex = 0;
|
||||
ViewModel.ApplyPlaylist();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Window
|
||||
x:Class="BetterLyrics.WinUI3.Views.MusicGalleryWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="MusicGalleryWindow"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<local:MusicGalleryPage />
|
||||
|
||||
</Window>
|
||||
@@ -0,0 +1,39 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using WinUIEx;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MusicGalleryWindow : Window
|
||||
{
|
||||
public MusicGalleryWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Title = App.ResourceLoader?.GetString("MusicGalleryPageTitle");
|
||||
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
|
||||
AppWindow.SetIcons();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,10 @@
|
||||
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch x:Name="AutoStartupToggleSwitch" Loaded="AutoStartupToggleSwitch_Loaded" />
|
||||
<ToggleSwitch
|
||||
x:Name="AutoStartupToggleSwitch"
|
||||
Loaded="AutoStartupToggleSwitch_Loaded"
|
||||
Unloaded="AutoStartupToggleSwitch_Unloaded" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoStartWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
@@ -138,6 +141,14 @@
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IgnoreFullscreenWindow, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageHideWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.HideWindowWhenNotPlaying, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageGlobalDrag" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDragEverywhereEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- Desktop mode -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppDesktop" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
@@ -187,6 +198,37 @@
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppDock" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDockMonitor" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RefreshMonitorDeviceNamesCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.MonitorDeviceNames, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedDockMonitorDeviceName, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDockWindowHeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.DockWindowHeight, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" px" />
|
||||
<Slider
|
||||
Maximum="200"
|
||||
Minimum="64"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.DockWindowHeight, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
@@ -250,6 +292,24 @@
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageBackgroundAcrylicEffectAmount" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind ViewModel.CoverAcrylicEffectAmount, Mode=OneWay}" />
|
||||
<Slider
|
||||
Maximum="10"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.CoverAcrylicEffectAmount, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
@@ -306,18 +366,12 @@
|
||||
CanDragItems="True"
|
||||
CanReorderItems="True"
|
||||
DragItemsCompleted="AlbumArtSearchProvidersListView_DragItemsCompleted"
|
||||
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||
ItemsSource="{x:Bind ViewModel.AlbumArtSearchProvidersInfo, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</ListView.OpacityTransition>
|
||||
<ListView.ItemContainerStyle>
|
||||
<Style TargetType="ListViewItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:AlbumArtSearchProviderInfo">
|
||||
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
@@ -364,7 +418,7 @@
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind ViewModel.LocalLyricsFolders, Mode=OneWay}">
|
||||
ItemsSource="{x:Bind ViewModel.LocalMediaFolders, Mode=OneWay}">
|
||||
<controls:SettingsExpander.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:SettingsCard>
|
||||
@@ -399,13 +453,13 @@
|
||||
<interactivity:Interaction.Behaviors>
|
||||
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
Binding="{x:Bind ViewModel.LocalMediaFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.LocalLyricsFolders.Count, Mode=OneWay}"
|
||||
Binding="{x:Bind ViewModel.LocalMediaFolders.Count, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="0">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
@@ -484,6 +538,16 @@
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontFamily" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.SystemFontNames, Mode=OneWay}" SelectedIndex="{x:Bind ViewModel.SelectedFontFamilyIndex, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontWeight" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsFontWeight, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsThin" />
|
||||
@@ -639,22 +703,60 @@
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</ColorPicker>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsStandardFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=LyricsFontSizeSlider, Path=Value, Mode=OneWay}" />
|
||||
Text="{Binding ElementName=LyricsStandardFontSizeSlider, Path=Value, Mode=OneWay}" />
|
||||
<Slider
|
||||
x:Name="LyricsFontSizeSlider"
|
||||
x:Name="LyricsStandardFontSizeSlider"
|
||||
Maximum="96"
|
||||
Minimum="12"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsFontSize, Mode=TwoWay}" />
|
||||
Value="{x:Bind ViewModel.LyricsStandardFontSize, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsDockFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=LyricsDockFontSizeSlider, Path=Value, Mode=OneWay}" />
|
||||
<Slider
|
||||
x:Name="LyricsDockFontSizeSlider"
|
||||
Maximum="96"
|
||||
Minimum="12"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsDockFontSize, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsDesktopFontSize" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=LyricsDesktopFontSizeSlider, Path=Value, Mode=OneWay}" />
|
||||
<Slider
|
||||
x:Name="LyricsDesktopFontSizeSlider"
|
||||
Maximum="96"
|
||||
Minimum="12"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="2"
|
||||
TickFrequency="2"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsDesktopFontSize, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -845,9 +947,10 @@
|
||||
x:Uid="SettingsPageTranslationConfig"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsExpanded="True">
|
||||
IsExpanded="{x:Bind ViewModel.IsLibreTranslateEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsLibreTranslateEnabled, Mode=TwoWay}" />
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLibreTranslateServer">
|
||||
<controls:SettingsCard x:Uid="SettingsPageLibreTranslateServer" IsEnabled="{x:Bind ViewModel.IsLibreTranslateEnabled, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBox
|
||||
x:Name="LibreTranslateServerTextBox"
|
||||
@@ -885,7 +988,6 @@
|
||||
<Paragraph>
|
||||
<Run x:Uid="SettingsPageVersion" />
|
||||
<Run Text="{x:Bind ViewModel.Version, Mode=OneWay}" />
|
||||
<Run Text="{x:Bind ViewModel.BuildDate, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</controls:SettingsCard.Description>
|
||||
@@ -912,6 +1014,10 @@
|
||||
<Button x:Uid="SettingsPageJoinNowButton" Click="DiscodGroupButton_Click" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageTelegram" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Telegram.png}">
|
||||
<Button x:Uid="SettingsPageJoinNowButton" Click="TelegramGroupButton_Click" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
@@ -26,9 +25,9 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch)
|
||||
{
|
||||
if (toggleSwitch.DataContext is LocalLyricsFolder localLyricsFolder)
|
||||
if (toggleSwitch.DataContext is LocalMediaFolder localLyricsFolder)
|
||||
{
|
||||
ViewModel.ToggleLocalLyricsFolder(localLyricsFolder);
|
||||
ViewModel.ToggleLocalLyricsFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +46,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
if (toggleSwitch.DataContext is LyricsSearchProviderInfo providerInfo)
|
||||
{
|
||||
ViewModel.ToggleLyricsSearchProvider(providerInfo);
|
||||
ViewModel.ToggleLyricsSearchProvider();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +64,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
Microsoft.UI.Xaml.RoutedEventArgs e
|
||||
)
|
||||
{
|
||||
ViewModel.RemoveFolderAsync((LocalLyricsFolder)(sender as HyperlinkButton)!.Tag);
|
||||
ViewModel.RemoveFolderAsync((LocalMediaFolder)(sender as HyperlinkButton)!.Tag);
|
||||
}
|
||||
|
||||
private void MediaSourceProviderToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
@@ -131,5 +130,15 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
Launcher.LaunchUriAsync(new Uri(MetadataHelper.DiscordUrl));
|
||||
}
|
||||
|
||||
private void TelegramGroupButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Launcher.LaunchUriAsync(new Uri(MetadataHelper.TelegramUrl));
|
||||
}
|
||||
|
||||
private void AutoStartupToggleSwitch_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AutoStartupToggleSwitch.Toggled -= AutoStartupToggleSwitch_Toggled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid>
|
||||
<local:SettingsPage />
|
||||
<Grid x:Name="RootGrid">
|
||||
<Frame x:Name="RootFrame" />
|
||||
</Grid>
|
||||
|
||||
</Window>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using H.NotifyIcon;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using WinUIEx;
|
||||
|
||||
@@ -8,26 +9,16 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class SettingsWindow : Window
|
||||
{
|
||||
public SettingsWindowViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService<SettingsWindowViewModel>();
|
||||
|
||||
public SettingsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Title = App.ResourceLoader?.GetString("SettingsPageTitle");
|
||||
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
|
||||
AppWindow.SetIcons();
|
||||
|
||||
Title = App.ResourceLoader!.GetString("SettingsPageTitle");
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
AppWindow.Closing += AppWindow_Closing;
|
||||
}
|
||||
|
||||
public SettingsWindowViewModel ViewModel { get; set; } =
|
||||
Ioc.Default.GetRequiredService<SettingsWindowViewModel>();
|
||||
|
||||
private void AppWindow_Closing(
|
||||
Microsoft.UI.Windowing.AppWindow sender,
|
||||
Microsoft.UI.Windowing.AppWindowClosingEventArgs args
|
||||
)
|
||||
{
|
||||
args.Cancel = true; // Prevent the window from closing
|
||||
this.Hide(true);
|
||||
RootFrame.Navigate(typeof(SettingsPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
FAQ/FAQ.md
45
FAQ/FAQ.md
@@ -1,45 +0,0 @@
|
||||
### I couldn't see any button that I can interact with
|
||||
|
||||
This app is built with immersive experience, just hover your mouse on the top/bottom area of the app and then you'll see everything.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### I have set up all the settings related to translation but there is no translation at all
|
||||
|
||||

|
||||
|
||||
Please make sure that you have already enable "Translation" function (at the bottom-right of the lyrics page, click to toggle).
|
||||
|
||||
### How can I lock the window when switching to desktop mode
|
||||
|
||||

|
||||
|
||||
Again, hover you mouse on the top, click on the lock icon and you're good to go! Or, alternatively, press `Ctrl + Alt + U`.
|
||||
|
||||
### How can I unlock the window in desktop mode
|
||||
|
||||

|
||||
|
||||
It's in the system tray, right-click on the icon and you'll see "Unlock the window". Or, alternatively, press `Ctrl + Alt + U`.
|
||||
|
||||
### There's a delay in lyrics timeline
|
||||
|
||||
Hover you mouse on the very bottom of the app,
|
||||
|
||||

|
||||
|
||||
And then click on the first icon button (Lyrics timeline offset), here you can adjust the offset freely.
|
||||
|
||||
### I'm using Apple Music, the lyrics is moving forward and afterward constantly
|
||||
|
||||

|
||||
|
||||
Hover your mouse at the very bottom of the app and then click on the very last icon button to go to settings page.
|
||||
|
||||

|
||||
|
||||
Go to "Advanced options" section, increase the threshold value (marked with the bigger red rectangle) until the lyrics is working properly.
|
||||
|
||||
---
|
||||
BIN
FAQ/image-10.png
Normal file
BIN
FAQ/image-10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
BIN
FAQ/image-11.png
Normal file
BIN
FAQ/image-11.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 221 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user