Compare commits

..

59 Commits

Author SHA1 Message Date
Zhe Fang
22b813e687 chores: bump to v1.1.186.0 2025-12-13 17:02:55 -05:00
Zhe Fang
fda94d5020 fix: settings save issue 2025-12-13 16:54:08 -05:00
Zhe Fang
205cbe8fb6 fix: settings storage issue 2025-12-13 16:19:05 -05:00
Zhe Fang
816f7064db fix: fan style lyrics animation 2025-12-13 15:45:41 -05:00
Zhe Fang
132c5267b0 chores: bump to v1.1.184.0 2025-12-13 12:20:20 -05:00
Zhe Fang
4e866818df fix: font family issue 2025-12-13 12:11:50 -05:00
Zhe Fang
9b7b56a0ee Revise acknowledgments in README.CN.md
Updated the acknowledgments section to reflect ongoing support and gratitude towards contributors and users.
2025-12-13 09:46:47 -05:00
Zhe Fang
66f2da0e4c Revise acknowledgments and donation list in README
Updated the acknowledgments section to reflect user support and removed the manual donation list.
2025-12-13 09:46:04 -05:00
Zhe Fang
1735c6a7e6 Add sponsors list and gratitude section to SPONSORS.md 2025-12-13 09:44:10 -05:00
Zhe Fang
8c06c98068 Update README.CN.md to acknowledge supporters
Added a section to thank supporters and users.
2025-12-13 09:36:00 -05:00
Zhe Fang
e2ac4c166c Add acknowledgments for supporters in README
Added a section to acknowledge and thank supporters.
2025-12-13 09:34:22 -05:00
Zhe Fang
728397cafa chores: code cleanup 2025-12-13 08:39:48 -05:00
Zhe Fang
059787a28f chores: bump to v1.1.183.0 2025-12-13 08:16:23 -05:00
Zhe Fang
4c4231b48c fix: white line in fullscreen mode 2025-12-13 07:58:28 -05:00
Zhe Fang
2412927b29 Add Zread badge to README.CN.md 2025-12-12 16:53:45 -05:00
Zhe Fang
f3bdbba83e Add Zread badge to README 2025-12-12 16:53:30 -05:00
Zhe Fang
4c811b12ca Create initial wiki structure for BetterLyrics
Added comprehensive documentation for BetterLyrics, including sections on installation, configuration, architecture, user interface, media integration, and development.
2025-12-12 16:20:42 -05:00
Zhe Fang
933103c57f Update badge links in README.CN.md 2025-12-12 15:55:15 -05:00
Zhe Fang
718e7bdad3 Fix badge formatting in README.md 2025-12-12 15:52:48 -05:00
Zhe Fang
42284b4f45 add: CoverBackgroundRenderer 2025-12-11 15:20:51 -05:00
Zhe Fang
7da8af7c2a fix: lyrics window manager tab not showing 2025-12-11 08:24:51 -05:00
Zhe Fang
a4fc457065 chores: bump to v1.1.181.0 2025-12-11 07:44:20 -05:00
Zhe Fang
d3c2ee592c fix: selectorbar section disappear 2025-12-11 07:19:05 -05:00
Zhe Fang
2eef88523c Modify taskbar.json for layout and style changes
Updated taskbar settings including bounds, opacity, and effects.
2025-12-10 16:17:25 -05:00
Zhe Fang
220b1063ac fix: taskbar mode is not working in release mode 2025-12-10 16:15:58 -05:00
Zhe Fang
28bcd8ddfc chores: remove taskbar mode entry 2025-12-10 14:09:16 -05:00
Zhe Fang
5750bd3ad7 add: more info in thanks control 2025-12-10 11:54:03 -05:00
Zhe Fang
1667c701b0 feat: taskbar mode 2025-12-10 11:09:53 -05:00
Zhe Fang
160398f7ab fix: add null check for Window.AppWindow 2025-12-10 10:51:45 -05:00
Zhe Fang
c25ddf770f fix: when open app with window locked, then unlock the window, title bar and bottom bar will not be interactive 2025-12-10 10:42:06 -05:00
Zhe Fang
44fa3312b2 fix: docked mode ux exp 2025-12-10 07:09:08 -05:00
Zhe Fang
81f3d1f6bf fix: cannot exit app when docked mode is on 2025-12-10 06:54:21 -05:00
Zhe Fang
2f627d7531 feat: add credit list in settings 2025-12-09 19:09:39 -05:00
Zhe Fang
caed76e9b9 fix: chinese to tc or sc 2025-12-09 13:16:00 -05:00
Zhe Fang
a74dc705c9 feat: search for file name in music gallery 2025-12-09 13:02:42 -05:00
Zhe Fang
21eb8fcf9c fix: add apply traditional or simplified chinese condition 2025-12-09 12:22:37 -05:00
Zhe Fang
4662d3d54d fix: auto show and hide lyrics window issue 2025-12-09 11:03:32 -05:00
Zhe Fang
b10f108f93 chores: hide lock button when in fullscreen mode 2025-12-09 10:33:03 -05:00
Zhe Fang
2defe620c7 fix: NowPlayingWIndow titlebar and lock/unlock button 2025-12-09 10:22:34 -05:00
Zhe Fang
315c10c83c fix: translation and roman 2025-12-09 08:23:11 -05:00
Zhe Fang
981bc3f933 fix: fullscreen maximize button error 2025-12-08 23:02:46 -05:00
Zhe Fang
88460899bd fix: listen on lyrics color change 2025-12-08 22:05:20 -05:00
Zhe Fang
8341642658 feat: lyrics opacity settings 2025-12-08 21:40:50 -05:00
Zhe Fang
26f4ff3a58 chores: set host lyrics matching threshold to 40% 2025-12-08 20:30:05 -05:00
Zhe Fang
b1b763c6fe fix: ui color is not updated for the first time when in docked mode or desktop mode 2025-12-08 20:27:37 -05:00
Zhe Fang
28323c39f6 chores: code cleanup 2025-12-08 19:57:49 -05:00
Zhe Fang
b099965715 fix: music gallery init 2025-12-08 19:57:26 -05:00
Zhe Fang
8f9fdc18bb fix: album art sizing issue 2025-12-08 19:37:28 -05:00
Zhe Fang
5a549bd5f5 fix: auto asearch is not using cache for some cases 2025-12-08 18:48:29 -05:00
Zhe Fang
48e94f275b fix: translation 2025-12-08 18:08:16 -05:00
Zhe Fang
691071d725 chores: code cleanup 2025-12-08 16:37:01 -05:00
Zhe Fang
591c1a6d00 fix: lock button status 2025-12-08 15:22:13 -05:00
Zhe Fang
4e6a2df2cb fix 2025-12-08 15:07:12 -05:00
Zhe Fang
8b196e45a4 fix 2025-12-07 22:41:37 -05:00
Zhe Fang
0ddeaef126 fix: auto play only be valid when first opening gallery window 2025-12-07 17:26:41 -05:00
Zhe Fang
e3747c113a feat: multi window 2025-12-07 17:06:50 -05:00
Zhe Fang
1b1449ce3b fix: add scroll bar for playback sources settings 2025-12-05 11:59:55 -05:00
Zhe Fang
ea246a96be fix: album art only paddings 2025-12-05 11:43:39 -05:00
Zhe Fang
28722d325a feat: multi lyrics window 2025-12-04 22:07:02 -05:00
115 changed files with 5192 additions and 2748 deletions

291
.devin/wiki.json Normal file
View File

@@ -0,0 +1,291 @@
{
"repo_notes": [
{
"content": "Always use the latest files in the repo to generate the wiki"
}
],
"pages": [
{
"title": "Overview",
"purpose": "Introduce BetterLyrics, its purpose as a WinUI3 lyrics display application, key features, and supported music players",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Getting Started",
"purpose": "Guide users through installation, initial setup, and basic usage of BetterLyrics",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Installation and Deployment",
"purpose": "Explain how to install BetterLyrics from Microsoft Store or build from source, system requirements, and supported Windows versions",
"parent": "Getting Started",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Initial Configuration",
"purpose": "Walk through first-time setup including media player configuration, folder selection, and basic settings",
"parent": "Getting Started",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Architecture",
"purpose": "Provide technical overview of BetterLyrics' internal architecture, design patterns, and component organization",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Application Entry Point and Dependency Injection",
"purpose": "Document the App.xaml.cs entry point, service registration, and dependency injection container configuration",
"parent": "Architecture",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Service Layer Architecture",
"purpose": "Explain the service-oriented architecture, service interfaces, and their implementations including MediaSessionsService, LyricsSearchService, and SettingsService",
"parent": "Architecture",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Data Models",
"purpose": "Document core data models including LyricsLine, LyricsData, SongInfo, and configuration models",
"parent": "Architecture",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Multi-Window System",
"purpose": "Explain how BetterLyrics manages multiple simultaneous lyrics windows, window lifecycle, and state management",
"parent": "Architecture",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "User Interface",
"purpose": "Document the UI components, windows, and user interaction patterns in BetterLyrics",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Now Playing Window",
"purpose": "Detail the main lyrics display window, its components, and integration with the rendering system",
"parent": "User Interface",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Display Modes and Window Configurations",
"purpose": "Explain different display modes (Standard, Desktop, Docked, Fullscreen, Narrow, Taskbar) and how to configure them",
"parent": "User Interface",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Settings and Customization",
"purpose": "Document the settings interface, configuration options, and how to customize lyrics appearance and behavior",
"parent": "User Interface",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Music Gallery",
"purpose": "Explain the local music library management feature, including playback, playlist management, and integration with media controls",
"parent": "User Interface",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "System Tray and Global Controls",
"purpose": "Document the system tray integration, global hotkeys, and application-wide controls",
"parent": "User Interface",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Lyrics System",
"purpose": "Comprehensive documentation of the lyrics acquisition, processing, and display pipeline",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Lyrics Search and Providers",
"purpose": "Document the lyrics search system, supported providers (QQ Music, Netease, Kugou, LrcLib, Apple Music, local files), and search strategies",
"parent": "Lyrics System",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Parsing and Translation",
"purpose": "Explain how lyrics are parsed from different formats (LRC, QRC, TTML), translation system using LibreTranslate, and metadata matching",
"parent": "Lyrics System",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Rendering Pipeline",
"purpose": "Document the Win2D-based rendering system, LyricsCanvas, PlayingLineRenderer, UnplayingLineRenderer, and LyricsLayoutManager",
"parent": "Lyrics System",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Visual Effects and Animation",
"purpose": "Explain character-level effects (glow, float, scale), background effects (fluid, snow, fog, spectrum), and the animation transition system",
"parent": "Lyrics System",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Media Integration",
"purpose": "Document how BetterLyrics integrates with music players and manages media sessions",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Windows Media Transport Controls Integration",
"purpose": "Explain how BetterLyrics uses Windows SMTC to work universally with media players",
"parent": "Media Integration",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Supported Players and Special Configurations",
"purpose": "List supported media players, document special configurations (Apple Music token, LX Music SSE), and player-specific handling",
"parent": "Media Integration",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Album Art and Theme Colors",
"purpose": "Document album art retrieval from multiple sources, color palette generation (MedianCut, OctTree), and adaptive theming",
"parent": "Media Integration",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Internationalization",
"purpose": "Explain the localization system, supported languages, and how resources are managed",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Development",
"purpose": "Technical documentation for developers contributing to or extending BetterLyrics",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Build Configuration and Deployment",
"purpose": "Document the build process, publish profiles for different architectures, CI/CD pipeline, and packaging for Microsoft Store",
"parent": "Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Helper Utilities and Extensions",
"purpose": "Document utility classes including TaskbarHook, WindowHook, ColorHelper, ImageHelper, and various extension methods",
"parent": "Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "External Dependencies and Libraries",
"purpose": "List and explain third-party dependencies including Win2D, NAudio, ATL.NET, FlaUI, and their usage in the application",
"parent": "Development",
"page_notes": [
{
"content": ""
}
]
}
]
}

View File

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

View File

@@ -50,6 +50,7 @@
<converter:CornerRadiusToDoubleConverter x:Key="CornerRadiusToDoubleConverter" />
<converter:LyricsSearchProviderToDisplayNameConverter x:Key="LyricsSearchProviderToDisplayNameConverter" />
<converter:TranslationSearchProviderToDisplayNameConverter x:Key="TranslationSearchProviderToDisplayNameConverter" />
<converter:TransliterationSearchProviderToDisplayNameConverter x:Key="TransliterationSearchProviderToDisplayNameConverter" />
<converter:AlbumArtSearchProviderToDisplayNameConverter x:Key="AlbumArtSearchProviderToDisplayNameConverter" />
<converter:SecondsToFormattedTimeConverter x:Key="SecondsToFormattedTimeConverter" />
<converter:MillisecondsToFormattedTimeConverter x:Key="MillisecondsToFormattedTimeConverter" />
@@ -95,8 +96,7 @@
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="16,9,16,11" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="14,6,14,9" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style x:Key="GhostButtonStyle" TargetType="Button">
@@ -110,10 +110,10 @@
x:Key="TitleBarToggleButtonStyle"
BasedOn="{StaticResource ToggleButtonRevealStyle}"
TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="16,9,16,11" />
<Setter Property="Padding" Value="14,6,14,9" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">

View File

@@ -6,7 +6,6 @@ using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.DiscordService;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.ResourceService;
@@ -21,11 +20,11 @@ using Microsoft.UI.Xaml;
using Microsoft.Windows.ApplicationModel.Resources;
using Serilog;
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Vanara.PInvoke;
using WinUIEx;
namespace BetterLyrics.WinUI3
{
@@ -69,12 +68,26 @@ namespace BetterLyrics.WinUI3
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
// 设置托盘
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
WindowHook.HideWindow<SystemTrayWindow>();
var settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
if (Ioc.Default.GetRequiredService<ISettingsService>().AppSettings.MusicGallerySettings.AutoOpen)
WindowHook.OpenOrShowWindow<SystemTrayWindow>();
if (settingsService.AppSettings.GeneralSettings.AutoStartLyricsWindow)
{
var defaultStatus = settingsService.AppSettings.WindowBoundsRecords.Where(x => x.IsDefault);
if (defaultStatus != null)
{
foreach (var item in defaultStatus)
{
WindowHook.OpenOrShowWindow<NowPlayingWindow>(item);
if (!settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
{
break;
}
}
}
}
if (settingsService.AppSettings.MusicGallerySettings.AutoOpen)
{
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
@@ -96,7 +109,6 @@ namespace BetterLyrics.WinUI3
loggingBuilder.AddSerilog();
})
// Services
.AddSingleton<ILiveStatesService, LiveStatesService>()
.AddSingleton<ISettingsService, SettingsService>()
.AddSingleton<IMediaSessionsService, MediaSessionsService>()
.AddSingleton<IAlbumArtSearchService, AlbumArtSearchService>()
@@ -114,15 +126,17 @@ namespace BetterLyrics.WinUI3
.AddSingleton<LyricsWindowSettingsControlViewModel>()
.AddSingleton<LyricsWindowSwitchControlViewModel>()
.AddSingleton<LyricsWindowSwitchWindowViewModel>()
.AddSingleton<NowPlayingWindowViewModel>()
.AddSingleton<NowPlayingPageViewModel>()
.AddSingleton<SettingsWindowViewModel>()
.AddSingleton<SystemTrayViewModel>()
.AddSingleton<SettingsPageViewModel>()
.AddSingleton<MusicGalleryViewModel>()
.AddSingleton<MusicGalleryPageViewModel>()
.AddSingleton<AboutControlViewModel>()
.AddSingleton<MusicGalleryWindowViewModel>()
.AddTransient<NowPlayingWindowViewModel>()
.AddTransient<NowPlayingPageViewModel>()
.AddTransient<NowPlayingBarViewModel>()
.BuildServiceProvider()
);
}

View File

@@ -34,6 +34,7 @@
<None Remove="Controls\LyricsWindowSettingsControl.xaml" />
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
<None Remove="Controls\MediaSettingsControl.xaml" />
<None Remove="Controls\NowPlayingBar.xaml" />
<None Remove="Controls\PlaybackSettingsControl.xaml" />
<None Remove="Controls\PropertyRow.xaml" />
<None Remove="Controls\ShortcutTextBox.xaml" />
@@ -68,14 +69,16 @@
<PackageReference Include="ComputeSharp.D2D1.WinUI" Version="3.2.0" />
<PackageReference Include="csharp-kana" Version="1.0.2" />
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
<PackageReference Include="DevWinUI.Controls" Version="9.7.0" />
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
<PackageReference Include="DevWinUI.Controls" Version="9.7.1" />
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.6" />
<PackageReference Include="F23.StringSimilarity" Version="7.0.1" />
<PackageReference Include="FlaUI.UIA3" Version="5.0.0" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.4.1" />
<PackageReference Include="Hqub.Last.fm" Version="2.5.1" />
<PackageReference Include="Interop.UIAutomationClient" Version="10.19041.0" />
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.1" />
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
@@ -85,8 +88,8 @@
<PackageReference Include="NTextCat" Version="0.3.65" />
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="System.Drawing.Common" Version="10.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.0" />
<PackageReference Include="System.Drawing.Common" Version="10.0.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.1" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.2.1" />
@@ -114,6 +117,8 @@
</ItemGroup>
<!--Disable Trimming for Specific Packages-->
<ItemGroup>
<TrimmerRootAssembly Include="FlaUI.UIA3" />
<TrimmerRootAssembly Include="Interop.UIAutomationClient" />
<TrimmerRootAssembly Include="NAudio.Wasapi" />
<TrimmerRootAssembly Include="TagLibSharp" />
<TrimmerRootAssembly Include="Vanara.PInvoke.DwmApi" />
@@ -337,6 +342,11 @@
<ItemGroup>
<Folder Include="TemplateSelector\" />
</ItemGroup>
<ItemGroup>
<Page Update="Controls\NowPlayingBar.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Views\SystemTrayWindow.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -3,6 +3,8 @@
public static class App
{
public const string AppAuthor = "Zhe Fang";
public const string AppAuthorNicknameEN = "jayfunc";
public const string AppAuthorNicknameZH = "摘叶飞镖";
public const string AppName = "BetterLyrics";
public const string AutoStartupTaskId = "AutoStartup";

View File

@@ -4,15 +4,23 @@
{
public const string MicrosoftStore = "https://apps.microsoft.com/detail/9p1wcd1p597r";
public const string GitHub = "https://github.com/jayfunc/BetterLyrics";
public const string ShareHub = $"{GitHub}/blob/dev/ShareHub/index.md";
public const string TermsOfService = $"{GitHub}/blob/dev/TermsofService.md";
public const string PrivacyPolicy = $"{GitHub}/blob/dev/PrivacyPolicy.md";
public const string UserGuide = $"{GitHub}/wiki/User-Guide";
public const string AuthorGitHub = "https://github.com/jayfunc";
public const string BetterLyricsGitHub = $"{AuthorGitHub}/BetterLyrics";
public const string ShareHub = $"{BetterLyricsGitHub}/blob/dev/ShareHub/index.md";
public const string TermsOfService = $"{BetterLyricsGitHub}/blob/dev/TermsofService.md";
public const string PrivacyPolicy = $"{BetterLyricsGitHub}/blob/dev/PrivacyPolicy.md";
public const string UserGuide = $"{BetterLyricsGitHub}/wiki/User-Guide";
public const string AppleMusicCfg = $"{UserGuide}#lyrics-source-configuration";
public const string QQGroup = "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 Discord = "https://discord.gg/5yAQPnyCKv";
public const string Telegram = "https://t.me/+svhSLZ7awPsxNGY1";
public const string BuyMeACoffee = "https://buymeacoffee.com/founchoo";
public const string PayPal = "https://paypal.me/zhefangpay";
public const string Afdian = "https://afdian.com/a/jayfunc";
}
}

View File

@@ -28,30 +28,37 @@
</StackPanel>
</dev:SettingsExpander.Header>
<dev:SettingsExpander.Description>
<StackPanel
Margin="0,2,0,0"
Orientation="Horizontal"
Spacing="2">
<TextBlock Text="©" />
<HyperlinkButton
Margin="0,-1,0,0"
Content="Zhe Fang"
NavigateUri="https://github.com/jayfunc" />
<TextBlock Text="2025" />
<StackPanel Orientation="Horizontal">
<StackPanel
Margin="0,2,0,0"
Orientation="Horizontal"
Spacing="2">
<TextBlock Text="©" />
<HyperlinkButton
Margin="0,-1,0,0"
Content="{x:Bind const:App.AppAuthor}"
NavigateUri="https://github.com/jayfunc" />
<TextBlock Text="2025" />
</StackPanel>
</StackPanel>
</dev:SettingsExpander.Description>
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}">
<Paragraph>
<Run x:Uid="SettingsPageVersion" />
<Run Text="{x:Bind helper:MetadataHelper.AppVersion}" />
</Paragraph>
</RichTextBlock>
<StackPanel Orientation="Horizontal" Spacing="6">
<RichTextBlock
Margin="0,-1,0,0"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}">
<Paragraph>
<Run x:Uid="SettingsPageVersion" />
<Run Text="{x:Bind helper:MetadataHelper.AppVersion}" />
</Paragraph>
</RichTextBlock>
</StackPanel>
<dev:SettingsExpander.Items>
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
<StackPanel Spacing="6">
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHub}" />
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.BetterLyricsGitHub}" />
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
@@ -75,14 +82,14 @@
<StackPanel Spacing="6">
<TextBlock x:Uid="SetingsPageDonation" />
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="https://buymeacoffee.com/founchoo" />
<HyperlinkButton Content="PayPal" NavigateUri="https://paypal.me/zhefangpay" />
<Button
Content="支付宝"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<Flyout>
<HyperlinkButton Content="Buy Me a Coffee" NavigateUri="{x:Bind const:Link.BuyMeACoffee}" />
<HyperlinkButton Content="PayPal" NavigateUri="{x:Bind const:Link.PayPal}" />
<HyperlinkButton
x:Name="AlipayButton"
Click="AlipayButton_Click"
Content="支付宝">
<HyperlinkButton.ContextFlyout>
<Flyout x:Name="AlipayFlyout">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="CornerRadius" Value="12" />
@@ -91,14 +98,14 @@
</Flyout.FlyoutPresenterStyle>
<Image Height="300" Source="/Assets/Alipay.jpg" />
</Flyout>
</Button.Flyout>
</Button>
<Button
Content="微信"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<Flyout>
</HyperlinkButton.ContextFlyout>
</HyperlinkButton>
<HyperlinkButton
x:Name="WeChatButton"
Click="WeChat_Click"
Content="微信">
<HyperlinkButton.ContextFlyout>
<Flyout x:Name="WeChatFlyout">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="CornerRadius" Value="12" />
@@ -107,8 +114,9 @@
</Flyout.FlyoutPresenterStyle>
<Image Height="300" Source="/Assets/WeChatReward.png" />
</Flyout>
</Button.Flyout>
</Button>
</HyperlinkButton.ContextFlyout>
</HyperlinkButton>
<HyperlinkButton Content="爱发电" NavigateUri="{x:Bind const:Link.Afdian}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="*" />
@@ -120,16 +128,13 @@
</StackPanel>
</dev:SettingsCard>
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
<StackPanel Spacing="6">
<TextBlock x:Uid="SetingsPageContributors" />
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
<HyperlinkButton Content="jayfunc" NavigateUri="https://github.com/jayfunc" />
<HyperlinkButton Content="Raspberry-Monster" NavigateUri="https://github.com/Raspberry-Monster" />
<HyperlinkButton Content="ZHider" NavigateUri="https://github.com/ZHider" />
<HyperlinkButton Content="kusutori" NavigateUri="https://github.com/kusutori" />
</StackPanel>
</StackPanel>
<dev:SettingsCard x:Uid="SettingsPageThanksList">
<Button
Click="Patron_Click"
Content="{ui:FontIcon FontSize=16,
FontFamily={StaticResource IconFontFamily},
Glyph=&#xE7FD;}"
Style="{StaticResource AccentButtonStyle}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
@@ -177,10 +182,6 @@
</dev:SettingsExpander.ItemsHeader>
</dev:SettingsExpander>
<dev:SettingsCard x:Uid="SettingsPageDebugOverlay">
<ToggleSwitch IsOn="{x:Bind ViewModel.IsDebugOverlayEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageFixedTimeStep" Visibility="Collapsed">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.AdvancedSettings.IsFixedTimeStep, Mode=TwoWay}" />
</dev:SettingsCard>
@@ -198,5 +199,190 @@
</Grid>
</ScrollViewer>
<Grid
x:Name="CreditsReel"
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
Opacity="0"
SizeChanged="CreditsReel_SizeChanged"
Tapped="CreditsReel_Tapped"
Visibility="Collapsed">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<ScrollViewer
x:Name="CreditsReelScrollViewer"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollMode="Disabled">
<RichTextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalTextAlignment="Center"
LineHeight="28"
PointerEntered="RichTextBlock_PointerEntered"
PointerExited="RichTextBlock_PointerExited">
<Paragraph x:Name="CreditsReelHeader" />
<!-- 贡献者 -->
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run x:Uid="SetingsPageContributors" />
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/jayfunc">
<Run Text="jayfunc" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/Raspberry-Monster">
<Run Text="Raspberry-Monster" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/ZHider">
<Run Text="ZHider" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/kusutori">
<Run Text="kusutori" />
</Hyperlink>
</Paragraph>
<!-- 赞助 -->
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run x:Uid="SettingsPagePatrons" />
</Paragraph>
<Paragraph>
<Run Text="YE" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Dec 3, 2025" />
</Paragraph>
<Paragraph>
<Run Text="**玄" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 23, 2025" />
</Paragraph>
<Paragraph>
<Run Text="**智" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 21, 2025" />
</Paragraph>
<Paragraph>
<Run Text="*鹤" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 17, 2025" />
</Paragraph>
<Paragraph>
<Run Text="借过" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Nov 2, 2025" />
</Paragraph>
<Paragraph>
<Run Text="**华" />
<Run Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="Aug 28, 2025" />
</Paragraph>
<Paragraph>
<Run x:Uid="SettingsPageUserWhoPurchased" />
</Paragraph>
<!-- 特别鸣谢 -->
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run x:Uid="SetingsPageSpecialThanks" />
</Paragraph>
<Paragraph>
<Run x:Uid="SettingsPageYouNowUsing" />
</Paragraph>
<!-- 代码参考 -->
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run x:Uid="SetingsPageDeps" />
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce">
<Run Text="Get album artwork from ITunes (with Python3 or C#)" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://stackoverflow.com/a/32013610/11048731">
<Run Text="FullyObservableCollection" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/Impressionist">
<Run Text="Impressionist" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/Storyteller-Studios/ColorThief.WinUI3">
<Run Text="ColorThief.WinUI3" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/Johnwikix/SpectrumVisualization">
<Run Text="SpectrumVisualization" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://www.shadertoy.com/view/Mdt3Df">
<Run Text="Snow (as shown in sweden)" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://www.shadertoy.com/view/lllSR2">
<Run Text="w10" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/mo-jinran/Taskbar-Lyrics">
<Run Text="Taskbar-Lyrics" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/jayfunc/BetterLyrics/network/dependencies">
<Run Text="..." />
</Hyperlink>
</Paragraph>
<!-- UI/UX 设计参考 -->
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run x:Uid="SetingsPageUIUXRef" />
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/solstice23/refined-now-playing-netease">
<Run Text="refined-now-playing-netease" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/WXRIW/Lyricify-App">
<Run Text="Lyricify" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://moriafly.com/program/salt-player">
<Run Text="椒盐音乐 Salt Player" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="https://github.com/TwilightLemon/MyToolBar">
<Run Text="MyToolBar" />
</Hyperlink>
</Paragraph>
<Paragraph>
<Hyperlink NavigateUri="">
<Run Text="" />
</Hyperlink>
</Paragraph>
<Paragraph Margin="0,20,0,0" FontWeight="Bold">
<Run Text="{x:Bind const:App.AppName}" />
</Paragraph>
<Paragraph>
<Run Text="Proudly built by" />
<Hyperlink NavigateUri="{x:Bind const:Link.AuthorGitHub}">
<Run Text="{x:Bind const:App.AppAuthor}" />
</Hyperlink>
</Paragraph>
<Paragraph x:Name="CreditsReelFooter" />
</RichTextBlock>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -1,6 +1,8 @@
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System.Threading.Tasks;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -9,6 +11,7 @@ namespace BetterLyrics.WinUI3.Controls
{
public sealed partial class AboutControl : UserControl
{
private bool _isCreditsScrolling = false;
public AboutControlViewModel ViewModel => (AboutControlViewModel)DataContext;
public AboutControl()
@@ -16,5 +19,56 @@ namespace BetterLyrics.WinUI3.Controls
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<AboutControlViewModel>();
}
private async void Patron_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
CompositionTarget.Rendering += CompositionTarget_Rendering;
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Visible;
CreditsReel.Opacity = 1;
_isCreditsScrolling = true;
}
private void CompositionTarget_Rendering(object? sender, object e)
{
if (_isCreditsScrolling)
{
CreditsReelScrollViewer.ChangeView(null, CreditsReelScrollViewer.VerticalOffset + 0.5, null);
}
}
private async void CreditsReel_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
{
CreditsReel.Opacity = 0;
await Task.Delay(Constants.Time.AnimationDuration);
CreditsReel.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
CompositionTarget.Rendering -= CompositionTarget_Rendering;
CreditsReelScrollViewer.ChangeView(null, 0, null);
}
private void CreditsReel_SizeChanged(object sender, Microsoft.UI.Xaml.SizeChangedEventArgs e)
{
CreditsReelHeader.LineHeight = e.NewSize.Height;
CreditsReelFooter.LineHeight = e.NewSize.Height / 2;
}
private void RichTextBlock_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
_isCreditsScrolling = false;
}
private void RichTextBlock_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
_isCreditsScrolling = true;
}
private void WeChat_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
WeChatFlyout.ShowAt(WeChatButton);
}
private void AlipayButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
AlipayFlyout.ShowAt(AlipayButton);
}
}
}

View File

@@ -1,18 +1,6 @@
using BetterLyrics.WinUI3.Models.Settings;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

View File

@@ -40,10 +40,9 @@
<ToggleSwitch IsOn="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard IsEnabled="{x:Bind AlbumArtLayoutSettings.IsAutoCoverImageHeight, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Maximum="512"
<NumberBox
Minimum="0"
Unit="px"
SpinButtonPlacementMode="Inline"
Value="{x:Bind AlbumArtLayoutSettings.CoverImageHeight, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>

View File

@@ -44,9 +44,9 @@
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- App behavior -->
<!-- Startup -->
<TextBlock x:Uid="SettingsPageAppBehavior" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<TextBlock x:Uid="SettingsPageStartup" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="SettingsPageAutoStart" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF71C;}">
<ToggleSwitch
@@ -59,42 +59,42 @@
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoOpen, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAutoOpenLyricsWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE90B;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.AutoStartLyricsWindow, Mode=TwoWay}" />
</dev:SettingsCard>
<!-- Music gallery window -->
<TextBlock x:Uid="SettingsPageMusicGallery" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="SettingsPageAutoPlayWhenOpenMusicGalleryWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE768;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.AutoPlay, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageExitOnGalleryWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE711;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.MusicGallerySettings.ExitOnWindowClosed, Mode=TwoWay}" />
</dev:SettingsCard>
<!-- Lyrics window -->
<TextBlock x:Uid="SettingsPageLyricsWindow" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="SettingsPageExitOnLyricsWindowClosed" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE711;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageListenNewSession" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xF270;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
</dev:SettingsCard>
<!-- Playback shortcut -->
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8D8;}">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
</dev:SettingsCard>
<TextBlock x:Uid="SettingsPageShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="SettingsPageShowHideHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageBorderlessHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.BorderlessShortcut, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageClickThroughHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.ClickThroughShortcut, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLyricsWindowSwitchHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut, Mode=TwoWay}" />
</dev:SettingsCard>
<!-- Playback shortcut -->
<TextBlock x:Uid="SettingsPagePlaybackShortcut" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="SettingsPagePlayOrPauseSongHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEDA7;}">
<local:ShortcutTextBox Shortcut="{x:Bind ViewModel.AppSettings.GeneralSettings.PlayOrPauseShortcut, Mode=TwoWay}" />
</dev:SettingsCard>

View File

@@ -4,12 +4,8 @@ using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -52,7 +48,7 @@ namespace BetterLyrics.WinUI3.Controls
// FontFamilies = fontFamilies;
// });
//});
FontFamilies = FontHelper.SystemFontFamilies.OrderBy(x => x).ToList();
FontFamilies = FontHelper.GetSystemFontFamilies();
}
private void AutoSuggestBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)

View File

@@ -1,19 +1,7 @@
using BetterLyrics.WinUI3.Enums;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

View File

@@ -8,7 +8,6 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Renderer;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
@@ -21,27 +20,23 @@ using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Windows.Foundation;
using Windows.UI;
namespace BetterLyrics.WinUI3.Controls
{
public sealed partial class LyricsCanvas : UserControl,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>,
IRecipient<PropertyChangedMessage<TimeSpan>>,
IRecipient<PropertyChangedMessage<LyricsData?>>,
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>,
IRecipient<PropertyChangedMessage<SongInfo?>>,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<double>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<TextAlignmentType>>,
IRecipient<PropertyChangedMessage<SongInfo?>>,
IRecipient<PropertyChangedMessage<LyricsFontWeight>>,
IRecipient<PropertyChangedMessage<string>>
{
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
private readonly ILastFMService _lastFMService = Ioc.Default.GetRequiredService<ILastFMService>();
@@ -108,6 +103,9 @@ namespace BetterLyrics.WinUI3.Controls
private double _renderLyricsHeight = 0;
private double _renderLyricsOpacity = 0;
private LyricsWindowStatus? _lyricsWindowStatus = null;
private AlbumArtThemeColors _albumArtThemeColors = new();
private Point _mousePosition = new(0, 0);
private int _mouseHoverLineIndex = -1;
private bool _isMouseInLyricsArea = false;
@@ -128,6 +126,24 @@ namespace BetterLyrics.WinUI3.Controls
public double ActualLyricsHeight => LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines);
public int CurrentHoveringLineIndex => _mouseHoverLineIndex;
public LyricsWindowStatus? LyricsWindowStatus
{
get { return (LyricsWindowStatus?)GetValue(LyricsWindowStatusProperty); }
set { SetValue(LyricsWindowStatusProperty, value); }
}
public static readonly DependencyProperty LyricsWindowStatusProperty =
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsCanvas), new PropertyMetadata(null, OnDependencyPropertyChanged));
public AlbumArtThemeColors AlbumArtThemeColors
{
get { return (AlbumArtThemeColors)GetValue(AlbumArtThemeColorsProperty); }
set { SetValue(AlbumArtThemeColorsProperty, value); }
}
public static readonly DependencyProperty AlbumArtThemeColorsProperty =
DependencyProperty.Register(nameof(AlbumArtThemeColors), typeof(AlbumArtThemeColors), typeof(LyricsCanvas), new PropertyMetadata(new AlbumArtThemeColors(), OnDependencyPropertyChanged));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> X <20><><EFBFBD><EFBFBD>
public double LyricsStartX
{
@@ -136,7 +152,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty LyricsStartXProperty =
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(LyricsStartX), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ Y <20><><EFBFBD><EFBFBD>
public double LyricsStartY
@@ -146,7 +162,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty LyricsStartYProperty =
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(LyricsStartY), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
public double LyricsWidth
@@ -156,7 +172,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty LyricsWidthProperty =
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(LyricsWidth), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>߶<EFBFBD>
public double LyricsHeight
@@ -166,7 +182,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty LyricsHeightProperty =
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(LyricsHeight), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͸<EFBFBD><CDB8><EFBFBD><EFBFBD>
public double LyricsOpacity
@@ -176,7 +192,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty LyricsOpacityProperty =
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(LyricsOpacity), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
/// <summary>
/// <20>û<EFBFBD><C3BB>ٿ<EFBFBD><D9BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ѹ<EFBFBD><D1B9><EFBFBD><EFBFBD>ľ<EFBFBD><C4BE><EFBFBD><EBA3A8> 0 <20><>ʼ<EFBFBD>
@@ -188,7 +204,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty MouseScrollOffsetProperty =
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnLayoutPropChanged));
DependencyProperty.Register(nameof(MouseScrollOffset), typeof(double), typeof(LyricsCanvas), new PropertyMetadata(0.0, OnDependencyPropertyChanged));
/// <summary>
/// <20>û<EFBFBD><C3BB><EFBFBD><EFBFBD>굱ǰ<EAB5B1><C7B0>λ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻǣ<CFBD>
@@ -200,7 +216,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty MousePositionProperty =
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnLayoutPropChanged));
DependencyProperty.Register(nameof(MousePosition), typeof(Point), typeof(LyricsCanvas), new PropertyMetadata(new Point(0, 0), OnDependencyPropertyChanged));
public bool IsMouseInLyricsArea
{
@@ -209,7 +225,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty IsMouseInLyricsAreaProperty =
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
DependencyProperty.Register(nameof(IsMouseInLyricsArea), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
public bool IsMousePressing
{
@@ -218,7 +234,7 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty IsMousePressingProperty =
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
DependencyProperty.Register(nameof(IsMousePressing), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
public bool IsMouseScrolling
{
@@ -227,30 +243,27 @@ namespace BetterLyrics.WinUI3.Controls
}
public static readonly DependencyProperty IsMouseScrollingProperty =
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnLayoutPropChanged));
DependencyProperty.Register(nameof(IsMouseScrolling), typeof(bool), typeof(LyricsCanvas), new PropertyMetadata(false, OnDependencyPropertyChanged));
public LyricsCanvas()
{
InitializeComponent();
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<int>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<AlbumArtThemeColors>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TimeSpan>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsData?>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsWindowStatus>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<double>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<bool>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<TextAlignmentType>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<SongInfo?>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<LyricsFontWeight>>(this);
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<string>>(this);
WeakReferenceMessenger.Default.RegisterAll(this);
UpdateRenderLyricsLines();
}
private static void OnLayoutPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is LyricsCanvas canvas)
{
if (e.Property == LyricsStartXProperty)
if (e.Property == LyricsWindowStatusProperty)
{
canvas._lyricsWindowStatus = (LyricsWindowStatus)e.NewValue;
canvas._isLayoutChanged = true;
}
else if (e.Property == LyricsStartXProperty)
{
canvas._renderLyricsStartX = Convert.ToDouble(e.NewValue);
canvas._isLayoutChanged = true;
@@ -300,6 +313,18 @@ namespace BetterLyrics.WinUI3.Controls
}
canvas._isMouseScrolling = value;
}
else if (e.Property == AlbumArtThemeColorsProperty)
{
var albumArtThemeColors = (AlbumArtThemeColors)e.NewValue;
canvas._immersiveBgColorTransition.StartTransition(albumArtThemeColors.EnvColor);
canvas._accentColor1Transition.StartTransition(albumArtThemeColors.AccentColor1);
canvas._accentColor2Transition.StartTransition(albumArtThemeColors.AccentColor2);
canvas._accentColor3Transition.StartTransition(albumArtThemeColors.AccentColor3);
canvas._accentColor4Transition.StartTransition(albumArtThemeColors.AccentColor4);
canvas._albumArtThemeColors = albumArtThemeColors;
canvas._isLayoutChanged = true;
}
}
}
@@ -307,23 +332,22 @@ namespace BetterLyrics.WinUI3.Controls
private void Canvas_Draw(ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args)
{
if (_lyricsWindowStatus == null) return;
var bounds = new Rect(0, 0, sender.Size.Width, sender.Size.Height);
var status = _liveStatesService.LiveStates.LyricsWindowStatus;
var albumArtLayout = status.AlbumArtLayoutSettings;
var lyricsBg = status.LyricsBackgroundSettings;
var lyricsStyle = status.LyricsStyleSettings;
var lyricsEffect = status.LyricsEffectSettings;
var albumArtLayout = _lyricsWindowStatus.AlbumArtLayoutSettings;
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
double songDuration = _mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0;
bool isForceWordByWord = _settingsService.AppSettings.GeneralSettings.IsForceWordByWordEffect;
var lyricsThemeColors = _mediaSessionsService.AlbumArtThemeColors;
Color overlayColor;
double finalOpacity;
if (status.IsAdaptToEnvironment)
if (_lyricsWindowStatus.IsAdaptToEnvironment)
{
// <20><><EFBFBD><EFBFBD>Ӧɫ
overlayColor = _immersiveBgColorTransition.Value;
@@ -344,7 +368,7 @@ namespace BetterLyrics.WinUI3.Controls
lyricsBg.IsPureColorOverlayEnabled
);
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity;
_fluidRenderer.Opacity = lyricsBg.FluidOverlayOpacity / 100.0;
_fluidRenderer.IsEnabled = lyricsBg.IsFluidOverlayEnabled;
_fluidRenderer.Draw(sender, args.DrawingSession);
@@ -368,10 +392,10 @@ namespace BetterLyrics.WinUI3.Controls
userScrollOffset: _mouseYScrollTransition.Value,
lyricsOpacity: _renderLyricsOpacity,
playingLineTopOffsetFactor: lyricsStyle.PlayingLineTopOffset / 100.0,
windowStatus: status,
strokeColor: lyricsThemeColors.StrokeFontColor,
bgColor: lyricsThemeColors.BgFontColor,
fgColor: lyricsThemeColors.FgFontColor,
windowStatus: _lyricsWindowStatus,
strokeColor: _albumArtThemeColors.StrokeFontColor,
bgColor: _albumArtThemeColors.BgFontColor,
fgColor: _albumArtThemeColors.FgFontColor,
getPlaybackState: (lineIndex) =>
{
if (_renderLyricsLines == null) return new LinePlaybackState();
@@ -403,33 +427,34 @@ namespace BetterLyrics.WinUI3.Controls
style: lyricsBg.SpectrumStyle,
canvasWidth: sender.Size.Width,
canvasHeight: sender.Size.Height,
fillColor: lyricsThemeColors.BgFontColor
fillColor: _albumArtThemeColors.BgFontColor
);
}
#if DEBUG
args.DrawingSession.DrawText(
$"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
$"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
$"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
$"Playing line (idx): {_playingLineIndex}\n" +
$"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
$"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
$"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
$"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
$"Y offset: {_canvasYScrollTransition.Value}\n" +
$"User scroll offset: {_mouseYScrollTransition.Value}",
new Vector2(0, 0), Colors.Red);
//args.DrawingSession.DrawText(
// $"Lyrics render start pos: ({(int)_renderLyricsStartX}, {(int)_renderLyricsStartY})\n" +
// $"Lyrics render size: [{(int)_renderLyricsWidth} x {(int)_renderLyricsHeight}]\n" +
// $"Lyrics actual height: {LyricsLayoutManager.CalculateActualHeight(_renderLyricsLines)}\n" +
// $"Playing line (idx): {_playingLineIndex}\n" +
// $"Mouse hovering line (idx): {_mouseHoverLineIndex}\n" +
// $"Visible lines range (idx): [{_visibleRange.Start}, {_visibleRange.End}]\n" +
// $"Total line count: {LyricsLayoutManager.CalculateMaxRange(_renderLyricsLines).End + 1}\n" +
// $"Played: {_songPosition} / {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
// $"Y offset: {_canvasYScrollTransition.Value}\n" +
// $"User scroll offset: {_mouseYScrollTransition.Value}",
// new Vector2(0, 0), Colors.Red);
#endif
}
private void Canvas_Update(ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args)
{
var lyricsBg = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings;
var lyricsStyle = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings;
var lyricsEffect = _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings;
var albumArtThemeColors = _mediaSessionsService.AlbumArtThemeColors;
if (_lyricsWindowStatus == null) return;
var lyricsBg = _lyricsWindowStatus.LyricsBackgroundSettings;
var lyricsStyle = _lyricsWindowStatus.LyricsStyleSettings;
var lyricsEffect = _lyricsWindowStatus.LyricsEffectSettings;
var lyricsData = _mediaSessionsService.CurrentLyricsData;
TimeSpan elapsedTime = args.Timing.ElapsedTime;
@@ -500,10 +525,11 @@ namespace BetterLyrics.WinUI3.Controls
sender.Size.Height,
_canvasTargetScrollOffset,
lyricsStyle.PlayingLineTopOffset / 100.0,
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings,
_lyricsWindowStatus.LyricsStyleSettings,
_lyricsWindowStatus.LyricsEffectSettings,
_canvasYScrollTransition,
albumArtThemeColors.BgFontColor,
albumArtThemeColors.FgFontColor,
_albumArtThemeColors.BgFontColor,
_albumArtThemeColors.FgFontColor,
elapsedTime,
_isMouseScrolling,
_isLayoutChanged,
@@ -559,14 +585,17 @@ namespace BetterLyrics.WinUI3.Controls
private void Canvas_Unloaded(object sender, RoutedEventArgs e)
{
Canvas.RemoveFromVisualTree();
Canvas = null;
_fluidRenderer.Dispose();
_snowRenderer.Dispose();
_fogRenderer.Dispose();
_spectrumRenderer.Dispose();
_renderLyricsLines = null;
DisposeAnalyzer();
Canvas.RemoveFromVisualTree();
Canvas = null;
}
private async void Canvas_CreateResources(CanvasAnimatedControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
@@ -592,12 +621,12 @@ namespace BetterLyrics.WinUI3.Controls
private void TriggerRelayout()
{
if (_renderLyricsLines == null || !_isLayoutChanged) return;
if (_renderLyricsLines == null || !_isLayoutChanged || _lyricsWindowStatus == null) return;
LyricsLayoutManager.MeasureAndArrange(
resourceCreator: Canvas,
lines: _renderLyricsLines,
status: _liveStatesService.LiveStates.LyricsWindowStatus,
status: _lyricsWindowStatus,
appSettings: _settingsService.AppSettings,
canvasWidth: Canvas.Size.Width,
canvasHeight: Canvas.Size.Height,
@@ -639,22 +668,18 @@ namespace BetterLyrics.WinUI3.Controls
_isLastFMTracked = false;
}
public void Receive(PropertyChangedMessage<AlbumArtThemeColors> message)
private void UpdateRenderLyricsLines()
{
if (message.Sender is IMediaSessionsService)
_renderLyricsLines = null;
_renderLyricsLines = _mediaSessionsService.CurrentLyricsData?.LyricsLines.Select(x => new RenderLyricsLine()
{
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtThemeColors))
{
var lyricsThemeColors = message.NewValue;
_immersiveBgColorTransition.StartTransition(lyricsThemeColors.EnvColor);
_accentColor1Transition.StartTransition(lyricsThemeColors.AccentColor1);
_accentColor2Transition.StartTransition(lyricsThemeColors.AccentColor2);
_accentColor3Transition.StartTransition(lyricsThemeColors.AccentColor3);
_accentColor4Transition.StartTransition(lyricsThemeColors.AccentColor4);
_isLayoutChanged = true;
}
}
LyricsSyllables = x.LyricsSyllables,
StartMs = x.StartMs,
EndMs = x.EndMs,
PhoneticText = x.PhoneticText,
OriginalText = x.OriginalText,
TranslatedText = x.TranslatedText
}).ToList();
}
public void Receive(PropertyChangedMessage<TimeSpan> message)
@@ -696,38 +721,26 @@ namespace BetterLyrics.WinUI3.Controls
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentLyricsData))
{
_renderLyricsLines = null;
if (_mediaSessionsService.CurrentLyricsData is LyricsData lyricsData)
{
_renderLyricsLines = lyricsData.LyricsLines.Select(x => new RenderLyricsLine()
{
LyricsSyllables = x.LyricsSyllables,
StartMs = x.StartMs,
EndMs = x.EndMs,
PhoneticText = x.PhoneticText,
OriginalText = x.OriginalText,
TranslatedText = x.TranslatedText
}).ToList();
}
UpdateRenderLyricsLines();
_isLayoutChanged = true;
}
}
}
public void Receive(PropertyChangedMessage<LyricsWindowStatus> message)
public void Receive(PropertyChangedMessage<SongInfo?> message)
{
if (message.Sender is LiveStates)
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
{
_isLayoutChanged = true;
ResetPlaybackState();
}
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is LyricsStyleSettings)
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.PhoneticLyricsFontSize))
{
@@ -749,8 +762,20 @@ namespace BetterLyrics.WinUI3.Controls
{
_isLayoutChanged = true;
}
else if (message.PropertyName == nameof(LyricsStyleSettings.PhoneticLyricsOpacity))
{
_isLayoutChanged = true;
}
else if (message.PropertyName == nameof(LyricsStyleSettings.OriginalLyricsOpacity))
{
_isLayoutChanged = true;
}
else if (message.PropertyName == nameof(LyricsStyleSettings.TranslatedLyricsOpacity))
{
_isLayoutChanged = true;
}
}
else if (message.Sender is LyricsEffectSettings)
else if (message.Sender == LyricsWindowStatus?.LyricsEffectSettings)
{
if (message.PropertyName == nameof(LyricsEffectSettings.LyricsScrollDuration))
{
@@ -781,7 +806,7 @@ namespace BetterLyrics.WinUI3.Controls
public void Receive(PropertyChangedMessage<double> message)
{
if (message.Sender is LyricsStyleSettings)
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsLineSpacingFactor))
{
@@ -792,7 +817,7 @@ namespace BetterLyrics.WinUI3.Controls
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is LyricsEffectSettings)
if (message.Sender == LyricsWindowStatus?.LyricsEffectSettings)
{
if (message.PropertyName == nameof(LyricsEffectSettings.IsFanLyricsEnabled))
{
@@ -803,7 +828,7 @@ namespace BetterLyrics.WinUI3.Controls
_isLayoutChanged = true;
}
}
else if (message.Sender is LyricsStyleSettings)
else if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.IsDynamicLyricsFontSize))
{
@@ -814,7 +839,7 @@ namespace BetterLyrics.WinUI3.Controls
public void Receive(PropertyChangedMessage<TextAlignmentType> message)
{
if (message.Sender is LyricsStyleSettings)
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsAlignmentType))
{
@@ -823,20 +848,9 @@ namespace BetterLyrics.WinUI3.Controls
}
}
public void Receive(PropertyChangedMessage<SongInfo?> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
{
ResetPlaybackState();
}
}
}
public void Receive(PropertyChangedMessage<LyricsFontWeight> message)
{
if (message.Sender is LyricsStyleSettings)
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFontWeight))
{
@@ -847,7 +861,7 @@ namespace BetterLyrics.WinUI3.Controls
public void Receive(PropertyChangedMessage<string> message)
{
if (message.Sender is LyricsStyleSettings)
if (message.Sender == LyricsWindowStatus?.LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCJKFontFamily))
{
@@ -859,5 +873,6 @@ namespace BetterLyrics.WinUI3.Controls
}
}
}
}
}

View File

@@ -29,19 +29,77 @@
</dev:SettingsCard>
<!-- 辉光效果 -->
<dev:SettingsCard x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE9A9;}">
<dev:SettingsExpander x:Uid="SettingsPageLyricsGlowEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE9A9;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageScope">
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageEffectScopeLongDurationSyllable" />
<ComboBoxItem x:Uid="SettingsPageEffectLineStartToCurrentChar" />
</ComboBox>
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageLongSyllableDuration">
<local:ExtendedSlider
Default="700"
Maximum="1000"
Minimum="0"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectLongSyllableDuration, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectAmountAutoAdjust, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsGlowEffectAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Maximum="16"
Minimum="0"
Value="{x:Bind LyricsEffectSettings.LyricsGlowEffectAmount, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 缩放效果 -->
<dev:SettingsCard x:Uid="SettingsPageLyricsScaleEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8A3;}">
<dev:SettingsExpander x:Uid="SettingsPageLyricsScaleEffect" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8A3;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageLongSyllableDuration">
<local:ExtendedSlider
Default="700"
Maximum="1000"
Minimum="0"
Unit="ms"
Value="{x:Bind LyricsEffectSettings.LyricsScaleEffectLongSyllableDuration, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectAmountAutoAdjust, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsScaleEffectAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Default="115"
Maximum="200"
Minimum="100"
Unit="%"
Value="{x:Bind LyricsEffectSettings.LyricsScaleEffectAmount, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 浮动动画 -->
<dev:SettingsCard x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8C5;}">
<dev:SettingsExpander x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8C5;}">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageAutoAdjust">
<ToggleSwitch IsOn="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationAmountAutoAdjust, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageAmount" IsEnabled="{x:Bind LyricsEffectSettings.IsLyricsFloatAnimationAmountAutoAdjust, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}">
<local:ExtendedSlider
Default="4"
Maximum="16"
Minimum="0"
Value="{x:Bind LyricsEffectSettings.LyricsFloatAnimationAmount, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 扇形歌词 -->
<dev:SettingsExpander

View File

@@ -105,14 +105,6 @@
<Run Text="*" />
<Run x:Uid="ArtistsSplitHint" />
</Paragraph>
</RichTextBlock>
<RichTextBlock
FontSize="12"
FontWeight="Bold"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Loaded="ArtistsSplitHintRichTextBlock_Loaded"
TextWrapping="Wrap">
<Paragraph>
<Run Text="; , / " />
</Paragraph>
@@ -168,6 +160,10 @@
Style="{StaticResource AccentButtonStyle}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="LyricsSearchControlIgnoreCache">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IgnoreCacheWhenSearching, Mode=TwoWay}" />
</dev:SettingsCard>
</StackPanel>
</ScrollViewer>
</Grid>

View File

@@ -2,8 +2,6 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;
using Microsoft.UI.Xaml.Media;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -25,25 +23,5 @@ namespace BetterLyrics.WinUI3.Controls
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
}
private void ArtistsSplitHintRichTextBlock_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
if (sender is RichTextBlock richTextBlock)
{
TextHighlighter highlighter = new()
{
Background = App.Current.Resources["AccentTextFillColorPrimaryBrush"] as SolidColorBrush,
Ranges =
{
new() { StartIndex = 0, Length = 1 },
new() { StartIndex = 5, Length = 1 },
new() { StartIndex = 10, Length = 1 },
new() { StartIndex = 15, Length = 1 },
new() { StartIndex = 20, Length = 1 },
new() { StartIndex = 25, Length = 1 },
}
};
richTextBlock.TextHighlighters.Add(highlighter);
}
}
}
}

View File

@@ -227,6 +227,36 @@
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
<!-- 字体不透明度 -->
<dev:SettingsExpander x:Uid="SettingsPageLyricsOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEF20;}">
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPagePhoneticText">
<local:ExtendedSlider
Default="60"
Frequency="1"
Maximum="100"
Minimum="0"
Value="{x:Bind LyricsStyleSettings.PhoneticLyricsOpacity, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageOriginalText">
<local:ExtendedSlider
Default="30"
Frequency="1"
Maximum="100"
Minimum="0"
Value="{x:Bind LyricsStyleSettings.OriginalLyricsOpacity, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageTranslatedText">
<local:ExtendedSlider
Default="60"
Frequency="1"
Maximum="100"
Minimum="0"
Value="{x:Bind LyricsStyleSettings.TranslatedLyricsOpacity, Mode=TwoWay}" />
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -21,24 +21,7 @@
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="SettingsPageRecordedWindowStatus"
Grid.Column="0"
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<Button
Grid.Column="2"
Margin="0,30,0,0"
Command="{x:Bind ViewModel.OpenConfigPanelCommand}"
Style="{StaticResource AccentButtonStyle}">
<TextBlock x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig" />
</Button>
</Grid>
<TextBlock x:Uid="SettingsPageRecordedWindowStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<StackPanel Orientation="Horizontal" Spacing="3">
@@ -54,6 +37,7 @@
<MenuFlyoutItem x:Uid="SettingsPageDockedMode" Command="{x:Bind ViewModel.CreateDockedLyricsWindowStatusCommand}" />
<MenuFlyoutItem x:Uid="SettingsPageFullscreenMode" Command="{x:Bind ViewModel.CreateFullLyricsWindowStatusCommand}" />
<MenuFlyoutItem x:Uid="SettingsPageNarrowMode" Command="{x:Bind ViewModel.CreateNarrowLyricsWindowStatusCommand}" />
<MenuFlyoutItem x:Uid="SettingsPageTaskbarMode" Command="{x:Bind ViewModel.CreateTaskbarLyricsWindowStatusCommand}" />
</MenuFlyout>
</Button.Flyout>
</Button>
@@ -87,7 +71,7 @@
Padding="0,12"
CornerRadius="4"
ItemsSource="{x:Bind ViewModel.AppSettings.WindowBoundsRecords, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=TwoWay}">
SelectionMode="None">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanel HorizontalSpacing="0" VerticalSpacing="0" />
@@ -101,10 +85,6 @@
Spacing="6">
<StackPanel.ContextFlyout>
<MenuBarItemFlyout>
<MenuFlyoutItem
x:Uid="LyricsWindowSettingsControlSetDefault"
Click="SetDefaultMenuFlyoutItem_Click"
IsEnabled="{Binding IsDefault, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
<MenuFlyoutItem x:Uid="SettingsPageCreateFromCurrent" Click="CopyMenuFlyoutItem_Click" />
<MenuFlyoutItem x:Uid="LyricsWindowSettingsControlShare" Click="ShareMenuFlyoutItem_Click" />
<MenuFlyoutItem
@@ -113,13 +93,61 @@
IsEnabled="{Binding IsDefault, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" />
</MenuBarItemFlyout>
</StackPanel.ContextFlyout>
<uc:DemoWindowGrid LyricsWindowStatus="{Binding}" />
<Grid>
<Border
BorderBrush="{ThemeResource AccentAAFillColorDefaultBrush}"
BorderThickness="4"
CornerRadius="4"
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<uc:DemoWindowGrid
Margin="4"
LyricsWindowStatus="{Binding}"
Tapped="DemoWindowGrid_Tapped" />
</Grid>
<Grid>
<ToggleButton
Grid.Column="0"
HorizontalAlignment="Stretch"
Click="SetDefaultMenuFlyoutItem_Click"
IsChecked="{Binding IsDefault, Mode=OneWay}">
<TextBlock x:Uid="LyricsWindowSettingsControlSetDefault" />
</ToggleButton>
</Grid>
<Grid ColumnSpacing="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="0"
HorizontalAlignment="Stretch"
Click="ConfigButton_Click">
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
</Button>
<Button
Grid.Column="1"
HorizontalAlignment="Stretch"
Click="CloseStatusButton_Click"
IsEnabled="{Binding IsOpened, Mode=OneWay}">
<TextBlock x:Uid="SettingsPageCloseStatus" />
</Button>
</Grid>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<dev:SettingsCard x:Uid="SettingsPageMultiNowPlayingWindows">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.MultiNowPlayingWindowMode, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageMusicGalleryLyrics">
<Button HorizontalAlignment="Stretch" Click="EmbeddedConfigButton_Click">
<TextBlock x:Uid="LyricsWindowSettingsControlLyricsWindowConfig" />
</Button>
</dev:SettingsCard>
</StackPanel>
</Grid>
@@ -137,8 +165,15 @@
<Grid.TranslationTransition>
<Vector3Transition />
</Grid.TranslationTransition>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Padding="36,0" Style="{StaticResource SettingsGridStyle}">
<Grid
Grid.Row="0"
Padding="36,0"
Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<Grid>
<Grid.ColumnDefinitions>
@@ -147,9 +182,9 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
x:Uid="LyricsWindowSettingsControlCurrentLyricsWindowConfig"
Grid.Column="0"
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
Style="{StaticResource SettingsSectionHeaderTextBlockStyle}"
Text="{x:Bind LyricsWindowStatus.Name, Mode=OneWay}" />
<Button
Grid.Column="2"
Margin="0,30,0,0"
@@ -160,77 +195,39 @@
Style="{StaticResource AccentButtonStyle}" />
</Grid>
<Pivot SelectionChanged="Pivot_SelectionChanged">
<controls:Segmented
x:Name="ConfigSegmented"
SelectionChanged="ConfigSegmented_SelectionChanged"
Style="{StaticResource PivotSegmentedStyle}">
<PivotItem Tag="Window">
<PivotItem.Header>
<TextBlock
x:Uid="AppSettingsControlGeneral"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<controls:SegmentedItem x:Name="WindowSegmentedItem" Tag="Window">
<TextBlock x:Uid="AppSettingsControlGeneral" />
</controls:SegmentedItem>
<controls:SegmentedItem x:Name="LayoutSegmentedItem" Tag="Layout">
<TextBlock x:Uid="SettingsPageLayout" />
</controls:SegmentedItem>
<controls:SegmentedItem x:Name="AlbumArtStyleSegmentedItem" Tag="AlbumArtStyle">
<TextBlock x:Uid="SettingsPageAlbumStyle" />
</controls:SegmentedItem>
<controls:SegmentedItem Tag="AlbumArtEffect">
<TextBlock x:Uid="SettingsPageAlbumEffect" />
</controls:SegmentedItem>
<controls:SegmentedItem Tag="LyricsStyle">
<TextBlock x:Uid="SettingsPageLyricsStyle" />
</controls:SegmentedItem>
<controls:SegmentedItem Tag="LyricsEffect">
<TextBlock x:Uid="SettingsPageLyricsEffect" />
</controls:SegmentedItem>
<controls:SegmentedItem Tag="LyricsBackground">
<TextBlock x:Uid="SettingsPageBackgroundOverlay" />
</controls:SegmentedItem>
<PivotItem Tag="Layout">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageLayout"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<PivotItem Tag="AlbumArtStyle">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageAlbumStyle"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<PivotItem Tag="AlbumArtEffect">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageAlbumEffect"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<PivotItem Tag="LyricsStyle">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageLyricsStyle"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<PivotItem Tag="LyricsEffect">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageLyricsEffect"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
<PivotItem Tag="LyricsBackground">
<PivotItem.Header>
<TextBlock
x:Uid="SettingsPageBackgroundOverlay"
VerticalAlignment="Center"
Style="{StaticResource BodyTextBlockStyle}" />
</PivotItem.Header>
</PivotItem>
</Pivot>
</controls:Segmented>
</StackPanel>
</Grid>
<controls:SwitchPresenter Margin="0,110,0,0" Value="{x:Bind ViewModel.ListViewSelectedItemTag, Mode=OneWay}">
<controls:SwitchPresenter Grid.Row="1" Value="{x:Bind ViewModel.SelectorBarSelectedItemTag, Mode=OneWay}">
<controls:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
@@ -239,7 +236,7 @@
<!-- Window -->
<controls:Case Value="Window">
<uc:WindowSettingsControl LyricsWindowStatus="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=OneWay}" />
<uc:WindowSettingsControl LyricsWindowStatus="{x:Bind LyricsWindowStatus, Mode=OneWay}" />
</controls:Case>
<!-- Layout -->
@@ -251,14 +248,14 @@
<TextBlock x:Uid="SettingsPageLayout" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsExpander x:Uid="SettingsPageDisplayTypeSwitcher" IsExpanded="True">
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.LyricsDisplayType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="MainPageAlbumArtOnly" />
<ComboBoxItem x:Uid="MainPageLyriscOnly" />
<ComboBoxItem x:Uid="MainPageSplitView" />
</ComboBox>
<dev:SettingsExpander.Items>
<dev:SettingsCard x:Uid="SettingsPageLayoutOrientation">
<ComboBox SelectedIndex="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsLayoutOrientation, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.LyricsLayoutOrientation, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationHorizontal" />
<ComboBoxItem x:Uid="SettingsPageLayoutOrientationVertical" />
</ComboBox>
@@ -273,27 +270,27 @@
<!-- Album art area style -->
<controls:Case Value="AlbumArtStyle">
<uc:AlbumArtAreaStyleSettingsControl AlbumArtLayoutSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
<uc:AlbumArtAreaStyleSettingsControl AlbumArtLayoutSettings="{x:Bind LyricsWindowStatus.AlbumArtLayoutSettings, Mode=OneWay}" />
</controls:Case>
<!-- Album art area effect -->
<controls:Case Value="AlbumArtEffect">
<uc:AlbumArtAreaEffectSettingsControl AlbumArtAreaEffectSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.AlbumArtAreaEffectSettings, Mode=OneWay}" />
<uc:AlbumArtAreaEffectSettingsControl AlbumArtAreaEffectSettings="{x:Bind LyricsWindowStatus.AlbumArtAreaEffectSettings, Mode=OneWay}" />
</controls:Case>
<!-- Lyrics style -->
<controls:Case Value="LyricsStyle">
<uc:LyricsStyleSettingsControl LyricsStyleSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsStyleSettings, Mode=OneWay}" />
<uc:LyricsStyleSettingsControl LyricsStyleSettings="{x:Bind LyricsWindowStatus.LyricsStyleSettings, Mode=OneWay}" />
</controls:Case>
<!-- Lyrics effect -->
<controls:Case Value="LyricsEffect">
<uc:LyricsEffectSettingsControl LyricsEffectSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsEffectSettings, Mode=OneWay}" />
<uc:LyricsEffectSettingsControl LyricsEffectSettings="{x:Bind LyricsWindowStatus.LyricsEffectSettings, Mode=OneWay}" />
</controls:Case>
<!-- Lyrics background -->
<controls:Case Value="LyricsBackground">
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind ViewModel.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
<uc:LyricsBackgroundSettingsControl LyricsBackgroundSettings="{x:Bind LyricsWindowStatus.LyricsBackgroundSettings, Mode=OneWay}" />
</controls:Case>
</controls:SwitchPresenter>

View File

@@ -1,15 +1,15 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Serialization;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using NTextCat.Commons;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -25,7 +25,15 @@ namespace BetterLyrics.WinUI3.Controls
public LyricsWindowSettingsControlViewModel ViewModel => (LyricsWindowSettingsControlViewModel)DataContext;
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
public LyricsWindowStatus LyricsWindowStatus
{
get { return (LyricsWindowStatus)GetValue(LyricsWindowStatusProperty); }
set { SetValue(LyricsWindowStatusProperty, value); }
}
public static readonly DependencyProperty LyricsWindowStatusProperty =
DependencyProperty.Register(nameof(LyricsWindowStatus), typeof(LyricsWindowStatus), typeof(LyricsWindowSettingsControl), new PropertyMetadata(default));
public LyricsWindowSettingsControl()
{
@@ -39,10 +47,9 @@ namespace BetterLyrics.WinUI3.Controls
{
if (menuFlyoutItem.DataContext is LyricsWindowStatus data)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus == data)
{
_liveStatesService.LiveStates.LyricsWindowStatus = ViewModel.AppSettings.WindowBoundsRecords.First();
}
var windows = WindowHook.GetWindows<NowPlayingWindow>();
var window = windows.FirstOrDefault(x => x.LyricsWindowStatus == data);
window?.CloseWindow();
ViewModel.AppSettings.WindowBoundsRecords.Remove(data);
}
}
@@ -50,12 +57,11 @@ namespace BetterLyrics.WinUI3.Controls
private void SetDefaultMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem menuFlyoutItem)
if (sender is FrameworkElement element)
{
if (menuFlyoutItem.DataContext is LyricsWindowStatus data)
if (element.DataContext is LyricsWindowStatus data)
{
ViewModel.AppSettings.WindowBoundsRecords.ForEach(x => x.IsDefault = false);
data.IsDefault = true;
data.IsDefault = !data.IsDefault;
}
}
}
@@ -115,17 +121,6 @@ namespace BetterLyrics.WinUI3.Controls
}
}
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is Pivot pivot)
{
if (pivot.SelectedItem is PivotItem pivotItem)
{
ViewModel?.ListViewSelectedItemTag = pivotItem.Tag;
}
}
}
private async void ImportButton_Click(object sender, RoutedEventArgs e)
{
string[] fileTypeFilter = [".json"];
@@ -154,5 +149,69 @@ namespace BetterLyrics.WinUI3.Controls
{
ViewModel.DisplayPanelHeight = e.NewSize.Height;
}
private void ConfigButton_Click(object sender, RoutedEventArgs e)
{
WindowSegmentedItem.IsEnabled = LayoutSegmentedItem.IsEnabled = true;
ConfigSegmented.SelectedItem = WindowSegmentedItem;
LyricsWindowStatus = (LyricsWindowStatus)((Button)sender).DataContext;
ViewModel.OpenConfigPanel();
}
private void EmbeddedConfigButton_Click(object sender, RoutedEventArgs e)
{
WindowSegmentedItem.IsEnabled = LayoutSegmentedItem.IsEnabled = false;
ConfigSegmented.SelectedItem = AlbumArtStyleSegmentedItem;
LyricsWindowStatus = _settingsService.AppSettings.MusicGallerySettings.LyricsWindowStatus;
ViewModel.OpenConfigPanel();
}
private void DemoWindowGrid_Tapped(object sender, TappedRoutedEventArgs e)
{
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
// <20>࿪ģʽ
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
{
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
}
// <20><><EFBFBD><EFBFBD>ģʽ
else
{
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
{
item.CloseWindow();
}
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
}
}
private void ConfigSelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
{
if (sender is SelectorBar bar)
{
if (bar.SelectedItem is SelectorBarItem item)
{
ViewModel?.SelectorBarSelectedItemTag = item.Tag;
}
}
}
private void CloseStatusButton_Click(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement element)
{
if (element.DataContext is LyricsWindowStatus data)
{
var window = WindowHook.GetWindows<NowPlayingWindow>().FirstOrDefault(x => x.LyricsWindowStatus == data);
window?.CloseWindow();
}
}
}
private void ConfigSegmented_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ViewModel.SelectorBarSelectedItemTag = (string)((SegmentedItem)((Segmented)sender).SelectedItem).Tag;
}
}
}

View File

@@ -44,8 +44,9 @@
<ListView
Margin="48,56"
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
ItemsSource="{x:Bind ViewModel.AppSettings.WindowBoundsRecords, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.LiveStates.LyricsWindowStatus, Mode=TwoWay}">
SelectionMode="None">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanel HorizontalSpacing="0" VerticalSpacing="0" />
@@ -54,12 +55,14 @@
<ListView.ItemTemplate>
<DataTemplate>
<Grid
Margin="0,10"
Padding="5"
AllowFocusOnInteraction="True"
Margin="16"
CornerRadius="4"
Tapped="Grid_Tapped">
<uc:DemoWindowGrid LyricsWindowStatus="{Binding}" />
<Border
BorderBrush="{ThemeResource AccentAAFillColorDefaultBrush}"
BorderThickness="4"
Visibility="{Binding IsOpened, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<uc:DemoWindowGrid Margin="4" LyricsWindowStatus="{Binding}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>

View File

@@ -1,10 +1,13 @@
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using System.Linq;
using System.Threading.Tasks;
// To learn more about WinUI, the WinUI project structure,
@@ -16,6 +19,8 @@ namespace BetterLyrics.WinUI3.Controls
{
public LyricsWindowSwitchControlViewModel ViewModel => (LyricsWindowSwitchControlViewModel)DataContext;
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
public LyricsWindowSwitchControl()
{
InitializeComponent();
@@ -24,6 +29,22 @@ namespace BetterLyrics.WinUI3.Controls
private async void Grid_Tapped(object sender, TappedRoutedEventArgs e)
{
var status = (LyricsWindowStatus)(((FrameworkElement)sender).DataContext);
// <20>࿪ģʽ
if (_settingsService.AppSettings.GeneralSettings.MultiNowPlayingWindowMode)
{
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
}
// <20><><EFBFBD><EFBFBD>ģʽ
else
{
var openedWindows = WindowHook.GetWindows<NowPlayingWindow>();
foreach (var item in openedWindows.Where(x => x.LyricsWindowStatus != status))
{
item.CloseWindow();
}
WindowHook.OpenOrShowWindow<NowPlayingWindow>(status);
}
await HideAsync();
}
@@ -37,7 +58,7 @@ namespace BetterLyrics.WinUI3.Controls
var lyricsWindowSwitchWindow = WindowHook.GetWindow<LyricsWindowSwitchWindow>();
lyricsWindowSwitchWindow?.ViewModel.RootGridOpacity = 0;
await Task.Delay(300);
WindowHook.HideWindow<LyricsWindowSwitchWindow>();
lyricsWindowSwitchWindow?.HideWindow();
}
private void ShadowRect_Loaded(object sender, RoutedEventArgs e)

View File

@@ -0,0 +1,433 @@
<?xml version="1.0" encoding="utf-8" ?>
<UserControl
x:Class="BetterLyrics.WinUI3.Controls.NowPlayingBar"
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:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:BetterLyrics.WinUI3.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Grid x:Name="RootGrid">
<Grid
x:Name="BottomCommandGrid"
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
PointerEntered="BottomCommandGrid_PointerEntered"
PointerExited="BottomCommandGrid_PointerExited">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<Grid x:Name="BottomCommandContent">
<Grid HorizontalAlignment="Left">
<StackPanel
x:Name="BottomLeftCommandStackPanel"
Orientation="Horizontal"
Spacing="3">
<StackPanel
x:Name="SongInfoStackPanel"
Margin="8"
Padding="8"
VerticalAlignment="Center"
CornerRadius="4"
Opacity="{x:Bind ShowSongInfo, Mode=OneWay, Converter={StaticResource BoolToOpacityConverter}}"
Orientation="Horizontal"
Spacing="12"
Tapped="SongInfoStackPanel_Tapped"
Visibility="{x:Bind ShowSongInfo, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited">
<interactivity:ChangePropertyAction PropertyName="Background" Value="Transparent" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerPressed">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerReleased">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<Grid VerticalAlignment="Center" CornerRadius="4">
<local:ImageSwitcher
x:Name="AlbumArtImageSwitcher"
Width="36"
Height="36" />
</Grid>
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="TitleTextBlock" />
<TextBlock
x:Name="ArtistsTextBlock"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
</StackPanel>
<StackPanel
x:Name="TimeStackPanel"
Margin="8"
Padding="8"
VerticalAlignment="Center"
Opacity="{x:Bind ShowTime, Mode=OneWay, Converter={StaticResource BoolToOpacityConverter}}"
Orientation="Horizontal"
Spacing="2"
Tapped="TimeStackPanel_Tapped"
Visibility="{x:Bind ShowTime, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited">
<interactivity:ChangePropertyAction PropertyName="Background" Value="Transparent" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerPressed">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorTertiaryBrush}" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerReleased">
<interactivity:ChangePropertyAction PropertyName="Background" Value="{ThemeResource SubtleFillColorSecondaryBrush}" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<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>
</StackPanel>
</Grid>
<Grid HorizontalAlignment="Center">
<StackPanel
x:Name="BottomCenterCommandStackPanel"
Padding="16"
Orientation="Horizontal"
Spacing="3">
<Button
Command="{x:Bind ViewModel.PreviousSongCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE622;}"
Style="{StaticResource GhostButtonStyle}" />
<Button
Command="{x:Bind ViewModel.PauseSongCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF8AE;}"
Style="{StaticResource GhostButtonStyle}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, 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=&#xF5B0;}"
Style="{StaticResource GhostButtonStyle}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, 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=&#xE623;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</Grid>
<Grid HorizontalAlignment="Right">
<StackPanel
x:Name="BottomRightCommandStackPanel"
Padding="16"
Orientation="Horizontal"
Spacing="3">
<!-- Volume -->
<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
<Grid>
<!-- Volumn: 0 -->
<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="&#xE74F;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</FontIcon>
<!-- Volumn: 1-32 -->
<FontIcon
x:Name="VolumeLevel1"
FontFamily="{StaticResource IconFontFamily}"
Glyph="&#xE993;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="GreaterThanOrEqual"
Value="1">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="LessThan"
Value="1">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</FontIcon>
<!-- Volumn: 33-65 -->
<FontIcon
x:Name="VolumeLevel2"
FontFamily="{StaticResource IconFontFamily}"
Glyph="&#xE994;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="GreaterThanOrEqual"
Value="33">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="LessThan"
Value="33">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</FontIcon>
<!-- Volumn: 66-100 -->
<FontIcon
x:Name="VolumeLevel3"
FontFamily="{StaticResource IconFontFamily}"
Glyph="&#xE995;">
<FontIcon.OpacityTransition>
<ScalarTransition />
</FontIcon.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="GreaterThanOrEqual"
Value="66">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.Volume, Mode=OneWay}"
ComparisonCondition="LessThan"
Value="66">
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</FontIcon>
</Grid>
<Button.ContextFlyout>
<Flyout x:Name="VolumeFlyout" ShouldConstrainToRootBounds="False">
<local:ExtendedSlider
Frequency="10"
IsSliderEnabled="False"
Maximum="100"
Minimum="0"
ResetButtonVisibility="Collapsed"
Unit="%"
ValueChangedByUser="ExtendedSlider_ValueChangedByUser"
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
</Flyout>
</Button.ContextFlyout>
</Button>
<!-- More -->
<Button Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE712;}" Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<MenuFlyout>
<!-- Lyrics search window -->
<MenuFlyoutItem
x:Uid="LyricsPageLyricsSearch"
Command="{x:Bind ViewModel.OpenLyricsSearchWindowCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE721;}" />
<!-- Lyrics window manager shortcut settings -->
<MenuFlyoutItem
x:Uid="LyricsPageLyricsSettings"
Click="LyricsSettingsShortcutMenuFlyoutItem_Click"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE61F;}">
<MenuFlyoutItem.ContextFlyout>
<Flyout
x:Name="LyricsSettingsFlyout"
Closed="LyricsSettingsFlyout_Closed"
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False" />
</MenuFlyoutItem.ContextFlyout>
</MenuFlyoutItem>
<!-- Playback shortcut settings -->
<MenuFlyoutItem
x:Uid="LyricsPagePlaybackSource"
Click="PlaybackSettingsShortcutMenuFlyoutItem_Click"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEA69;}">
<MenuFlyoutItem.ContextFlyout>
<Flyout
x:Name="PlaybackSettingsFlyout"
Closed="PlaybackSettingsFlyout_Closed"
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False" />
</MenuFlyoutItem.ContextFlyout>
</MenuFlyoutItem>
<!-- Settings -->
<MenuFlyoutItem
x:Uid="LyricsPageSettings"
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE713;}" />
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
<Slider
x:Name="TimelineSlider"
Margin="0,-12,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Maximum="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DurationMs, Mode=OneWay, Converter={StaticResource MillisecondsToSecondsConverter}}"
Minimum="0"
Style="{StaticResource GhostSliderStyle}"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}" />
<Grid
x:Name="TimelineSliderLyricsLineInfo"
Margin="0,-32,0,0"
Padding="8,4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}"
CornerRadius="6"
Opacity="{x:Bind ViewModel.TimelineSliderThumbOpacity, Mode=OneWay}">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<Grid.TranslationTransition>
<Vector3Transition />
</Grid.TranslationTransition>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock
Margin="0,0,0,2"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}, Mode=OneWay}" />
<!-- TODO 原文翻译共同显示 -->
<TextBlock Margin="0,0,0,2" Text="{x:Bind ViewModel.TimelineSliderThumbLyricsLine.OriginalText, Mode=OneWay}" />
</StackPanel>
</Grid>
<Grid
Height="32"
Margin="0,-12,0,0"
VerticalAlignment="Top"
Background="Transparent"
PointerEntered="TimelineSliderOverlay_PointerEntered"
PointerExited="TimelineSliderOverlay_PointerExited"
PointerMoved="TimelineSliderOverlay_PointerMoved"
PointerPressed="TimelineSliderOverlay_PointerPressed" />
</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
x:Name="BottomCommandFlyoutTriggerHint"
Width="150"
Margin="4"
Background="{ThemeResource TextFillColorPrimaryBrush}"
CornerRadius="2"
Translation="0,0,0">
<Grid.TranslationTransition>
<Vector3Transition />
</Grid.TranslationTransition>
</Grid>
<Grid.ContextFlyout>
<Flyout x:Name="BottomCommandFlyout" ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0" />
<Setter Property="MinWidth" Value="400" />
<Setter Property="MinHeight" Value="100" />
<Setter Property="CornerRadius" Value="12" />
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid x:Name="BottomCommandFlyoutContainer" VerticalAlignment="Bottom" />
</Flyout>
</Grid.ContextFlyout>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,301 @@
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Numerics;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace BetterLyrics.WinUI3.Controls;
public sealed partial class NowPlayingBar : UserControl,
IRecipient<PropertyChangedMessage<SongInfo?>>,
IRecipient<PropertyChangedMessage<BitmapImage?>>,
IRecipient<PropertyChangedMessage<TimeSpan>>
{
public NowPlayingBarViewModel ViewModel => (NowPlayingBarViewModel)DataContext;
public event EventHandler? SongInfoTapped;
public event EventHandler? TimeTapped;
public bool ShowTime
{
get { return (bool)GetValue(ShowTimeProperty); }
set { SetValue(ShowTimeProperty, value); }
}
public static readonly DependencyProperty ShowTimeProperty =
DependencyProperty.Register(nameof(ShowTime), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
public bool ShowSongInfo
{
get { return (bool)GetValue(ShowSongInfoProperty); }
set { SetValue(ShowSongInfoProperty, value); }
}
public static readonly DependencyProperty ShowSongInfoProperty =
DependencyProperty.Register(nameof(ShowSongInfo), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false));
public bool IsCompactMode
{
get { return (bool)GetValue(IsCompactModeProperty); }
set { SetValue(IsCompactModeProperty, value); }
}
public static readonly DependencyProperty IsCompactModeProperty =
DependencyProperty.Register(nameof(IsCompactMode), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false, OnDependencyPropertyChanged));
public bool IsAutoHideEnabled
{
get { return (bool)GetValue(IsAutoHideEnabledProperty); }
set { SetValue(IsAutoHideEnabledProperty, value); }
}
public static readonly DependencyProperty IsAutoHideEnabledProperty =
DependencyProperty.Register(nameof(IsAutoHideEnabled), typeof(bool), typeof(NowPlayingBar), new PropertyMetadata(false, OnDependencyPropertyChanged));
private bool _isPointerInBottomCommandGrid = false;
public NowPlayingBar()
{
InitializeComponent();
DataContext = Ioc.Default.GetRequiredService<NowPlayingBarViewModel>();
WeakReferenceMessenger.Default.RegisterAll(this);
}
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is NowPlayingBar self)
{
if (e.Property == IsCompactModeProperty)
{
self.OnIsCompactModeChanged();
}
else if (e.Property == IsAutoHideEnabledProperty)
{
self.OnIsAutoHideEnabledChanged();
}
}
}
private void OnIsAutoHideEnabledChanged()
{
if (IsAutoHideEnabled)
{
if (!_isPointerInBottomCommandGrid)
{
ViewModel.BottomCommandGridOpacity = 0;
}
}
else
{
ViewModel.BottomCommandGridOpacity = 1;
}
}
private void OnIsCompactModeChanged()
{
if (IsCompactMode)
{
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 PlaybackSettingsShortcutMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
PlaybackSettingsFlyout.Content = new PlaybackSettingsControl
{
MaxHeight = 500,
MaxWidth = 850,
};
PlaybackSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
}
private void VolumeButton_Click(object sender, RoutedEventArgs e)
{
VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
}
private void PlaybackSettingsFlyout_Closed(object sender, object e)
{
PlaybackSettingsFlyout.Content = null;
}
private void LyricsSettingsFlyout_Closed(object sender, object e)
{
LyricsSettingsFlyout.Content = null;
}
private void LyricsSettingsShortcutMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
LyricsSettingsFlyout.Content = new LyricsWindowSettingsControl
{
MaxHeight = 500,
MaxWidth = 850,
};
LyricsSettingsFlyout.ShowAt(BottomRightCommandStackPanel);
}
private void TimelineSliderOverlay_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
var grid = (Grid)sender;
var pos = e.GetCurrentPoint(grid).Position;
var ratio = pos.X / grid.ActualWidth;
ViewModel.MediaSessionsService.ChangePosition(TimelineSlider.Maximum * ratio);
}
private void TimelineSliderOverlay_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
float targetX;
var grid = (Grid)sender;
var pos = e.GetCurrentPoint(grid).Position;
var ratio = pos.X / grid.ActualWidth;
ViewModel.TimelineSliderThumbSeconds = TimelineSlider.Maximum * ratio;
if (pos.X + TimelineSliderLyricsLineInfo.ActualWidth > grid.ActualWidth)
{
targetX = (float)(grid.ActualWidth - TimelineSliderLyricsLineInfo.ActualWidth);
}
else
{
targetX = (float)pos.X;
}
TimelineSliderLyricsLineInfo.Translation = new Vector3(targetX, 0, 0);
}
private void TimelineSliderOverlay_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
ViewModel.TimelineSliderThumbOpacity = 0.7f;
}
private void TimelineSliderOverlay_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
ViewModel.TimelineSliderThumbOpacity = 0f;
}
private void ExtendedSlider_ValueChangedByUser(object sender, Events.ExtendedSliderValueChangedByUserEventArgs e)
{
SystemVolumeHook.MasterVolume = ViewModel.Volume;
}
private void LyricsSearchShortcutButton_Click(object sender, RoutedEventArgs e)
{
WindowHook.OpenOrShowWindow<LyricsSearchWindow>();
}
private void SongInfoStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
{
SongInfoTapped?.Invoke(this, EventArgs.Empty);
}
private void TimeStackPanel_Tapped(object sender, TappedRoutedEventArgs e)
{
TimeTapped?.Invoke(this, EventArgs.Empty);
}
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
_isPointerInBottomCommandGrid = true;
if (IsAutoHideEnabled && BottomCommandGrid.Children.Count != 0)
{
ViewModel.BottomCommandGridOpacity = 1f;
}
e.Handled = true;
}
private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
_isPointerInBottomCommandGrid = false;
if (IsAutoHideEnabled && BottomCommandGrid.Children.Count != 0)
{
ViewModel.BottomCommandGridOpacity = 0f;
}
e.Handled = true;
}
private void BottomCommandFlyoutTrigger_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (BottomCommandFlyoutContainer.Children.Count != 0)
{
ViewModel.BottomCommandFlyoutTriggerOpacity = 1f;
}
}
private void BottomCommandFlyoutTrigger_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
if (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);
}
}
public void Receive(PropertyChangedMessage<SongInfo?> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
{
TitleTextBlock.Text = message.NewValue?.Title;
ArtistsTextBlock.Text = message.NewValue?.DisplayArtists;
}
}
}
public void Receive(PropertyChangedMessage<BitmapImage?> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtBitmapImage))
{
AlbumArtImageSwitcher.Source = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<TimeSpan> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
{
DispatcherQueue.TryEnqueue(() =>
{
TimelineSlider.Value = message.NewValue.TotalSeconds;
});
}
}
}
}

View File

@@ -22,8 +22,9 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowSpacing="18">
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
@@ -42,51 +43,53 @@
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<!-- 播放源列表 -->
<ListView
x:Name="MediaSourceProvidersListView"
<InfoBar
Grid.Row="0"
AllowDrop="True"
CanDragItems="True"
CanReorderItems="True"
DragItemsCompleted="MediaSourceProvidersListView_DragItemsCompleted"
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
<StackPanel Orientation="Horizontal" Spacing="6">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Provider, Mode=OneWay}" />
</ToolTipService.ToolTip>
<FontIcon
FontFamily="Segoe UI Symbol"
FontSize="12"
Glyph="&#x283F;" />
<Grid
Width="16"
Height="16"
CornerRadius="4">
<ImageIcon Source="{Binding LogoPath}" />
</Grid>
<TextBlock
MaxWidth="200"
Text="{Binding DisplayName, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
IsClosable="False"
IsOpen="{x:Bind ViewModel.AppSettings.MusicGallerySettings.LyricsWindowStatus.IsOpened, Mode=OneWay}"
Message="音乐库窗口已打开,将忽略对其他播放源的监听"
Severity="Informational" />
<ScrollViewer Grid.Row="1" Style="{StaticResource SettingsScrollViewerStyle}">
<!-- 播放源列表 -->
<Grid Grid.Row="1" Margin="36,0,36,4">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<TextBlock x:Uid="SettingsPageConfigPlaybackSource" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<ComboBox
x:Name="MediaSourceProvidersListView"
HorizontalAlignment="Stretch"
ItemsSource="{x:Bind ViewModel.AppSettings.MediaSourceProvidersInfo, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedMediaSourceProvider, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:MediaSourceProviderInfo">
<Grid Padding="2,4" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Provider, Mode=OneWay}" />
</ToolTipService.ToolTip>
<Grid
Grid.Column="0"
Width="16"
Height="16"
CornerRadius="4">
<ImageIcon Source="{Binding LogoPath}" />
</Grid>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding DisplayName, Mode=OneWay}"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
<!-- 播放源配置 -->
<ScrollViewer Grid.Row="2" Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -269,6 +272,7 @@
</Grid>
<!-- 播放源空白占位 -->
<StackPanel
Grid.Column="0"
HorizontalAlignment="Center"
@@ -294,6 +298,8 @@
HorizontalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
<!-- 右侧杂项设置 -->
<Grid Grid.Column="1">
<ScrollViewer Style="{StaticResource SettingsScrollViewerStyle}">
<Grid Style="{StaticResource SettingsGridStyle}">
@@ -347,7 +353,8 @@
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}"
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay}"
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
<local:PropertyRow x:Uid="LyricsPageTransliterationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TransliterationProvider, Mode=OneWay, Converter={StaticResource TransliterationSearchProviderToDisplayNameConverter}}" />
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.TranslationProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
<local:PropertyRow
x:Uid="LyricsPageMatchPercentage"
Unit="%"
@@ -359,6 +366,10 @@
</StackPanel>
</Expander>
<dev:SettingsCard x:Uid="SettingsPageListenNewSession">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.ListenOnNewPlaybackSource, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
</dev:SettingsCard>
@@ -514,6 +525,7 @@
</Grid>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -1,17 +1,7 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -76,13 +66,13 @@ namespace BetterLyrics.WinUI3.Controls
private void UpdateShadowCastGridCornerRadius()
{
var minSize = Math.Min(ShadowCastGrid.ActualHeight, ShadowCastGrid.ActualWidth);
ShadowCastGrid.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
ShadowCastGrid.CornerRadius = new(CornerRadiusAmount / 100.0 * (minSize / 2));
}
private void UpdateShadowRectCornerRadius()
{
var minSize = Math.Min(ShadowRect.ActualHeight, ShadowRect.ActualWidth);
ShadowRect.CornerRadius = new(CornerRadiusAmount / 100.0 * minSize);
ShadowRect.CornerRadius = new(CornerRadiusAmount / 100.0 * (minSize / 2));
}
private void ShadowCastGrid_SizeChanged(object sender, SizeChangedEventArgs e)

View File

@@ -14,9 +14,9 @@
x:Name="TrayIcon"
x:FieldModifier="public"
ContextMenuMode="SecondWindow"
DoubleClickCommand="{x:Bind ViewModel.OpenLyricsCommand}"
DoubleClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
IconSource="ms-appx:///Assets/Logo.ico"
LeftClickCommand="{x:Bind ViewModel.OpenLyricsCommand}"
LeftClickCommand="{x:Bind ViewModel.OpenLyricsWindowSwitchCommand}"
NoLeftClickDelay="True"
ToolTipText="{x:Bind ViewModel.ToolTipText, Mode=OneWay}">
<tb:TaskbarIcon.ContextFlyout>
@@ -39,11 +39,6 @@
Command="{x:Bind ViewModel.OpenLyricsSearchWindowCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE721;}" />
<MenuFlyoutItem
x:Uid="SystemTrayLyrics"
Command="{x:Bind ViewModel.OpenLyricsCommand}"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE90B;}" />
<MenuFlyoutItem
x:Uid="SystemTrayMusicGallery"
Command="{x:Bind ViewModel.OpenMusicGalleryCommand}"

View File

@@ -87,34 +87,17 @@
</dev:SettingsExpander>
<dev:SettingsExpander
x:Uid="SettingsPageWindowBounds"
x:Uid="SettingsPagePinToTaskbar"
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xF16B;}"
IsExpanded="True">
Glyph=&#xE7C4;}"
IsExpanded="{x:Bind LyricsWindowStatus.IsPinToTaskbar, Mode=OneWay}">
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsPinToTaskbar, Mode=TwoWay}" />
<dev:SettingsExpander.Items>
<dev:SettingsCard Header="X">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind LyricsWindowStatus.WindowX, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard Header="Y">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind LyricsWindowStatus.WindowY, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageWidth">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind LyricsWindowStatus.WindowWidth, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageHeight">
<NumberBox
SmallChange="10"
SpinButtonPlacementMode="Inline"
Value="{x:Bind LyricsWindowStatus.WindowHeight, Mode=TwoWay}" />
<dev:SettingsCard x:Uid="SettingsPageTaskbarPlacement">
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TaskbarPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageLeft" />
<ComboBoxItem x:Uid="SettingsPageRight" />
</ComboBox>
</dev:SettingsCard>
</dev:SettingsExpander.Items>
</dev:SettingsExpander>
@@ -140,14 +123,6 @@
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsShownInSwitchers, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageClickThrough" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE7C9;}">
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsClickThrough, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageBorderless" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE8B2;}">
<ToggleSwitch IsOn="{x:Bind LyricsWindowStatus.IsBorderless, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="SettingsPageDragArea" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEB41;}">
<ComboBox SelectedIndex="{x:Bind LyricsWindowStatus.TitleBarArea, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
<ComboBoxItem x:Uid="SettingsPageTitleBarAreaNone" />

View File

@@ -1,23 +1,10 @@
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

View File

@@ -0,0 +1,41 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml.Data;
using System;
namespace BetterLyrics.WinUI3.Converter
{
public partial class TransliterationSearchProviderToDisplayNameConverter : IValueConverter
{
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is TransliterationSearchProvider provider)
{
return provider switch
{
TransliterationSearchProvider.LrcLib => "LrcLib",
TransliterationSearchProvider.QQ => "QQ 音乐",
TransliterationSearchProvider.Netease => "网易云音乐",
TransliterationSearchProvider.Kugou => "酷狗音乐",
TransliterationSearchProvider.AmllTtmlDb => "amll-ttml-db",
TransliterationSearchProvider.AppleMusic => "Apple Music",
TransliterationSearchProvider.LocalLrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalLrcFile"),
TransliterationSearchProvider.LocalMusicFile => _resourceService.GetLocalizedString("LyricsSearchProviderLocalMusicFile"),
TransliterationSearchProvider.LocalEslrcFile => _resourceService.GetLocalizedString("LyricsSearchProviderEslrcFile"),
TransliterationSearchProvider.LocalTtmlFile => _resourceService.GetLocalizedString("LyricsSearchProviderTtmlFile"),
TransliterationSearchProvider.BetterLyrics => "BetterLyrics",
_ => "N/A",
};
}
return "N/A";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Enums
namespace BetterLyrics.WinUI3.Enums
{
public enum ImageSwitchType
{

View File

@@ -1,8 +0,0 @@
namespace BetterLyrics.WinUI3.Enums
{
public enum LineMaskType
{
Glow,
Highlight,
}
}

View File

@@ -1,11 +0,0 @@
// 2025/6/23 by Zhe Fang
namespace BetterLyrics.WinUI3.Enums
{
public enum LineRenderingType
{
CurrentChar,
LineStartToCurrentChar,
CurrentLine
}
}

View File

@@ -0,0 +1,8 @@
namespace BetterLyrics.WinUI3.Enums
{
public enum LyricsEffectScope
{
LongDurationSyllable,
LineStartToCurrentChar,
}
}

View File

@@ -3,8 +3,6 @@
public enum ShortcutID
{
LyricsWindowShowOrHide,
Borderless,
ClickThrough,
LyricsWindowSwitch,
PlayOrPauseSong,
NextSong,

View File

@@ -0,0 +1,8 @@
namespace BetterLyrics.WinUI3.Enums
{
public enum TaskbarPlacement
{
Left,
Right,
}
}

View File

@@ -0,0 +1,17 @@
namespace BetterLyrics.WinUI3.Enums
{
public enum TransliterationSearchProvider
{
QQ,
Kugou,
Netease,
LrcLib,
AmllTtmlDb,
AppleMusic,
LocalMusicFile,
LocalLrcFile,
LocalEslrcFile,
LocalTtmlFile,
BetterLyrics
}
}

View File

@@ -0,0 +1,16 @@
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
namespace BetterLyrics.WinUI3.Events
{
public class InteractiveAreaEventArgs : EventArgs
{
public IList<FrameworkElement> Elements { get; set; }
public InteractiveAreaEventArgs(IList<FrameworkElement> elements)
{
Elements = elements;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Events
{
public class TaskbarFreeBoundsChangedEventArgs : EventArgs
{
public Rect TaskbarFreeBounds { get; }
public TaskbarFreeBoundsChangedEventArgs(Rect taskbarBounds)
{
TaskbarFreeBounds = taskbarBounds;
}
}
}

View File

@@ -59,6 +59,21 @@ namespace BetterLyrics.WinUI3.Extensions
LyricsSearchProvider.LocalTtmlFile => TranslationSearchProvider.LocalTtmlFile,
_ => null,
};
public TransliterationSearchProvider? ToTransliterationSearchProvider() => provider switch
{
LyricsSearchProvider.LrcLib => TransliterationSearchProvider.LrcLib,
LyricsSearchProvider.QQ => TransliterationSearchProvider.QQ,
LyricsSearchProvider.Kugou => TransliterationSearchProvider.Kugou,
LyricsSearchProvider.Netease => TransliterationSearchProvider.Netease,
LyricsSearchProvider.AmllTtmlDb => TransliterationSearchProvider.AmllTtmlDb,
LyricsSearchProvider.AppleMusic => TransliterationSearchProvider.AppleMusic,
LyricsSearchProvider.LocalMusicFile => TransliterationSearchProvider.LocalMusicFile,
LyricsSearchProvider.LocalLrcFile => TransliterationSearchProvider.LocalLrcFile,
LyricsSearchProvider.LocalEslrcFile => TransliterationSearchProvider.LocalEslrcFile,
LyricsSearchProvider.LocalTtmlFile => TransliterationSearchProvider.LocalTtmlFile,
_ => null,
};
}
}
}

View File

@@ -1,8 +1,11 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Extensions
@@ -11,17 +14,17 @@ namespace BetterLyrics.WinUI3.Extensions
{
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
public static LyricsWindowStatus DesktopMode()
public static LyricsWindowStatus DesktopMode(Window? window = null)
{
return new LyricsWindowStatus
window ??= WindowHook.GetWindow<SystemTrayWindow>();
return new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("DesktopMode"),
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
WindowBounds = new Rect(100, 100, 600, 250),
IsLocked = true,
IsAlwaysOnTop = true,
IsAlwaysOnTopPolling = true,
IsBorderless = true,
IsClickThrough = true,
IsAdaptToEnvironment = true,
IsShownInSwitchers = false,
EnvironmentSampleMode = WindowPixelSampleMode.WindowEdge,
@@ -36,20 +39,19 @@ namespace BetterLyrics.WinUI3.Extensions
};
}
public static LyricsWindowStatus DockedMode()
public static LyricsWindowStatus DockedMode(Window? window = null)
{
var status = new LyricsWindowStatus
window ??= WindowHook.GetWindow<SystemTrayWindow>();
var status = new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("DockedMode"),
IsWorkArea = true,
IsAlwaysOnTop = true,
IsAlwaysOnTopPolling = true,
IsBorderless = true,
IsAdaptToEnvironment = true,
IsShownInSwitchers = false,
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
EnvironmentSampleMode = WindowPixelSampleMode.BelowWindow,
TitleBarArea = TitleBarArea.None,
LyricsStyleSettings = new LyricsStyleSettings
{
LyricsAlignmentType = TextAlignmentType.Center,
@@ -64,19 +66,18 @@ namespace BetterLyrics.WinUI3.Extensions
return status;
}
public static LyricsWindowStatus FullscreenMode()
public static LyricsWindowStatus FullscreenMode(Window? window = null)
{
var status = new LyricsWindowStatus
window ??= WindowHook.GetWindow<SystemTrayWindow>();
var status = new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("FullscreenMode"),
IsBorderless = true,
IsAlwaysOnTop = true,
TitleBarArea = TitleBarArea.None,
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
LyricsStyleSettings = new LyricsStyleSettings
{
LyricsAlignmentType = TextAlignmentType.Center,
},
IsFullscreen = true,
};
status.WindowBounds = new Rect(
status.MonitorBounds.X,
@@ -87,22 +88,49 @@ namespace BetterLyrics.WinUI3.Extensions
return status;
}
public static LyricsWindowStatus StandardMode()
public static LyricsWindowStatus StandardMode(Window? window = null)
{
return new LyricsWindowStatus
window ??= WindowHook.GetWindow<SystemTrayWindow>();
return new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("StandardMode"),
};
}
public static LyricsWindowStatus NarrowMode()
public static LyricsWindowStatus NarrowMode(Window? window = null)
{
return new LyricsWindowStatus
window ??= WindowHook.GetWindow<SystemTrayWindow>();
return new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("NarrowMode"),
WindowBounds = new Rect(100, 100, 400, 800),
LyricsLayoutOrientation = LyricsLayoutOrientation.Vertical,
};
}
public static LyricsWindowStatus TaskbarMode(Window? window = null)
{
window ??= WindowHook.GetWindow<SystemTrayWindow>();
return new LyricsWindowStatus(window)
{
Name = _resourceService.GetLocalizedString("TaskbarMode"),
LyricsDisplayType = LyricsDisplayType.LyricsOnly,
IsPinToTaskbar = true,
IsLocked = true,
IsAlwaysOnTop = true,
IsAlwaysOnTopPolling = true,
IsAdaptToEnvironment = true,
IsShownInSwitchers = false,
EnvironmentSampleMode = WindowPixelSampleMode.WindowEdge,
LyricsStyleSettings = new()
{
LyricsAlignmentType = TextAlignmentType.Center,
},
LyricsBackgroundSettings = new LyricsBackgroundSettings
{
IsFluidOverlayEnabled = false,
}
};
}
}
}

View File

@@ -1,4 +1,5 @@
using Windows.Foundation;
using Vanara.PInvoke;
using Windows.Foundation;
using Windows.Graphics;
namespace BetterLyrics.WinUI3.Extensions
@@ -77,5 +78,15 @@ namespace BetterLyrics.WinUI3.Extensions
);
}
}
extension(RECT rect)
{
public Rect ToRect() => new(
rect.Left,
rect.Top,
rect.Right - rect.Left,
rect.Bottom - rect.Top
);
}
}
}

View File

@@ -0,0 +1,18 @@
using System.Drawing;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Extensions
{
public static class RectangleExtensions
{
extension(Rectangle rect)
{
public Rect ToRect() => new(
rect.Left,
rect.Top,
rect.Right - rect.Left,
rect.Bottom - rect.Top
);
}
}
}

View File

@@ -26,6 +26,8 @@ namespace BetterLyrics.WinUI3.Extensions
}
return "";
}
public string GetFileName() => Path.GetFileName(track.Path);
}
}
}

View File

@@ -1,8 +1,8 @@
using Microsoft.Graphics.Canvas.Text;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
@@ -10,8 +10,6 @@ namespace BetterLyrics.WinUI3.Helper
{
public static class FontHelper
{
public static string[] SystemFontFamilies => CanvasTextFormat.GetSystemFontFamilies().Order().ToArray();
public static string GetLocalizedFontFamilyName(string sourceName, string langCode)
{
if (langCode == "")
@@ -35,5 +33,20 @@ namespace BetterLyrics.WinUI3.Helper
return sourceName;
}
public static List<string> GetSystemFontFamilies()
{
List<string> fontFamilies = new();
foreach (var font in Fonts.SystemFontFamilies)
{
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-us"), out string englishFamilyName))
{
fontFamilies.Add(englishFamilyName);
}
}
return fontFamilies.Order().ToList();
}
}
}

View File

@@ -2,7 +2,6 @@
using BetterLyrics.WinUI3.Enums;
using Impressionist.Abstractions;
using Microsoft.Graphics.Canvas;
using System;
using System.IO;
using System.Linq;
@@ -53,42 +52,13 @@ namespace BetterLyrics.WinUI3.Helper
return buffer;
}
public static async Task<BitmapDecoder> MakeSquareWithThemeColor(IBuffer buffer, PaletteGeneratorType generatorType)
public static async Task<BitmapDecoder> GetBitmapDecoder(IBuffer buffer)
{
using var stream = new InMemoryRandomAccessStream();
await stream.WriteAsync(buffer);
var decoder = await BitmapDecoder.CreateAsync(stream);
if (decoder.PixelWidth == decoder.PixelHeight)
{
// 已经是正方形,直接返回
return decoder;
}
using var device = CanvasDevice.GetSharedDevice();
using var canvasBitmap = await CanvasBitmap.LoadAsync(device, stream);
var size = Math.Max(decoder.PixelWidth, decoder.PixelHeight);
var result = await GetAccentColorAsync(decoder, generatorType);
var color = Windows.UI.Color.FromArgb(255, (byte)result.Color.X, (byte)result.Color.Y, (byte)result.Color.Z);
using var renderTarget = new CanvasRenderTarget(device, size, size, 96);
int offsetX = (int)(size - decoder.PixelWidth) / 2;
int offsetY = (int)(size - decoder.PixelHeight) / 2;
using (var ds = renderTarget.CreateDrawingSession())
{
ds.FillRectangle(0, 0, size, size, color);
ds.DrawImage(canvasBitmap, offsetX, offsetY);
}
// 保存为 PNG 并转为 byte[]
stream.Seek(0);
stream.Size = 0;
await renderTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
stream.Seek(0);
var newDecoder = await BitmapDecoder.CreateAsync(stream);
return newDecoder;
return decoder;
}
public static byte[] GenerateNoiseBGRA(int width, int height)

View File

@@ -0,0 +1,124 @@
using BetterLyrics.WinUI3.Events;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using Vanara.PInvoke;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Helper
{
/// <summary>
/// 用于管理覆盖层窗口的鼠标交互区域检测
/// </summary>
public class OverlayInputHelper
{
private readonly Window _window;
private readonly IntPtr _hwnd;
private readonly DispatcherTimer _timer;
private readonly List<FrameworkElement> _interactiveControls = new();
private bool _wasOverControl = false;
public Action<InteractiveAreaEventArgs> OnInteractiveAreaEntered;
public Action<InteractiveAreaEventArgs> OnInteractiveAreaMoved;
public Action OnInteractiveAreaExited;
public OverlayInputHelper(Window window)
{
_window = window;
_hwnd = WinRT.Interop.WindowNative.GetWindowHandle(_window);
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(50);
_timer.Tick += Timer_Tick;
}
public void Register(FrameworkElement element)
{
if (!_interactiveControls.Contains(element)) _interactiveControls.Add(element);
}
public void Unregister(FrameworkElement element)
{
if (_interactiveControls.Contains(element)) _interactiveControls.Remove(element);
}
public void Start()
{
_timer.Start();
OnInteractiveAreaExited?.Invoke();
}
public void Stop()
{
_timer.Stop();
}
private void Timer_Tick(object? sender, object e)
{
User32.GetCursorPos(out var mousePoint);
bool isOverAnyControl = false;
List<FrameworkElement> overlappedElements = new();
foreach (var control in _interactiveControls)
{
if (control.XamlRoot == null || !control.XamlRoot.IsHostVisible) continue;
var bounds = GetElementScreenBounds(control);
if (mousePoint.X >= bounds.X &&
mousePoint.X <= (bounds.X + bounds.Width) &&
mousePoint.Y >= bounds.Y &&
mousePoint.Y <= (bounds.Y + bounds.Height))
{
isOverAnyControl = true;
overlappedElements.Add(control);
}
}
if (isOverAnyControl)
{
OnInteractiveAreaMoved?.Invoke(new InteractiveAreaEventArgs(overlappedElements));
}
if (isOverAnyControl != _wasOverControl)
{
if (isOverAnyControl)
{
OnInteractiveAreaEntered?.Invoke(new InteractiveAreaEventArgs(overlappedElements));
}
else
{
OnInteractiveAreaExited?.Invoke();
}
_wasOverControl = isOverAnyControl;
}
}
private Rect GetElementScreenBounds(FrameworkElement element)
{
try
{
var transform = element.TransformToVisual(null);
var topLeft = transform.TransformPoint(new Point(0, 0));
double dpiScale = element.XamlRoot.RasterizationScale;
int clientX = (int)(topLeft.X * dpiScale);
int clientY = (int)(topLeft.Y * dpiScale);
var point = new POINT { X = clientX, Y = clientY };
User32.ClientToScreen(_hwnd, ref point);
return new Rect(point.X, point.Y, element.ActualWidth * dpiScale, element.ActualHeight * dpiScale);
}
catch
{
return Rect.Empty;
}
}
}
}

View File

@@ -29,6 +29,8 @@ namespace BetterLyrics.WinUI3.Helper
public static bool IsAppleMusic(string? id) => id is PlayerID.AppleMusic or PlayerID.AppleMusicAlternative;
public static bool IsBetterLyrics(string? id) => id is PlayerID.BetterLyrics or PlayerID.BetterLyricsDebug;
public static string? GetDisplayName(string? id) => id switch
{
PlayerID.Spotify => PlayerName.Spotify,

View File

@@ -66,7 +66,7 @@ namespace BetterLyrics.WinUI3.Hooks
_hooks.Clear();
//_timer.Stop();
_timer.Stop();
}
private void Timer_Tick(object? sender, object e)

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Enums;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using Vanara.PInvoke;
@@ -19,13 +20,10 @@ namespace BetterLyrics.WinUI3.Hooks
/// <param name="id"></param>
/// <param name="keys"></param>
/// <param name="action"></param>
private static void RegisterHotKey<T>(ShortcutID id, List<string> keys, Action action)
private static void RegisterHotKey(Window window, ShortcutID id, List<string> keys, Action action)
{
if (keys.Count == 0) return;
var window = WindowHook.GetWindow<T>();
if (window == null) return;
HWND hwnd = WindowNative.GetWindowHandle(window);
User32.HotKeyModifiers modifiers = User32.HotKeyModifiers.MOD_NONE;
VirtualKey key = VirtualKey.None;
@@ -60,21 +58,18 @@ namespace BetterLyrics.WinUI3.Hooks
}
}
private static void UnregisterHotKey<T>(ShortcutID id)
private static void UnregisterHotKey(Window window, ShortcutID id)
{
var window = WindowHook.GetWindow<T>();
if (window == null) return;
HWND hwnd = WindowNative.GetWindowHandle(window);
User32.UnregisterHotKey(hwnd, (int)id);
_actions.Remove((int)id);
_keys.Remove((int)id);
}
public static void UpdateHotKey<T>(ShortcutID id, List<string> keys, Action action)
public static void UpdateHotKey(Window window, ShortcutID id, List<string> keys, Action action)
{
UnregisterHotKey<T>(id);
RegisterHotKey<T>(id, keys, action);
UnregisterHotKey(window, id);
RegisterHotKey(window, id, keys, action);
}
public static bool IsHotKeyRegistered(ShortcutID id)

View File

@@ -0,0 +1,211 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Extensions;
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Definitions;
using FlaUI.Core.EventHandlers;
using FlaUI.UIA3;
using Microsoft.UI.Dispatching;
using System;
using System.Drawing;
using System.Threading;
namespace BetterLyrics.WinUI3.Hooks
{
public partial class TaskbarHook : IDisposable
{
private readonly UIA3Automation _automation;
private AutomationElement? _taskbar;
private StructureChangedEventHandlerBase? _structureHandler;
private PropertyChangedEventHandlerBase? _propertyHandler;
private TaskbarPlacement _currentPlacement;
private readonly DispatcherQueue _dispatcherQueue;
private readonly Action<TaskbarFreeBoundsChangedEventArgs> _onLayoutChanged;
private Timer? _debounceTimer;
private const int DebounceDelay = 150;
private bool _isDisposed;
public TaskbarHook(TaskbarPlacement placement, Action<TaskbarFreeBoundsChangedEventArgs> onLayoutChanged)
{
_automation = new UIA3Automation();
_onLayoutChanged = onLayoutChanged;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
_currentPlacement = placement;
StartHook();
}
public void UpdatePlacement(TaskbarPlacement newPlacement)
{
_currentPlacement = newPlacement;
RequestUpdate(); // 立即刷新位置
}
private void StartHook()
{
try
{
var desktop = _automation.GetDesktop();
_taskbar = desktop.FindFirstChild(cf => cf.ByClassName("Shell_TrayWnd"));
if (_taskbar == null) return;
// 监听结构变化
// 这里的返回值就是一个可以 Dispose 的对象
_structureHandler = _taskbar.RegisterStructureChangedEvent(
TreeScope.Descendants,
(element, type, id) => RequestUpdate());
// 监听属性变化
_propertyHandler = _taskbar.RegisterPropertyChangedEvent(
TreeScope.Element,
(element, id, val) => RequestUpdate(),
_automation.PropertyLibrary.Element.BoundingRectangle);
RequestUpdate();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Hook Init Failed: {ex.Message}");
}
}
private void RequestUpdate()
{
if (_isDisposed) return;
_debounceTimer?.Dispose();
_debounceTimer = new Timer(_ =>
{
Rectangle voidRect = CalculateVoidRect(_currentPlacement);
_dispatcherQueue.TryEnqueue(() =>
{
if (!_isDisposed && voidRect != Rectangle.Empty)
{
_onLayoutChanged?.Invoke(new TaskbarFreeBoundsChangedEventArgs(voidRect.ToRect()));
}
});
}, null, DebounceDelay, Timeout.Infinite);
}
private Rectangle CalculateVoidRect(TaskbarPlacement placement)
{
try
{
if (_taskbar == null) return Rectangle.Empty;
try { var _ = _taskbar.BoundingRectangle; }
catch
{
var desktop = _automation.GetDesktop();
_taskbar = desktop.FindFirstChild(cf => cf.ByClassName("Shell_TrayWnd"));
if (_taskbar == null) return Rectangle.Empty;
}
Rectangle taskbarRect = _taskbar.BoundingRectangle;
// 绝对右边界:托盘
int barrierRight = taskbarRect.Right;
var tray = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId("SystemTrayIcon")); // Win11
if (tray == null) tray = _taskbar.FindFirstDescendant(cf => cf.ByClassName("TrayNotifyWnd")); // Win10
if (tray != null) barrierRight = tray.BoundingRectangle.Left;
// 绝对左边界:任务栏左边缘 或 小组件(Win11)
int barrierLeft = taskbarRect.Left;
var widgets = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId("WidgetsButton"));
// 只有当小组件确实在最左侧时 (Win11默认),它才构成左边界
// 如果用户把任务栏设为靠左对齐,小组件会在开始按钮右边,这时候不把它当做左边界
if (widgets != null && widgets.BoundingRectangle.Left < taskbarRect.Left + 100)
{
barrierLeft = (int)widgets.BoundingRectangle.Right;
}
// 寻找 中间内容区域 (Start + Search + Apps) 的 左右极值
int contentMinLeft = barrierRight;
int contentMaxRight = barrierLeft;
// 定义所有中间元素
string[] systemButtonIds = new[] {
"StartButton", "SearchButton", "TaskViewButton", "ChatButton"
};
// 系统按钮
foreach (var id in systemButtonIds)
{
var btn = _taskbar.FindFirstDescendant(cf => cf.ByAutomationId(id));
if (btn != null)
{
var rect = btn.BoundingRectangle;
// 排除不可见的
if (rect.Width <= 0) continue;
// 更新极值
if (rect.Left < contentMinLeft) contentMinLeft = (int)rect.Left;
if (rect.Right > contentMaxRight) contentMaxRight = (int)rect.Right;
}
}
// App 图标
var appIcons = _taskbar.FindAllDescendants(cf => cf.ByClassName("Taskbar.TaskListButtonAutomationPeer"));
foreach (var icon in appIcons)
{
var rect = icon.BoundingRectangle;
if (rect.Width <= 0) continue;
if (rect.Left < contentMinLeft) contentMinLeft = (int)rect.Left;
if (rect.Right > contentMaxRight) contentMaxRight = (int)rect.Right;
}
// 如果完全没找到内容,重置为中间
if (contentMinLeft == barrierRight) contentMinLeft = taskbarRect.Left;
if (contentMaxRight == barrierLeft) contentMaxRight = taskbarRect.Left;
int finalLeft, finalRight;
int padding = 10;
if (placement == TaskbarPlacement.Left)
{
// 【小组件】... [空隙] ...【开始按钮】
// 如果是 Win10 或 Win11左对齐contentMinLeft 几乎等于 barrierLeft空隙为0
finalLeft = barrierLeft + padding;
finalRight = contentMinLeft - padding;
}
else // Right
{
// 【最后一个图标】... [空隙] ...【托盘】
finalLeft = contentMaxRight + padding;
finalRight = barrierRight - padding;
}
int width = finalRight - finalLeft;
if (width < 20) return Rectangle.Empty;
return new Rectangle(finalLeft, taskbarRect.Top, width, taskbarRect.Height);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Calc Rect Error: {ex.Message}");
return Rectangle.Empty;
}
}
public void Dispose()
{
if (_isDisposed) return;
_isDisposed = true;
_debounceTimer?.Dispose();
_structureHandler?.Dispose();
_propertyHandler?.Dispose();
_automation?.Dispose();
}
}
}

View File

@@ -1,11 +1,8 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
@@ -26,42 +23,31 @@ namespace BetterLyrics.WinUI3.Hooks
private static List<object> _activeWindows = [];
private static List<object> _workAreas = [];
private static readonly Dictionary<HWND, WindowStyle> _defaultWindowStyle = [];
private static readonly Dictionary<HWND, ExtendedWindowStyle> _defaultExtendedWindowStyle = [];
private static WindowStyle? _defaultWindowStyle;
private static ExtendedWindowStyle? _defaultExtendedWindowStyle;
private static readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
private static readonly IMediaSessionsService _mediaSessionsService = Ioc.Default.GetRequiredService<IMediaSessionsService>();
private static DispatcherQueueTimer? _setLyricsWindowVisibilityByPlayingStatusTimer;
public static void HideWindow<T>()
public static void HideWindow(this Window window)
{
var window = _activeWindows.Find(w => w is T);
var castedWindow = window as Window;
castedWindow?.Hide();
window.Hide();
}
public static void CloseWindow<T>()
public static void CloseWindow(this Window window)
{
if (typeof(T) == typeof(NowPlayingWindow))
if (window is NowPlayingWindow nowPlayingWindow)
{
EnsureDockModeReleased();
}
var window = _activeWindows.Find(w => w is T);
if (window is Window w)
{
w.Close();
_activeWindows.Remove(w);
if (GetWindowHandle(window) is IntPtr hwnd)
{
UnregisterWorkArea(hwnd);
}
nowPlayingWindow.LyricsWindowStatus.IsOpened = false;
}
window.Close();
_activeWindows.Remove(window);
}
public static void MinimizeWindow<T>()
public static void MinimizeWindow(this Window window)
{
var window = _activeWindows.Find(w => w is T);
if (window is Window w)
{
w.Minimize();
}
window.Minimize();
}
public static T? GetWindow<T>()
@@ -76,15 +62,28 @@ namespace BetterLyrics.WinUI3.Hooks
return default;
}
public static List<T> GetWindows<T>()
{
var windows = new List<T>();
foreach (var window in _activeWindows)
{
if (window is T castedWindow)
{
windows.Add(castedWindow);
}
}
return windows;
}
public static IntPtr? GetWindowHandle(object? obj)
{
if (obj is FrameworkElement frameworkElement)
{
return frameworkElement.XamlRoot.ContentIslandEnvironment.AppWindowId.GetWindowHandle();
}
else if (obj != null)
else if (obj is Window window)
{
return WindowNative.GetWindowHandle(obj);
return WindowNative.GetWindowHandle(window);
}
else
{
@@ -97,16 +96,22 @@ namespace BetterLyrics.WinUI3.Hooks
return GetWindowHandle(GetWindow<T>());
}
public static void OpenOrShowWindow<T>()
public static T OpenOrShowWindow<T>(LyricsWindowStatus? status = null)
{
var window = _activeWindows.Find(w => w is T);
//window = null;
var window = _activeWindows.Find(w =>
(typeof(T) != typeof(NowPlayingWindow) && w is T) ||
(typeof(T) == typeof(NowPlayingWindow) && w is T && ((NowPlayingWindow)w).LyricsWindowStatus == status)
);
if (window == null)
{
if (typeof(T) == typeof(NowPlayingWindow))
{
window = new NowPlayingWindow();
((NowPlayingWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent);
if (status == null)
{
throw new NullReferenceException(nameof(status));
}
window = new NowPlayingWindow(status);
}
else if (typeof(T) == typeof(SettingsWindow))
{
@@ -135,33 +140,24 @@ namespace BetterLyrics.WinUI3.Hooks
TrackWindow(window);
var castedWindow = (Window)window;
castedWindow.Restore();
castedWindow.Activate();
if (typeof(T) == typeof(SystemTrayWindow))
{
_defaultWindowStyle = castedWindow.GetWindowStyle();
_defaultExtendedWindowStyle = castedWindow.GetExtendedWindowStyle();
castedWindow.HideWindow();
}
if (typeof(T) == typeof(NowPlayingWindow))
{
_liveStatesService.InitLyricsWindowStatus();
var hwnd = WindowNative.GetWindowHandle(castedWindow);
_defaultWindowStyle.Add(hwnd, castedWindow.GetWindowStyle());
_defaultExtendedWindowStyle.Add(hwnd, castedWindow.GetExtendedWindowStyle());
var lyricsWindow = (NowPlayingWindow)window;
lyricsWindow.ViewModel.InitShortcuts();
lyricsWindow.ViewModel.InitFgWindowWatcher();
_mediaSessionsService.InitPlaybackShortcuts();
//TaskbarList.ThumbBarAddButtons(hwnd,
// [
// new Shell32.THUMBBUTTON()
// {
// szTip = "Previous",
// dwFlags = Shell32.THUMBBUTTONFLAGS.THBF_ENABLED,
// dwMask = Shell32.THUMBBUTTONMASK.THB_TOOLTIP | Shell32.THUMBBUTTONMASK.THB_FLAGS,
// }
// ]
//);
lyricsWindow.InitStatus();
lyricsWindow.InitFgWindowWatcher();
}
}
else
@@ -170,6 +166,13 @@ namespace BetterLyrics.WinUI3.Hooks
castedWindow.Activate();
castedWindow.AppWindow.MoveInZOrderAtTop();
}
if (typeof(T) == typeof(NowPlayingWindow))
{
((NowPlayingWindow)window).LyricsWindowStatus.IsOpened = true;
}
return (T)window;
}
public static void RestartApp(string args = "")
@@ -194,13 +197,19 @@ namespace BetterLyrics.WinUI3.Hooks
public static void ExitApp()
{
EnsureDockModeReleased();
EnsureAllWorkAreasReleased();
Environment.Exit(0);
}
private static void EnsureDockModeReleased()
private static void EnsureAllWorkAreasReleased()
{
SetIsWorkArea<NowPlayingWindow>(false);
foreach (var item in _workAreas)
{
if (GetWindowHandle(item) is IntPtr hwnd)
{
UnregisterWorkArea(hwnd);
}
}
}
private static void TrackWindow(object window)
@@ -218,40 +227,18 @@ namespace BetterLyrics.WinUI3.Hooks
if (_activeWindows.Contains(sender))
{
_activeWindows.Remove(sender);
var hwnd = WindowNative.GetWindowHandle(sender);
_defaultWindowStyle.Remove(hwnd);
_defaultExtendedWindowStyle.Remove(hwnd);
}
}
public static void SetIsClickThrough<T>(bool enable)
public static void SetIsWorkArea(this NowPlayingWindow window, bool enable)
{
Window? window = GetWindow<T>() as Window;
if (window == null) return;
IntPtr hwnd = WindowNative.GetWindowHandle(window);
if (enable)
{
window.SetExtendedWindowStyle(_defaultExtendedWindowStyle[hwnd] | ExtendedWindowStyle.Transparent | ExtendedWindowStyle.Layered);
}
else
{
window.SetExtendedWindowStyle(_defaultExtendedWindowStyle[hwnd]);
}
}
public static void SetIsWorkArea<T>(bool enable)
{
Window? window = GetWindow<T>() as Window;
if (window == null) return;
IntPtr hwnd = WindowNative.GetWindowHandle(window);
if (enable)
{
RegisterWorkArea(hwnd);
RegisterWorkArea(hwnd, window.LyricsWindowStatus);
}
else
{
@@ -259,35 +246,86 @@ namespace BetterLyrics.WinUI3.Hooks
}
}
public static void SetIsBorderless<T>(bool enable)
public static void SetIsLocked(this Window window, bool enable)
{
var window = GetWindow<T>() as Window;
if (window == null) return;
SetIsBorderless(window, enable);
SetIsClickThrough(window, enable);
}
var hwnd = WindowNative.GetWindowHandle(window);
if (enable)
public static void SetIsClickThrough(this Window window, bool enable)
{
if (_defaultExtendedWindowStyle is ExtendedWindowStyle style)
{
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
}
else
{
window.SetWindowStyle(_defaultWindowStyle[hwnd]);
if (enable)
{
window.SetExtendedWindowStyle(style | ExtendedWindowStyle.Layered | ExtendedWindowStyle.Transparent);
}
else
{
window.SetExtendedWindowStyle(style);
}
}
}
public static void SetIsShowInSwitchers<T>(bool enable)
public static void SetIsBorderless(this Window window, bool enable)
{
var window = GetWindow<T>() as Window;
if (window == null) return;
if (_defaultWindowStyle is WindowStyle style)
{
if (enable)
{
window.SetWindowStyle(WindowStyle.Popup | WindowStyle.Visible);
}
else
{
window.SetWindowStyle(style);
}
}
}
public static bool SetIsFullscreen(this Window window, bool enable, bool defaultExtendsContentIntoTitleBar = true)
{
if (window.AppWindow == null) return false;
if (enable)
{
window.ExtendsContentIntoTitleBar = false;
window.AppWindow.SetPresenter(AppWindowPresenterKind.FullScreen);
}
else
{
window.ExtendsContentIntoTitleBar = defaultExtendsContentIntoTitleBar;
window.AppWindow.SetPresenter(AppWindowPresenterKind.Overlapped);
}
return true;
}
public static bool SetIsMaximized(this Window window, bool enable)
{
if (window.AppWindow == null) return false;
if (enable)
{
window.Maximize();
}
else
{
window.Restore();
}
return true;
}
public static void SetIsShowInSwitchers(this Window window, bool enable)
{
if (window.AppWindow == null) return;
window.AppWindow.IsShownInSwitchers = enable;
}
public static void SetIsAlwaysOnTop<T>(bool enable)
public static void SetIsAlwaysOnTop(this Window window, bool enable)
{
var window = GetWindow<T>() as Window;
if (window == null) return;
if (window.AppWindow == null) return;
if (window.AppWindow.Presenter is OverlappedPresenter presenter)
{
@@ -295,36 +333,27 @@ namespace BetterLyrics.WinUI3.Hooks
}
}
public static void MoveAndResize<T>(Rect rect)
public static void MoveAndResize(this Window window, Rect rect)
{
var window = GetWindow<T>() as Window;
if (window == null) return;
if (window.AppWindow == null) return;
window.AppWindow.Move(new Windows.Graphics.PointInt32((int)rect.X, (int)rect.Y));
window.AppWindow.Resize(new Windows.Graphics.SizeInt32((int)rect.Width, (int)rect.Height));
}
public static void SetTitleBarArea<T>(TitleBarArea titleBarArea)
public static void SetTitleBarArea(this NowPlayingWindow window, TitleBarArea titleBarArea)
{
if (typeof(T) == typeof(NowPlayingWindow))
{
NowPlayingWindow? lyricsWindow = GetWindow<NowPlayingWindow>();
lyricsWindow?.SetTitleBarArea(titleBarArea);
}
else
{
throw new Exception($"Unsupported window type: {typeof(T).FullName}");
}
window.SetTitleBarArea(titleBarArea);
}
private static void RegisterWorkArea(IntPtr hwnd)
private static void RegisterWorkArea(IntPtr hwnd, LyricsWindowStatus status)
{
if (_workAreas.Contains(hwnd)) return;
var uEdge = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
var uEdge = status.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
double top = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? _liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Top : _liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Bottom - _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
double bottom = top + _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
double top = status.DockPlacement == DockPlacement.Top ? status.MonitorBounds.Top : status.MonitorBounds.Bottom - status.DockHeight;
double bottom = top + status.DockHeight;
Shell32.APPBARDATA abd = new()
{
@@ -333,9 +362,9 @@ namespace BetterLyrics.WinUI3.Hooks
uEdge = uEdge,
rc = new RECT
{
Left = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Left,
Left = (int)status.MonitorBounds.Left,
Top = (int)top,
Right = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Right,
Right = (int)status.MonitorBounds.Right,
Bottom = (int)bottom,
},
};
@@ -363,23 +392,22 @@ namespace BetterLyrics.WinUI3.Hooks
_workAreas.Remove(hwnd);
}
public static void UpdateWorkArea<T>()
public static void UpdateWorkArea(this NowPlayingWindow window)
{
var window = GetWindow<T>() as Window;
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
if (!_workAreas.Contains(hwnd))
return;
var uEdge = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
var status = window.LyricsWindowStatus;
double top = _liveStatesService.LiveStates.LyricsWindowStatus.DockPlacement == DockPlacement.Top ?
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Top :
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Bottom - _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
var uEdge = status.DockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
double bottom = top + _liveStatesService.LiveStates.LyricsWindowStatus.DockHeight;
double top = status.DockPlacement == DockPlacement.Top ?
status.MonitorBounds.Top :
status.MonitorBounds.Bottom - status.DockHeight;
double bottom = top + status.DockHeight;
Shell32.APPBARDATA abd = new()
{
@@ -388,9 +416,9 @@ namespace BetterLyrics.WinUI3.Hooks
uEdge = uEdge,
rc = new RECT
{
Left = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Left,
Left = (int)status.MonitorBounds.Left,
Top = (int)top,
Right = (int)_liveStatesService.LiveStates.LyricsWindowStatus.MonitorBounds.Right,
Right = (int)status.MonitorBounds.Right,
Bottom = (int)bottom,
},
};
@@ -403,37 +431,32 @@ namespace BetterLyrics.WinUI3.Hooks
///
/// </summary>
/// <param name="dispatcherQueue">请确保此参数指向同一个对象,建议传值 BaseViewModel._dispatcherQueue</param>
public static void SetLyricsWindowVisibilityByPlayingStatus(DispatcherQueue dispatcherQueue)
public static void SetLyricsWindowVisibilityByPlayingStatus(this NowPlayingWindow window, bool isPlaying, DispatcherQueue dispatcherQueue)
{
_setLyricsWindowVisibilityByPlayingStatusTimer ??= dispatcherQueue.CreateTimer();
var status = window.LyricsWindowStatus;
_setLyricsWindowVisibilityByPlayingStatusTimer.Debounce(() =>
status.VisibilityTimer ??= dispatcherQueue.CreateTimer();
status.VisibilityTimer.Debounce(() =>
{
var window = GetWindow<NowPlayingWindow>();
if (window == null) return;
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
if (status.AutoShowOrHideWindow && !isPlaying)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
if (status.IsWorkArea)
{
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
SetIsWorkArea<NowPlayingWindow>(false);
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
window.SetIsWorkArea(false);
}
HideWindow<NowPlayingWindow>();
window.HideWindow();
}
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
else if (status.AutoShowOrHideWindow && isPlaying)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
if (status.IsWorkArea)
{
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = true;
SetIsWorkArea<NowPlayingWindow>(true);
_liveStatesService.LiveStates.IsLyricsWindowStatusRefreshing = false;
window.SetIsWorkArea(true);
}
OpenOrShowWindow<NowPlayingWindow>();
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
OpenOrShowWindow<NowPlayingWindow>(status);
if (status.IsWorkArea)
{
MoveAndResize<NowPlayingWindow>(_liveStatesService.LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
window.MoveAndResize(status.GetWindowBoundsWhenWorkArea());
}
}
}, Constants.Time.DebounceTimeout);

View File

@@ -21,6 +21,7 @@ namespace BetterLyrics.WinUI3.Logic
double canvasHeight,
double targetYScrollOffset,
double playingLineTopOffsetFactor,
LyricsStyleSettings lyricsStyle,
LyricsEffectSettings lyricsEffect,
ValueTransition<double> canvasYScrollTransition,
Color bgColor,
@@ -37,6 +38,10 @@ namespace BetterLyrics.WinUI3.Logic
var currentPlayingLine = lines.ElementAtOrDefault(playingLineIndex);
if (currentPlayingLine == null) return;
var phoneticOpacity = lyricsStyle.PhoneticLyricsOpacity / 100.0;
var originalOpacity = lyricsStyle.OriginalLyricsOpacity / 100.0;
var translatedOpacity = lyricsStyle.TranslatedLyricsOpacity / 100.0;
for (int i = startIndex; i <= endIndex + 1; i++)
{
var line = lines.ElementAtOrDefault(i);
@@ -91,19 +96,23 @@ namespace BetterLyrics.WinUI3.Logic
line.PhoneticOpacityTransition.SetDuration(yScrollDuration);
line.PhoneticOpacityTransition.SetDelay(yScrollDelay);
line.PhoneticOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
line.PhoneticOpacityTransition.StartTransition(
absLineCountDelta == 0 ? phoneticOpacity : (isMouseScrolling ? phoneticOpacity : (1 - distanceFactor) * phoneticOpacity));
line.PlayedOriginalOpacityTransition.SetDuration(yScrollDuration);
line.PlayedOriginalOpacityTransition.SetDelay(yScrollDelay);
line.PlayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 1 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
line.PlayedOriginalOpacityTransition.StartTransition(
absLineCountDelta == 0 ? 1 : (isMouseScrolling ? 1.0 : (1 - distanceFactor) * originalOpacity));
line.UnplayedOriginalOpacityTransition.SetDuration(yScrollDuration);
line.UnplayedOriginalOpacityTransition.SetDelay(yScrollDelay);
line.UnplayedOriginalOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.3 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
line.UnplayedOriginalOpacityTransition.StartTransition(
absLineCountDelta == 0 ? originalOpacity : (isMouseScrolling ? originalOpacity : (1 - distanceFactor) * originalOpacity));
line.TranslatedOpacityTransition.SetDuration(yScrollDuration);
line.TranslatedOpacityTransition.SetDelay(yScrollDelay);
line.TranslatedOpacityTransition.StartTransition(absLineCountDelta == 0 ? 0.6 : (isMouseScrolling ? 0.3 : (1 - distanceFactor) * 0.3));
line.TranslatedOpacityTransition.StartTransition(
absLineCountDelta == 0 ? translatedOpacity : (isMouseScrolling ? translatedOpacity : (1 - distanceFactor) * translatedOpacity));
line.ColorTransition.SetDuration(yScrollDuration);
line.ColorTransition.SetDelay(yScrollDelay);

View File

@@ -1,19 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.Models
{
public partial class LiveStates : ObservableRecipient
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsWindowStatus LyricsWindowStatus { get; set; }
/// <summary>
/// 在需要暂时禁用监听歌词窗口位置大小变化时使用
/// </summary>
public bool IsLyricsWindowStatusRefreshing { get; set; } = false;
public LiveStates()
{
LyricsWindowStatus = new LyricsWindowStatus();
}
}
}

View File

@@ -1,7 +1,6 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.ResourceService;
using CommunityToolkit.Mvvm.DependencyInjection;
using DevWinUI;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -20,7 +19,6 @@ namespace BetterLyrics.WinUI3.Models
}
public bool AutoGenerated { get; set; } = false;
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
public bool IsWordByWord => LyricsLines.Any(x => x.LyricsSyllables.Count != 0);
public LyricsData()
{
@@ -32,23 +30,7 @@ namespace BetterLyrics.WinUI3.Models
LyricsLines = lyricsLines;
}
public void ClearTranslatedText()
{
foreach (var line in LyricsLines)
{
line.TranslatedText = "";
}
}
public void ClearPhoneticText()
{
foreach (var line in LyricsLines)
{
line.PhoneticText = "";
}
}
public void SetTranslatedText(LyricsData translationData, int toleranceMs = 0)
public void SetTranslatedText(LyricsData translationData, int toleranceMs = 50)
{
foreach (var line in LyricsLines)
{
@@ -69,7 +51,7 @@ namespace BetterLyrics.WinUI3.Models
}
}
public void SetPhoneticText(LyricsData phoneticData, int toleranceMs = 0)
public void SetPhoneticText(LyricsData phoneticData, int toleranceMs = 50)
{
foreach (var line in LyricsLines)
{
@@ -108,30 +90,6 @@ 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()
{
return new LyricsData([new LyricsLine
@@ -142,16 +100,6 @@ namespace BetterLyrics.WinUI3.Models
}]);
}
public static LyricsData GetParseErrorPlaceholder()
{
return new LyricsData([new LyricsLine
{
StartMs = 0,
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
OriginalText = _resourceService.GetLocalizedString("LyricsParseError"),
}]);
}
public static LyricsData GetLoadingPlaceholder()
{
return new LyricsData([

View File

@@ -1,16 +1,6 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Numerics;
using Windows.UI;
namespace BetterLyrics.WinUI3.Models
{

View File

@@ -10,7 +10,7 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMatchingThresholdOverwritten { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 40;
public LyricsSearchProviderInfo() { }

View File

@@ -3,12 +3,15 @@ using BetterLyrics.WinUI3.Extensions;
using CommunityToolkit.Mvvm.ComponentModel;
using NTextCat.Commons;
using System;
using System.Text.Json.Serialization;
namespace BetterLyrics.WinUI3.Models
{
public partial class LyricsSearchResult : ObservableObject, ICloneable
{
public LyricsSearchProvider Provider { get; set; }
[ObservableProperty] public partial TranslationSearchProvider? TranslationProvider { get; set; }
[ObservableProperty] public partial TransliterationSearchProvider? TransliterationProvider { get; set; }
public string? Raw { get; set; }
@@ -31,11 +34,11 @@ namespace BetterLyrics.WinUI3.Models
public string? SelfPath { get; set; }
public bool IsFound => !string.IsNullOrEmpty(Raw);
[JsonIgnore] public bool IsFound => !string.IsNullOrEmpty(Raw);
public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
[JsonIgnore] public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
public string? DisplayArtists => Artists?.Join("; ");
[JsonIgnore] public string? DisplayArtists => Artists?.Join("; ");
public object Clone()
{

View File

@@ -3,7 +3,11 @@ using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Linq;
using System.Text.Json.Serialization;
using Windows.Foundation;
namespace BetterLyrics.WinUI3.Models
@@ -14,11 +18,16 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty] public partial bool IsDefault { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string MonitorDeviceName { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsWorkArea { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsBorderless { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAlwaysOnTop { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsAlwaysOnTopPolling { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsShownInSwitchers { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsClickThrough { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLocked { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsPinToTaskbar { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TaskbarPlacement TaskbarPlacement { get; set; } = TaskbarPlacement.Right;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMaximized { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsFullscreen { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsLayoutOrientation LyricsLayoutOrientation { get; set; } = LyricsLayoutOrientation.Horizontal;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsDisplayType LyricsDisplayType { get; set; } = LyricsDisplayType.SplitView;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Rect WindowBounds { get; set; } = new Rect(100, 100, 800, 500);
@@ -37,77 +46,92 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoShowOrHideWindow { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TitleBarArea TitleBarArea { get; set; } = TitleBarArea.Top;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowX { get; set; } = 100;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowY { get; set; } = 100;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowWidth { get; set; } = 800;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double WindowHeight { get; set; } = 500;
[JsonIgnore][ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsOpened { get; set; } = false;
[JsonIgnore] public DispatcherQueueTimer? VisibilityTimer { get; set; }
public LyricsWindowStatus()
{
UpdateMonitorNameAndBounds();
LyricsStyleSettings.PropertyChanged += LyricsStyleSettings_PropertyChanged;
LyricsEffectSettings.PropertyChanged += LyricsEffectSettings_PropertyChanged;
LyricsBackgroundSettings.PropertyChanged += LyricsBackgroundSettings_PropertyChanged;
AlbumArtLayoutSettings.PropertyChanged += AlbumArtLayoutSettings_PropertyChanged;
AlbumArtAreaEffectSettings.PropertyChanged += AlbumArtAreaEffectSettings_PropertyChanged;
}
public LyricsWindowStatus(Window? targetWindow = null) : this()
{
UpdateMonitorNameAndBounds(targetWindow);
UpdateDemoWindowAndMonitorBounds();
}
partial void OnLyricsStyleSettingsChanged(LyricsStyleSettings oldValue, LyricsStyleSettings newValue)
{
oldValue.PropertyChanged -= OldLyricsStyleSettings_PropertyChanged;
newValue.PropertyChanged += OldLyricsStyleSettings_PropertyChanged;
oldValue.PropertyChanged -= LyricsStyleSettings_PropertyChanged;
newValue.PropertyChanged += LyricsStyleSettings_PropertyChanged;
}
private void LyricsStyleSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(LyricsStyleSettings));
}
partial void OnLyricsEffectSettingsChanged(LyricsEffectSettings oldValue, LyricsEffectSettings newValue)
{
oldValue.PropertyChanged -= OldLyricsEffectSettings_PropertyChanged;
newValue.PropertyChanged += OldLyricsEffectSettings_PropertyChanged;
oldValue.PropertyChanged -= LyricsEffectSettings_PropertyChanged;
newValue.PropertyChanged += LyricsEffectSettings_PropertyChanged;
}
private void LyricsEffectSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(LyricsEffectSettings));
}
partial void OnLyricsBackgroundSettingsChanged(LyricsBackgroundSettings oldValue, LyricsBackgroundSettings newValue)
{
oldValue.PropertyChanged -= OldLyricsBackgroundSettings_PropertyChanged;
newValue.PropertyChanged += OldLyricsBackgroundSettings_PropertyChanged;
oldValue.PropertyChanged -= LyricsBackgroundSettings_PropertyChanged;
newValue.PropertyChanged += LyricsBackgroundSettings_PropertyChanged;
}
private void LyricsBackgroundSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(LyricsBackgroundSettings));
}
partial void OnAlbumArtLayoutSettingsChanged(AlbumArtAreaStyleSettings oldValue, AlbumArtAreaStyleSettings newValue)
{
oldValue.PropertyChanged -= OldAlbumArtLayoutSettings_PropertyChanged;
newValue.PropertyChanged += OldAlbumArtLayoutSettings_PropertyChanged;
oldValue.PropertyChanged -= AlbumArtLayoutSettings_PropertyChanged;
newValue.PropertyChanged += AlbumArtLayoutSettings_PropertyChanged;
}
private void AlbumArtLayoutSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(AlbumArtLayoutSettings));
}
partial void OnAlbumArtAreaEffectSettingsChanged(AlbumArtAreaEffectSettings oldValue, AlbumArtAreaEffectSettings newValue)
{
oldValue.PropertyChanged -= AlbumArtAreaEffectSettings_PropertyChanged;
newValue.PropertyChanged += AlbumArtAreaEffectSettings_PropertyChanged;
}
private void AlbumArtAreaEffectSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
OnPropertyChanged(nameof(AlbumArtAreaEffectSettings));
}
partial void OnWindowBoundsChanged(Rect value)
{
UpdateMonitorNameAndBounds();
UpdateDemoWindowAndMonitorBounds();
WindowX = WindowBounds.X;
WindowY = WindowBounds.Y;
WindowWidth = WindowBounds.Width;
WindowHeight = WindowBounds.Height;
}
private void OldLyricsStyleSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
private void UpdateMonitorNameAndBounds(Window? targetWindow = null)
{
this.OnPropertyChanged(nameof(LyricsStyleSettings));
}
targetWindow ??= WindowHook.GetWindows<NowPlayingWindow>().FirstOrDefault(x => x.LyricsWindowStatus == this);
if (targetWindow == null) return;
private void OldLyricsEffectSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
this.OnPropertyChanged(nameof(LyricsEffectSettings));
}
private void OldLyricsBackgroundSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
this.OnPropertyChanged(nameof(LyricsBackgroundSettings));
}
private void OldAlbumArtLayoutSettings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
this.OnPropertyChanged(nameof(AlbumArtLayoutSettings));
}
public void UpdateMonitorNameAndBounds()
{
var lyricsWindow = WindowHook.GetWindow<NowPlayingWindow>();
if (lyricsWindow == null) return;
var mointor = MonitorHook.GetMonitorInfoExFromWindow(lyricsWindow);
var mointor = MonitorHook.GetMonitorInfoExFromWindow(targetWindow);
MonitorDeviceName = mointor.szDevice;
MonitorBounds = new Rect(
mointor.rcMonitor.Left,
@@ -166,17 +190,23 @@ namespace BetterLyrics.WinUI3.Models
public object Clone()
{
return new LyricsWindowStatus
return new LyricsWindowStatus(null)
{
Name = this.Name,
IsDefault = this.IsDefault,
MonitorDeviceName = this.MonitorDeviceName,
IsWorkArea = this.IsWorkArea,
IsBorderless = this.IsBorderless,
IsAlwaysOnTop = this.IsAlwaysOnTop,
IsAlwaysOnTopPolling = this.IsAlwaysOnTopPolling,
IsShownInSwitchers = this.IsShownInSwitchers,
IsClickThrough = this.IsClickThrough,
IsLocked = this.IsLocked,
IsPinToTaskbar = this.IsPinToTaskbar,
TaskbarPlacement = this.TaskbarPlacement,
IsMaximized = this.IsMaximized,
IsFullscreen = this.IsFullscreen,
LyricsLayoutOrientation = this.LyricsLayoutOrientation,
LyricsDisplayType = this.LyricsDisplayType,
WindowBounds = this.WindowBounds,
@@ -196,11 +226,6 @@ namespace BetterLyrics.WinUI3.Models
EnvironmentSampleMode = this.EnvironmentSampleMode,
AutoShowOrHideWindow = this.AutoShowOrHideWindow,
TitleBarArea = this.TitleBarArea,
WindowX = this.WindowX,
WindowY = this.WindowY,
WindowWidth = this.WindowWidth,
WindowHeight = this.WindowHeight,
};
}

View File

@@ -6,6 +6,7 @@ using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Linq;
using System.Text.Json.Serialization;
namespace BetterLyrics.WinUI3.Models
{
@@ -32,13 +33,13 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchType LyricsSearchType { get; set; } = LyricsSearchType.Sequential;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 40;
public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
[JsonIgnore] public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
public string? DisplayName => PlayerIDHelper.GetDisplayName(Provider);
[JsonIgnore] public string? DisplayName => PlayerIDHelper.GetDisplayName(Provider);
public bool IsLXMusic => PlayerIDHelper.IsLXMusic(Provider);
[JsonIgnore] public bool IsLXMusic => PlayerIDHelper.IsLXMusic(Provider);
public MediaSourceProviderInfo()
{

View File

@@ -5,10 +5,7 @@ using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using Windows.UI;
namespace BetterLyrics.WinUI3.Models

View File

@@ -1,8 +1,6 @@
using BetterLyrics.WinUI3.Enums;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Models.Settings
{

View File

@@ -13,13 +13,15 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ExitOnLyricsWindowClosed { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ListenOnNewPlaybackSource { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IgnoreCacheWhenSearching { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> BorderlessShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "B" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> ClickThroughShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "C" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> LyricsWindowSwitchShortcut { get; set; } = new List<string>() { "Ctrl", "Alt", "S" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> PlayOrPauseShortcut { get; set; } = new List<string> { "Ctrl", "Alt", "P" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> NextSongShortcut { get; set; } = new List<string> { "Ctrl", "Alt", "Right" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial List<string> PreviousSongShortcut { get; set; } = new List<string> { "Ctrl", "Alt", "Left" };
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool MultiNowPlayingWindowMode { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoStartLyricsWindow { get; set; } = true;
public GeneralSettings() { }
}
}

View File

@@ -7,9 +7,21 @@ namespace BetterLyrics.WinUI3.Models.Settings
public partial class LyricsEffectSettings : ObservableRecipient, ICloneable
{
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsBlurEffectEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsEffectScope LyricsGlowEffectScope { get; set; } = LyricsEffectScope.LongDurationSyllable;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsGlowEffectLongSyllableDuration { get; set; } = 700; // 700ms
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsGlowEffectAmountAutoAdjust { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsGlowEffectAmount { get; set; } = 8;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsScaleEffectEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScaleEffectLongSyllableDuration { get; set; } = 700; // 700ms
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsScaleEffectAmountAutoAdjust { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScaleEffectAmount { get; set; } = 115; // 115%
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLyricsFloatAnimationAmountAutoAdjust { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFloatAnimationAmount { get; set; } = 8;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial EasingType LyricsScrollEasingType { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsScrollDuration { get; set; }
@@ -40,9 +52,20 @@ namespace BetterLyrics.WinUI3.Models.Settings
return new LyricsEffectSettings(this.LyricsScrollTopDuration, this.LyricsScrollDuration, this.LyricsScrollBottomDuration, this.LyricsScrollEasingType)
{
IsLyricsBlurEffectEnabled = this.IsLyricsBlurEffectEnabled,
IsLyricsGlowEffectEnabled = this.IsLyricsGlowEffectEnabled,
LyricsGlowEffectLongSyllableDuration = this.LyricsGlowEffectLongSyllableDuration,
IsLyricsGlowEffectAmountAutoAdjust = this.IsLyricsGlowEffectAmountAutoAdjust,
LyricsGlowEffectAmount = this.LyricsGlowEffectAmount,
IsLyricsScaleEffectEnabled = this.IsLyricsScaleEffectEnabled,
LyricsScaleEffectLongSyllableDuration = this.LyricsScaleEffectLongSyllableDuration,
IsLyricsScaleEffectAmountAutoAdjust = this.IsLyricsScaleEffectAmountAutoAdjust,
LyricsScaleEffectAmount = this.LyricsScaleEffectAmount,
IsLyricsFloatAnimationEnabled = this.IsLyricsFloatAnimationEnabled,
IsLyricsFloatAnimationAmountAutoAdjust = this.IsLyricsFloatAnimationAmountAutoAdjust,
LyricsFloatAnimationAmount = this.LyricsFloatAnimationAmount,
LyricsScrollEasingType = this.LyricsScrollEasingType,
LyricsScrollDuration = this.LyricsScrollDuration,

View File

@@ -15,23 +15,27 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsFontSize { get; set; } = 24;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsFontSize { get; set; } = 12;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PhoneticLyricsOpacity { get; set; } = 60; // 60 %
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int OriginalLyricsOpacity { get; set; } = 30; // 30 %
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TranslatedLyricsOpacity { get; set; } = 60; // 60 %
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TextAlignmentType LyricsAlignmentType { get; set; } = TextAlignmentType.Left;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int LyricsFontStrokeWidth { get; set; } = 0;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomBgFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomFgFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color LyricsCustomStrokeFontColor { get; set; } = Colors.White;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsBgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsFgFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontColorType LyricsStrokeFontColorType { get; set; } = LyricsFontColorType.AdaptiveGrayed;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsFontWeight LyricsFontWeight { get; set; } = LyricsFontWeight.Bold;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial double LyricsLineSpacingFactor { get; set; } = 0.5;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = FontHelper.SystemFontFamilies.FirstOrDefault() ?? "";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsCJKFontFamily { get; set; } = "Arial";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LyricsWesternFontFamily { get; set; } = "Arial";
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayingLineTopOffset { get; set; } = 50; // 50 %
@@ -45,6 +49,11 @@ namespace BetterLyrics.WinUI3.Models.Settings
PhoneticLyricsFontSize = this.PhoneticLyricsFontSize,
OriginalLyricsFontSize = this.OriginalLyricsFontSize,
TranslatedLyricsFontSize = this.TranslatedLyricsFontSize,
PhoneticLyricsOpacity = this.PhoneticLyricsOpacity,
OriginalLyricsOpacity = this.OriginalLyricsOpacity,
TranslatedLyricsOpacity = this.TranslatedLyricsOpacity,
LyricsAlignmentType = this.LyricsAlignmentType,
LyricsFontStrokeWidth = this.LyricsFontStrokeWidth,
LyricsCustomBgFontColor = this.LyricsCustomBgFontColor,

View File

@@ -11,6 +11,8 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PlayQueueIndex { get; set; } = -1;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoOpen { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool AutoPlay { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsWindowStatus LyricsWindowStatus { get; set; } = new();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ExitOnWindowClosed { get; set; } = false;
public MusicGallerySettings() { }
}

View File

@@ -10,7 +10,6 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LibreTranslateServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTranslationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string SelectedTargetLanguageCode { get; set; } = LanguageHelper.GetDefaultTargetTranslationLanguageCode();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsJyutpingEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ChineseRomanization ChineseRomanization { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsChineseRomanizationEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsJapaneseRomanizationEnabled { get; set; } = false;

View File

@@ -73,7 +73,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
if (single)
{
LyricsDataArr.Add(new LyricsData(lrcLines));
_lyricsDataArr.Add(new LyricsData(lrcLines));
}
else
{
@@ -87,8 +87,8 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
}
// 初始化每种语言的歌词列表
int langStartIndex = LyricsDataArr.Count;
for (int i = 0; i < languageCount; i++) LyricsDataArr.Add(new LyricsData());
int langStartIndex = _lyricsDataArr.Count;
for (int i = 0; i < languageCount; i++) _lyricsDataArr.Add(new LyricsData());
// 遍历每个时间分组
if (grouped != null)
@@ -102,7 +102,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
if (langIdx < linesInGroup.Count)
{
var lyricsLine = linesInGroup[langIdx];
LyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(lyricsLine);
_lyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(lyricsLine);
}
// 没有翻译行则不补原文,直接跳过
}

View File

@@ -52,7 +52,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
}
}
LyricsDataArr.Add(new LyricsData(lyricsLines));
_lyricsDataArr.Add(new LyricsData(lyricsLines));
}
}

View File

@@ -85,14 +85,14 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
ParseTtmlXRole(spans, romanLines, "x-roman", pStartMs, pEndMs);
}
LyricsDataArr.Add(new LyricsData(originalLines));
_lyricsDataArr.Add(new LyricsData(originalLines));
if (translationLines.Count > 0)
{
LyricsDataArr.Add(new LyricsData(translationLines));
_lyricsDataArr.Add(new LyricsData(translationLines));
}
if (romanLines.Count > 0)
{
LyricsDataArr.Add(new LyricsData(romanLines) { LanguageCode = PhoneticHelper.RomanCode });
_lyricsDataArr.Add(new LyricsData(romanLines) { LanguageCode = PhoneticHelper.RomanCode });
}
}
catch

View File

@@ -4,11 +4,18 @@ using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.TranslateService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers.General;
using Lyricify.Lyrics.Parsers;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
{
@@ -16,15 +23,19 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
{
private static readonly ILogger<LyricsParser> _logger = Ioc.Default.GetRequiredService<ILogger<LyricsParser>>();
public List<LyricsData> LyricsDataArr { get; private set; } = [];
private List<LyricsData> _lyricsDataArr = [];
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
public LyricsParser()
{
}
public List<LyricsData> Parse(LyricsSearchResult? lyricsSearchResult)
{
_logger.LogInformation("LyricsParser.Parse");
LyricsDataArr = [];
_lyricsDataArr = [];
if (string.IsNullOrWhiteSpace(lyricsSearchResult?.Raw))
{
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
_lyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
}
else
{
@@ -47,14 +58,98 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
break;
}
if (LyricsDataArr.Count == 0)
if (_lyricsDataArr.Count == 0)
{
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
_lyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
}
}
LoadTranslation(lyricsSearchResult);
LoadTransliteration(lyricsSearchResult);
GenerateTransliterationLyricsData();
return _lyricsDataArr;
}
public async Task<(LyricsData, TransliterationSearchProvider?, TranslationSearchProvider?)> Parse(ITranslateService translateService, TranslationSettings settings, LyricsSearchResult? lyricsSearchResult, CancellationToken token)
{
TransliterationSearchProvider? transliterationSearchProvider = null;
TranslationSearchProvider? translationSearchProvider = null;
Parse(lyricsSearchResult);
var main = _lyricsDataArr.First();
// 应用音译
LyricsData? phoneticLyricsData = null;
if (settings.IsChineseRomanizationEnabled && main.LanguageCode == LanguageHelper.ChineseCode)
{
phoneticLyricsData = settings.ChineseRomanization switch
{
ChineseRomanization.Pinyin => _lyricsDataArr.FirstOrDefault(x => x.LanguageCode == PhoneticHelper.PinyinCode),
ChineseRomanization.Jyutping => _lyricsDataArr.FirstOrDefault(x => x.LanguageCode == PhoneticHelper.JyutpingCode),
_ => null,
};
}
else if (settings.IsJapaneseRomanizationEnabled && main.LanguageCode == LanguageHelper.JapaneseCode)
{
phoneticLyricsData = _lyricsDataArr.FirstOrDefault(x => x.LanguageCode == PhoneticHelper.RomanCode);
}
if (phoneticLyricsData != null)
{
main.SetPhoneticText(phoneticLyricsData);
if (phoneticLyricsData.AutoGenerated)
{
transliterationSearchProvider = TransliterationSearchProvider.BetterLyrics;
}
else
{
transliterationSearchProvider = lyricsSearchResult?.Provider.ToTransliterationSearchProvider();
}
}
// 应用翻译
if (settings.IsTranslationEnabled && main.LanguageCode != settings.SelectedTargetLanguageCode)
{
var found = _lyricsDataArr.FirstOrDefault(x => x.LanguageCode == settings.SelectedTargetLanguageCode);
if (found != null)
{
main.SetTranslatedText(found);
translationSearchProvider = lyricsSearchResult?.Provider.ToTranslationSearchProvider();
}
else if (settings.IsLibreTranslateEnabled)
{
string translated = string.Empty;
try
{
translated = await translateService.TranslateTextAsync(main.WrappedOriginalText, settings.SelectedTargetLanguageCode, token);
_lyricsDataArr.FirstOrDefault()?.SetTranslation(translated);
translationSearchProvider = TranslationSearchProvider.LibreTranslate;
}
catch (TaskCanceledException) { }
catch (Exception)
{
ToastHelper.ShowToast("LibreTranslateFailed", null, InfoBarSeverity.Error);
}
}
}
// 应用简体中文/繁体中文
if (main.LanguageCode == LanguageHelper.ChineseCode)
{
foreach (var item in main.LyricsLines)
{
item.OriginalText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.OriginalText) : ChineseHelper.ToSC(item.OriginalText);
}
}
if (settings.SelectedTargetLanguageCode == LanguageHelper.ChineseCode)
{
foreach (var item in main.LyricsLines)
{
item.TranslatedText = settings.IsTraditionalChineseEnabled ? ChineseHelper.ToTC(item.TranslatedText) : ChineseHelper.ToSC(item.TranslatedText);
}
}
return (main, transliterationSearchProvider, translationSearchProvider);
}
private void LoadTranslation(LyricsSearchResult? lyricsSearchResult)
@@ -82,7 +177,7 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
{
case LyricsSearchProvider.Netease:
ParseLrc(lyricsSearchResult.Transliteration, true);
LyricsDataArr.LastOrDefault()?.LanguageCode = PhoneticHelper.RomanCode;
_lyricsDataArr.LastOrDefault()?.LanguageCode = PhoneticHelper.RomanCode;
break;
default:
break;
@@ -95,15 +190,15 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
/// </summary>
private void GenerateTransliterationLyricsData()
{
var main = LyricsDataArr.FirstOrDefault();
var main = _lyricsDataArr.FirstOrDefault();
if (main != null)
{
string? languageCode = main.LanguageCode;
if (languageCode == LanguageHelper.ChineseCode)
{
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.PinyinCode))
if (!_lyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.PinyinCode))
{
LyricsDataArr.Add(new LyricsData
_lyricsDataArr.Add(new LyricsData
{
LanguageCode = PhoneticHelper.PinyinCode,
AutoGenerated = true,
@@ -122,9 +217,9 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
}).ToList()
});
}
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.JyutpingCode))
if (!_lyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.JyutpingCode))
{
LyricsDataArr.Add(new LyricsData
_lyricsDataArr.Add(new LyricsData
{
LanguageCode = PhoneticHelper.JyutpingCode,
AutoGenerated = true,
@@ -146,9 +241,9 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
}
else if (languageCode == LanguageHelper.JapaneseCode)
{
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.RomanCode))
if (!_lyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.RomanCode))
{
LyricsDataArr.Add(new LyricsData
_lyricsDataArr.Add(new LyricsData
{
LanguageCode = PhoneticHelper.RomanCode,
AutoGenerated = true,
@@ -170,5 +265,6 @@ namespace BetterLyrics.WinUI3.Parsers.LyricsParser
}
}
}
}
}

View File

@@ -0,0 +1,165 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System;
using System.Numerics;
namespace BetterLyrics.WinUI3.Renderer
{
public partial class CoverBackgroundRenderer : IDisposable
{
private CanvasBitmap? _currentBitmap;
private CanvasBitmap? _previousBitmap;
private readonly ValueTransition<double> _crossfadeTransition;
private float _rotationAngle = 0f;
public bool IsEnabled { get; set; } = false;
public int Opacity { get; set; } = 100;
public int BlurAmount { get; set; } = 100;
public int Speed { get; set; } = 100;
public CoverBackgroundRenderer()
{
_crossfadeTransition = new ValueTransition<double>(1.0, 0.7, easingType: EasingType.Linear);
}
public void SetCoverBitmap(CanvasBitmap? newBitmap)
{
if (_currentBitmap == newBitmap) return;
if (_currentBitmap == null)
{
_currentBitmap = newBitmap;
_crossfadeTransition.StartTransition(1.0, jumpTo: true);
return;
}
_previousBitmap = _currentBitmap;
_currentBitmap = newBitmap;
if (newBitmap != null)
{
_crossfadeTransition.Reset(0.0);
_crossfadeTransition.StartTransition(1.0);
}
else
{
_previousBitmap = null;
_crossfadeTransition.StartTransition(1.0, jumpTo: true);
}
}
public void Update(TimeSpan deltaTime)
{
if (!IsEnabled) return;
_crossfadeTransition.Update(deltaTime);
if (Speed > 0)
{
float baseSpeed = 0.6f; // 弧度/秒
float currentSpeed = (Speed / 100.0f) * baseSpeed;
_rotationAngle += currentSpeed * (float)deltaTime.TotalSeconds;
_rotationAngle %= (float)(2 * Math.PI);
}
if (_crossfadeTransition.Value >= 1.0 && _previousBitmap != null)
{
_previousBitmap = null;
}
}
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)
{
if (!IsEnabled || Opacity <= 0) return;
float baseAlpha = Opacity / 100.0f;
float currentBlur = BlurAmount;
float angle = Speed > 0 ? _rotationAngle : 0f;
double fadeProgress = _crossfadeTransition.Value;
bool isCrossfading = fadeProgress < 1.0 && _previousBitmap != null;
if (isCrossfading)
{
DrawLayer(ds, control.Size, _previousBitmap, angle, currentBlur, baseAlpha);
float newLayerAlpha = baseAlpha * (float)fadeProgress;
if (newLayerAlpha > 0.005f)
{
DrawLayer(ds, control.Size, _currentBitmap, angle, currentBlur, newLayerAlpha);
}
}
else if (_currentBitmap != null)
{
DrawLayer(ds, control.Size, _currentBitmap, angle, currentBlur, baseAlpha);
}
}
private void DrawLayer(CanvasDrawingSession ds, Windows.Foundation.Size screenSize, CanvasBitmap? bitmap, float rotationRadians, float blurAmount, float alpha)
{
if (bitmap == null) return;
float imgW = bitmap.SizeInPixels.Width;
float imgH = bitmap.SizeInPixels.Height;
Vector2 screenCenter = new Vector2((float)screenSize.Width / 2f, (float)screenSize.Height / 2f);
float scale;
if (Speed > 0 && Math.Abs(rotationRadians) > 0.001f)
{
float screenDiagonal = (float)Math.Sqrt(screenSize.Width * screenSize.Width + screenSize.Height * screenSize.Height);
float scaleX = screenDiagonal / imgW;
float scaleY = screenDiagonal / imgH;
scale = Math.Max(scaleX, scaleY);
}
else
{
float scaleX = (float)screenSize.Width / imgW;
float scaleY = (float)screenSize.Height / imgH;
scale = Math.Max(scaleX, scaleY);
}
// 缩放图片 -> 将图片中心移动到 (0,0) 以便旋转 -> 旋转 ->将图片移回屏幕中心
Vector2 imgCenterOffset = new Vector2(
((float)screenSize.Width - imgW * scale) / 2.0f,
((float)screenSize.Height - imgH * scale) / 2.0f
);
Matrix3x2 transform =
Matrix3x2.CreateScale(scale) * Matrix3x2.CreateTranslation(imgCenterOffset) * Matrix3x2.CreateRotation(rotationRadians, screenCenter);
using (var transformEffect = new Transform2DEffect())
using (var blurEffect = new GaussianBlurEffect())
{
transformEffect.Source = bitmap;
transformEffect.TransformMatrix = transform;
transformEffect.InterpolationMode = CanvasImageInterpolation.Linear;
blurEffect.Source = transformEffect;
blurEffect.BlurAmount = blurAmount > 0 ? (blurAmount / 2.0f) : 0f;
blurEffect.BorderMode = EffectBorderMode.Hard;
ds.DrawImage(blurEffect, 0, 0, new Windows.Foundation.Rect(0, 0, screenSize.Width, screenSize.Height), alpha);
}
}
public void Dispose()
{
_currentBitmap?.Dispose();
_currentBitmap = null;
_previousBitmap?.Dispose();
_previousBitmap = null;
}
}
}

View File

@@ -63,14 +63,14 @@ namespace BetterLyrics.WinUI3.Renderer
_timeAccumulator += (float)deltaTime.TotalSeconds;
_fluidEffect.Properties["iTime"] = _timeAccumulator;
_fluidEffect?.Properties["iTime"] = _timeAccumulator;
_fluidEffect.Properties["color1"] = _c1;
_fluidEffect.Properties["color2"] = _c2;
_fluidEffect.Properties["color3"] = _c3;
_fluidEffect.Properties["color4"] = _c4;
_fluidEffect?.Properties["color1"] = _c1;
_fluidEffect?.Properties["color2"] = _c2;
_fluidEffect?.Properties["color3"] = _c3;
_fluidEffect?.Properties["color4"] = _c4;
_fluidEffect.Properties["EnableLightWave"] = EnableLightWave;
_fluidEffect?.Properties["EnableLightWave"] = EnableLightWave;
}
public void Draw(ICanvasAnimatedControl control, CanvasDrawingSession ds)

View File

@@ -135,7 +135,8 @@ namespace BetterLyrics.WinUI3.Renderer
var effectSettings = windowStatus.LyricsEffectSettings;
var styleSettings = windowStatus.LyricsStyleSettings;
var rotationY = currentPlayingLine.OriginalPosition.WithX(effectSettings.FanLyricsAngle < 0 ? (float)lyricsWidth : 0);
var rotationX = effectSettings.FanLyricsAngle < 0 ? lyricsWidth : 0;
rotationX += lyricsWidth / 2 * (effectSettings.FanLyricsAngle < 0 ? 1 : -1);
for (int i = startVisibleIndex; i <= endVisibleIndex; i++)
{
@@ -145,14 +146,19 @@ namespace BetterLyrics.WinUI3.Renderer
if (line.OriginalCanvasTextLayout == null) continue;
if (line.OriginalCanvasTextLayout.LayoutBounds.Width <= 0) continue;
double xOffset = lyricsX;
double yOffset = line.YOffsetTransition.Value + userScrollOffset + lyricsY + lyricsHeight * playingLineTopOffsetFactor;
var transform =
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition) *
Matrix3x2.CreateRotation((float)line.AngleTransition.Value, rotationY) *
Matrix3x2.CreateTranslation((float)lyricsX, (float)yOffset);
ds.Transform = Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition);
ds.Transform = transform;
if (effectSettings.IsFanLyricsEnabled)
{
xOffset += Math.Abs(line.AngleTransition.Value) / (Math.PI / 2) * lyricsWidth / 2 * (effectSettings.FanLyricsAngle < 0 ? 1 : -1);
var rotationY = line.CenterPosition.Y;
ds.Transform *= Matrix3x2.CreateRotation((float)line.AngleTransition.Value, new Vector2((float)rotationX, rotationY));
}
ds.Transform *= Matrix3x2.CreateTranslation((float)xOffset, (float)yOffset);
using (var textOnlyLayer = RenderBaseTextLayer(control, line, styleSettings.LyricsFontStrokeWidth, strokeColor, line.ColorTransition.Value))
{

View File

@@ -131,7 +131,6 @@ namespace BetterLyrics.WinUI3.Renderer
LinePlaybackState state,
LyricsEffectSettings settings)
{
var blur = line.BlurAmountTransition.Value;
var playedOpacity = line.PlayedOriginalOpacityTransition.Value;
var unplayedOpacity = line.UnplayedOriginalOpacityTransition.Value;
@@ -191,6 +190,16 @@ namespace BetterLyrics.WinUI3.Renderer
}
}
/// <summary>
///
/// </summary>
/// <param name="ds"></param>
/// <param name="line"></param>
/// <param name="charIndex">遍历的字符索引</param>
/// <param name="exactProgressIndex">当前播放字符相对于整行的索引</param>
/// <param name="source"></param>
/// <param name="state"></param>
/// <param name="settings"></param>
private void DrawSingleCharacter(
CanvasDrawingSession ds,
RenderLyricsLine line,
@@ -223,7 +232,16 @@ namespace BetterLyrics.WinUI3.Renderer
if (settings.IsLyricsFloatAnimationEnabled)
{
double targetFloatOffset = sourceCharRect.Height * 0.1;
double targetFloatOffset;
if (settings.IsLyricsFloatAnimationAmountAutoAdjust)
{
targetFloatOffset = sourceCharRect.Height * 0.1;
}
else
{
targetFloatOffset = settings.LyricsFloatAnimationAmount;
}
// 已经浮完了的
if (charIndex < curCharIndexInt)
{
@@ -240,22 +258,74 @@ namespace BetterLyrics.WinUI3.Renderer
{
floatOffset = -targetFloatOffset;
}
// 制造句间上浮过度动画,这里用任何一个 Transition 都行,主要是获取当前行的进入视野的 Progress
floatOffset *= line.YOffsetTransition.Progress;
}
var parentSyllable = line.LyricsSyllables.FirstOrDefault(x => x.StartIndex <= charIndex && charIndex < x.StartIndex + x.Text.Length);
if (parentSyllable != null && parentSyllable.IsLongDuration && parentSyllable.StartIndex == state.SyllableStartIndex)
if (settings.IsLyricsScaleEffectEnabled)
{
if (settings.IsLyricsScaleEffectEnabled)
if (parentSyllable != null && parentSyllable.StartIndex == state.SyllableStartIndex)
{
scale += Math.Sin(state.SyllableProgress * Math.PI) * 0.15;
if (parentSyllable.DurationMs >= settings.LyricsScaleEffectLongSyllableDuration)
{
if (settings.IsLyricsScaleEffectAmountAutoAdjust)
{
scale += Math.Sin(state.SyllableProgress * Math.PI) * 0.15;
}
else
{
scale += Math.Sin(state.SyllableProgress * Math.PI) * (settings.LyricsScaleEffectAmount / 100.0 - 1);
}
}
}
if (settings.IsLyricsGlowEffectEnabled)
}
if (settings.IsLyricsGlowEffectEnabled)
{
double maxGlow;
if (settings.IsLyricsGlowEffectAmountAutoAdjust)
{
glow = Math.Sin(state.SyllableProgress * Math.PI) * sourceCharRect.Height * 0.2;
drawGlow = true;
maxGlow = sourceCharRect.Height * 0.2;
}
else
{
maxGlow = settings.LyricsGlowEffectAmount;
}
switch (settings.LyricsGlowEffectScope)
{
case Enums.LyricsEffectScope.LongDurationSyllable:
if (parentSyllable != null && parentSyllable.StartIndex == state.SyllableStartIndex)
{
if (parentSyllable.DurationMs >= settings.LyricsGlowEffectLongSyllableDuration)
{
glow = maxGlow * Math.Sin(state.SyllableProgress * Math.PI);
drawGlow = true;
}
}
break;
case Enums.LyricsEffectScope.LineStartToCurrentChar:
// 已经唱了的
if (charIndex < curCharIndexInt)
{
glow = maxGlow;
drawGlow = true;
}
// 正在唱的
else if (charIndex == curCharIndexInt)
{
var p = exactProgressIndex - curCharIndexInt;
glow = p * maxGlow;
drawGlow = true;
}
// 还没唱的
else { }
glow *= Math.Clamp(line.OriginalText.Length - exactProgressIndex, 0, 1);
break;
default:
break;
}
}

View File

@@ -1,10 +0,0 @@
using BetterLyrics.WinUI3.Models;
namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
public interface ILiveStatesService
{
LiveStates LiveStates { get; set; }
void InitLyricsWindowStatus();
}
}

View File

@@ -1,154 +0,0 @@
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System.Linq;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services.LiveStatesService
{
public partial class LiveStatesService : BaseViewModel, ILiveStatesService,
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>
{
private readonly ISettingsService _settingsService;
public LiveStates LiveStates { get; set; } = new LiveStates();
public LiveStatesService(ISettingsService settingsService)
{
_settingsService = settingsService;
}
public void InitLyricsWindowStatus()
{
var defaultLyricsWindowStatus = _settingsService.AppSettings.WindowBoundsRecords.FirstOrDefault(x => x.IsDefault);
if (defaultLyricsWindowStatus == null)
{
defaultLyricsWindowStatus = LyricsWindowStatusExtensions.StandardMode();
defaultLyricsWindowStatus.IsDefault = true;
_settingsService.AppSettings.WindowBoundsRecords.Add(defaultLyricsWindowStatus);
_settingsService.AppSettings.WindowBoundsRecords.Add(LyricsWindowStatusExtensions.DesktopMode());
_settingsService.AppSettings.WindowBoundsRecords.Add(LyricsWindowStatusExtensions.DockedMode());
_settingsService.AppSettings.WindowBoundsRecords.Add(LyricsWindowStatusExtensions.NarrowMode());
_settingsService.AppSettings.WindowBoundsRecords.Add(LyricsWindowStatusExtensions.FullscreenMode());
}
LiveStates.LyricsWindowStatus = defaultLyricsWindowStatus;
}
private async void RefreshLyricsWindowStatus()
{
LiveStates.IsLyricsWindowStatusRefreshing = true;
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
WindowHook.SetIsWorkArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
WindowHook.UpdateWorkArea<NowPlayingWindow>();
}
await Task.Delay(300);
WindowHook.SetIsShowInSwitchers<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
WindowHook.SetIsAlwaysOnTop<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
WindowHook.SetIsClickThrough<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
WindowHook.SetIsBorderless<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
WindowHook.SetTitleBarArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
// 下述代码可以删除,但是为了避免给用户造成操作上的疑虑,暂时保留
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
LiveStates.LyricsWindowStatus.WindowBounds = LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea();
}
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds);
LiveStates.LyricsWindowStatus.WindowX = LiveStates.LyricsWindowStatus.WindowBounds.X;
LiveStates.LyricsWindowStatus.WindowY = LiveStates.LyricsWindowStatus.WindowBounds.Y;
LiveStates.LyricsWindowStatus.WindowWidth = LiveStates.LyricsWindowStatus.WindowBounds.Width;
LiveStates.LyricsWindowStatus.WindowHeight = LiveStates.LyricsWindowStatus.WindowBounds.Height;
LiveStates.LyricsWindowStatus.UpdateDemoWindowAndMonitorBounds();
LiveStates.IsLyricsWindowStatusRefreshing = false;
}
private void LyricsWindowStatus_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(LyricsWindowStatus.IsWorkArea):
LiveStates.IsLyricsWindowStatusRefreshing = true;
WindowHook.SetIsWorkArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsWorkArea);
LiveStates.IsLyricsWindowStatusRefreshing = false;
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
}
break;
case nameof(LyricsWindowStatus.DockHeight):
case nameof(LyricsWindowStatus.DockPlacement):
case nameof(LyricsWindowStatus.MonitorDeviceName):
LiveStates.LyricsWindowStatus.UpdateMonitorBounds();
if (LiveStates.LyricsWindowStatus.IsWorkArea)
{
LiveStates.IsLyricsWindowStatusRefreshing = true;
WindowHook.UpdateWorkArea<NowPlayingWindow>();
LiveStates.IsLyricsWindowStatusRefreshing = false;
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.GetWindowBoundsWhenWorkArea());
}
break;
case nameof(LyricsWindowStatus.IsShownInSwitchers):
WindowHook.SetIsShowInSwitchers<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsShownInSwitchers);
break;
case nameof(LyricsWindowStatus.IsAlwaysOnTop):
WindowHook.SetIsAlwaysOnTop<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsAlwaysOnTop);
break;
case nameof(LyricsWindowStatus.IsClickThrough):
WindowHook.SetIsClickThrough<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsClickThrough);
break;
case nameof(LyricsWindowStatus.IsBorderless):
WindowHook.SetIsBorderless<NowPlayingWindow>(LiveStates.LyricsWindowStatus.IsBorderless);
break;
case nameof(LyricsWindowStatus.WindowX):
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithX(LiveStates.LyricsWindowStatus.WindowX));
break;
case nameof(LyricsWindowStatus.WindowY):
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithY(LiveStates.LyricsWindowStatus.WindowY));
break;
case nameof(LyricsWindowStatus.WindowWidth):
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithWidth(LiveStates.LyricsWindowStatus.WindowWidth));
break;
case nameof(LyricsWindowStatus.WindowHeight):
WindowHook.MoveAndResize<NowPlayingWindow>(LiveStates.LyricsWindowStatus.WindowBounds.WithHeight(LiveStates.LyricsWindowStatus.WindowHeight));
break;
case nameof(LyricsWindowStatus.TitleBarArea):
WindowHook.SetTitleBarArea<NowPlayingWindow>(LiveStates.LyricsWindowStatus.TitleBarArea);
break;
case nameof(LyricsWindowStatus.AutoShowOrHideWindow):
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
break;
default:
break;
}
}
public void Receive(PropertyChangedMessage<LyricsWindowStatus> message)
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
{
message.OldValue.PropertyChanged -= LyricsWindowStatus_PropertyChanged;
message.NewValue.PropertyChanged += LyricsWindowStatus_PropertyChanged;
RefreshLyricsWindowStatus();
}
}
}
}
}

View File

@@ -40,7 +40,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
_lrcLibHttpClient = new();
_lrcLibHttpClient.DefaultRequestHeaders.Add(
"User-Agent",
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GitHub})"
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.BetterLyricsGitHub})"
);
_amllTtmlDbHttpClient = new();
_appleMusic = new AppleMusic();

View File

@@ -1,19 +1,16 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Models;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Windows.UI;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
public interface IMediaSessionsService : INotifyPropertyChanged
{
event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
Task PlayAsync();
Task PauseAsync();
Task PreviousAsync();
@@ -22,9 +19,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
Task ChangeLyricsLine(int index);
void UpdateLyrics();
void UpdateTranslations();
void InitPlaybackShortcuts();
MediaSourceProviderInfo? CurrentMediaSourceProviderInfo { get; }
@@ -34,9 +28,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
LyricsData? CurrentLyricsData { get; }
BitmapImage? AlbumArtBitmapImage { get; }
AlbumArtThemeColors AlbumArtThemeColors { get; }
TranslationSearchProvider? TranslationSearchProvider { get; }
AlbumArtThemeColors CalculateAlbumArtThemeColors(LyricsWindowStatus lyricsWindowStatus, Color backdropAccentColor);
LyricsSearchResult? CurrentLyricsSearchResult { get; }
}
}

View File

@@ -22,12 +22,14 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
private Color _envColor = Colors.Transparent;
private List<Color> _lightAccentColors = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _darkAccentColors = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _lightAccentColorsMedianCut = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _darkAccentColorsMedianCut = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _lightAccentColorsOctTree = Enumerable.Repeat(Colors.Black, 4).ToList();
private List<Color> _darkAccentColorsOctTree = Enumerable.Repeat(Colors.Black, 4).ToList();
private BitmapDecoder? _albumArtBitmapDecoder = null;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial BitmapImage? AlbumArtBitmapImage { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial AlbumArtThemeColors AlbumArtThemeColors { get; set; } = new();
private void UpdateAlbumArt()
{
@@ -47,8 +49,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
IBuffer? buffer = await Task.Run(async () => await _albumArtSearchService.SearchAsync(CurrentSongInfo, _SMTCAlbumArtBuffer, token), token);
if (token.IsCancellationRequested) return;
BitmapDecoder? decoder = null;
if (buffer == null)
{
using var placeHolderStream = await ImageHelper.GetAlbumArtPlaceholderAsync();
@@ -59,39 +59,55 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
buffer = tempBuffer;
}
decoder = await ImageHelper.MakeSquareWithThemeColor(buffer, _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.PaletteGeneratorType);
_albumArtBitmapDecoder = await ImageHelper.GetBitmapDecoder(buffer);
if (token.IsCancellationRequested) return;
var lightPalette = await ImageHelper.GetAccentColorsAsync(decoder, 4, _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.PaletteGeneratorType, false);
var darkPalette = await ImageHelper.GetAccentColorsAsync(decoder, 4, _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.PaletteGeneratorType, true);
if (token.IsCancellationRequested) return;
_lightAccentColorsMedianCut =
(await ImageHelper.GetAccentColorsAsync(_albumArtBitmapDecoder, 4, PaletteGeneratorType.MedianCut, false))
.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
_darkAccentColorsMedianCut =
(await ImageHelper.GetAccentColorsAsync(_albumArtBitmapDecoder, 4, PaletteGeneratorType.MedianCut, true))
.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
_lightAccentColorsOctTree =
(await ImageHelper.GetAccentColorsAsync(_albumArtBitmapDecoder, 4, PaletteGeneratorType.OctTree, false))
.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
_darkAccentColorsOctTree =
(await ImageHelper.GetAccentColorsAsync(_albumArtBitmapDecoder, 4, PaletteGeneratorType.OctTree, true))
.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
var bitmapImage = new BitmapImage();
await bitmapImage.SetSourceAsync(ImageHelper.ToIRandomAccessStream(buffer));
if (token.IsCancellationRequested) return;
_lightAccentColors = lightPalette.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
_darkAccentColors = darkPalette.Palette.Select(Helper.ColorHelper.FromVector3).ToList();
UpdateAlbumArtThemeColors();
AlbumArtBitmapImage = bitmapImage;
}
private void UpdateAlbumArtThemeColors()
public AlbumArtThemeColors CalculateAlbumArtThemeColors(LyricsWindowStatus lyricsWindowStatus, Color backdropAccentColor)
{
var status = _liveStatesService.LiveStates.LyricsWindowStatus;
var lightAccentColors = lyricsWindowStatus.LyricsBackgroundSettings.PaletteGeneratorType switch
{
PaletteGeneratorType.MedianCut => _lightAccentColorsMedianCut,
PaletteGeneratorType.OctTree => _lightAccentColorsOctTree,
_ => _lightAccentColorsMedianCut,
};
var darkAccentColors = lyricsWindowStatus.LyricsBackgroundSettings.PaletteGeneratorType switch
{
PaletteGeneratorType.MedianCut => _darkAccentColorsMedianCut,
PaletteGeneratorType.OctTree => _darkAccentColorsOctTree,
_ => _darkAccentColorsMedianCut,
};
var result = new AlbumArtThemeColors();
result.EnvColor = _envColor;
result.EnvColor = backdropAccentColor;
ElementTheme themeTypeSent;
if (status.IsAdaptToEnvironment)
if (lyricsWindowStatus.IsAdaptToEnvironment)
{
themeTypeSent = Helper.ColorHelper.GetElementThemeFromBackgroundColor(result.EnvColor);
}
else
{
themeTypeSent = status.LyricsBackgroundSettings.LyricsBackgroundTheme;
themeTypeSent = lyricsWindowStatus.LyricsBackgroundSettings.LyricsBackgroundTheme;
}
bool isLight = themeTypeSent switch
@@ -115,10 +131,10 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
// brightness = 0.7f;
grayedEnvironmentalColor = lightColor;
result.AccentColor1 = _lightAccentColors.ElementAtOrDefault(0);
result.AccentColor2 = _lightAccentColors.ElementAtOrDefault(1);
result.AccentColor3 = _lightAccentColors.ElementAtOrDefault(2);
result.AccentColor4 = _lightAccentColors.ElementAtOrDefault(3);
result.AccentColor1 = lightAccentColors.ElementAtOrDefault(0);
result.AccentColor2 = lightAccentColors.ElementAtOrDefault(1);
result.AccentColor3 = lightAccentColors.ElementAtOrDefault(2);
result.AccentColor4 = lightAccentColors.ElementAtOrDefault(3);
}
else
{
@@ -126,78 +142,53 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
// brightness = 0.3f;
grayedEnvironmentalColor = darkColor;
result.AccentColor1 = _darkAccentColors.ElementAtOrDefault(0);
result.AccentColor2 = _darkAccentColors.ElementAtOrDefault(1);
result.AccentColor3 = _darkAccentColors.ElementAtOrDefault(2);
result.AccentColor4 = _darkAccentColors.ElementAtOrDefault(3);
result.AccentColor1 = darkAccentColors.ElementAtOrDefault(0);
result.AccentColor2 = darkAccentColors.ElementAtOrDefault(1);
result.AccentColor3 = darkAccentColors.ElementAtOrDefault(2);
result.AccentColor4 = darkAccentColors.ElementAtOrDefault(3);
}
if (status.IsAdaptToEnvironment)
if (lyricsWindowStatus.IsAdaptToEnvironment)
{
adaptiveColoredFontColor = Helper.ColorHelper.GetForegroundColor(result.EnvColor);
}
else
{
if (isLight)
adaptiveColoredFontColor = _darkAccentColors.ElementAtOrDefault(0);
adaptiveColoredFontColor = darkAccentColors.ElementAtOrDefault(0);
else
adaptiveColoredFontColor = _lightAccentColors.ElementAtOrDefault(0);
adaptiveColoredFontColor = lightAccentColors.ElementAtOrDefault(0);
}
result.ThemeType = themeTypeSent;
// 背景字色
switch (status.LyricsStyleSettings.LyricsBgFontColorType)
result.BgFontColor = lyricsWindowStatus.LyricsStyleSettings.LyricsBgFontColorType switch
{
case LyricsFontColorType.AdaptiveGrayed:
result.BgFontColor = adaptiveGrayedFontColor;
break;
case LyricsFontColorType.AdaptiveColored:
result.BgFontColor = adaptiveColoredFontColor ?? adaptiveGrayedFontColor;
break;
case LyricsFontColorType.Custom:
result.BgFontColor = status.LyricsStyleSettings.LyricsCustomBgFontColor;
break;
default:
result.BgFontColor = adaptiveGrayedFontColor;
break;
}
LyricsFontColorType.AdaptiveGrayed => adaptiveGrayedFontColor,
LyricsFontColorType.AdaptiveColored => adaptiveColoredFontColor ?? adaptiveGrayedFontColor,
LyricsFontColorType.Custom => lyricsWindowStatus.LyricsStyleSettings.LyricsCustomBgFontColor,
_ => adaptiveGrayedFontColor,
};
// 前景字色
switch (status.LyricsStyleSettings.LyricsFgFontColorType)
result.FgFontColor = lyricsWindowStatus.LyricsStyleSettings.LyricsFgFontColorType switch
{
case LyricsFontColorType.AdaptiveGrayed:
result.FgFontColor = adaptiveGrayedFontColor;
break;
case LyricsFontColorType.AdaptiveColored:
result.FgFontColor = adaptiveColoredFontColor ?? adaptiveGrayedFontColor;
break;
case LyricsFontColorType.Custom:
result.FgFontColor = status.LyricsStyleSettings.LyricsCustomFgFontColor;
break;
default:
result.FgFontColor = adaptiveGrayedFontColor;
break;
}
LyricsFontColorType.AdaptiveGrayed => adaptiveGrayedFontColor,
LyricsFontColorType.AdaptiveColored => adaptiveColoredFontColor ?? adaptiveGrayedFontColor,
LyricsFontColorType.Custom => lyricsWindowStatus.LyricsStyleSettings.LyricsCustomFgFontColor,
_ => adaptiveGrayedFontColor,
};
// 描边颜色
switch (status.LyricsStyleSettings.LyricsStrokeFontColorType)
result.StrokeFontColor = lyricsWindowStatus.LyricsStyleSettings.LyricsStrokeFontColorType switch
{
case LyricsFontColorType.AdaptiveGrayed:
result.StrokeFontColor = grayedEnvironmentalColor.WithBrightness(0.7);
break;
case LyricsFontColorType.AdaptiveColored:
result.StrokeFontColor = result.EnvColor.WithBrightness(0.7);
break;
case LyricsFontColorType.Custom:
result.StrokeFontColor = status.LyricsStyleSettings.LyricsCustomStrokeFontColor;
break;
default:
result.StrokeFontColor = Colors.Transparent;
break;
}
AlbumArtThemeColors = result;
LyricsFontColorType.AdaptiveGrayed => grayedEnvironmentalColor.WithBrightness(0.7),
LyricsFontColorType.AdaptiveColored => result.EnvColor.WithBrightness(0.7),
LyricsFontColorType.Custom => lyricsWindowStatus.LyricsStyleSettings.LyricsCustomStrokeFontColor,
_ => Colors.Transparent,
};
return result;
}
}

View File

@@ -1,16 +1,9 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Parsers.LyricsParser;
using CommunityToolkit.Mvvm.ComponentModel;
using Lyricify.Lyrics.Helpers.General;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -19,188 +12,50 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public partial class MediaSessionsService : IMediaSessionsService
{
private LatestOnlyTaskRunner _refreshLyricsRunner = new();
private LatestOnlyTaskRunner _refreshTranslationRunner = new();
private int _langIndex = 0;
private List<LyricsData> _lyricsDataArr = [];
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsData? CurrentLyricsData { get; private set; }
public event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
[ObservableProperty] public partial LyricsSearchResult? CurrentLyricsSearchResult { get; private set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSearchProvider? TranslationSearchProvider { get; private set; }
[ObservableProperty] public partial bool IsTranslating { get; set; } = false;
private void SetCurrentLyricsData()
{
CurrentLyricsData = _lyricsDataArr.ElementAtOrDefault(_langIndex);
}
private async Task RefreshTranslationAsync(CancellationToken token)
{
TranslationSearchProvider = null;
_lyricsDataArr.ElementAtOrDefault(0)?.ClearTranslatedText();
App.Current.Resources.DispatcherQueue.TryEnqueue(SetCurrentLyricsData);
IsTranslating = true;
await SetPhoneticTextAsync(token);
await SetTranslatedTextAsync(token);
if (token.IsCancellationRequested) return;
IsTranslating = false;
App.Current.Resources.DispatcherQueue.TryEnqueue(SetCurrentLyricsData);
}
private async Task SetTranslatedTextAsync(CancellationToken token)
{
if (!_settingsService.AppSettings.TranslationSettings.IsTranslationEnabled) return;
_logger.LogInformation("SetTranslatedTextAsync");
string targetLangCode = _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode;
_logger.LogInformation("Target language code: {TargetLangCode}", targetLangCode);
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
if (originalText == null) return;
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
_logger.LogInformation("Original language code: {OriginalLangCode}", originalLangCode);
if (originalLangCode == targetLangCode)
{
_logger.LogInformation("Original lyrics already in target language: {TargetLangCode}", targetLangCode);
_lyricsDataArr.FirstOrDefault()?.ClearTranslatedText();
}
else
{
// Try get translation from itself first
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetLangCode);
if (found >= 0)
{
_logger.LogInformation("Found translated text in lyrics data at index {FoundIndex}", found);
_lyricsDataArr.FirstOrDefault()?.SetTranslatedText(_lyricsDataArr[found], 50);
TranslationSearchProvider = CurrentLyricsSearchResult?.Provider.ToTranslationSearchProvider();
}
else if (_settingsService.AppSettings.TranslationSettings.IsLibreTranslateEnabled)
{
_logger.LogInformation("LibreTranslate is enabled, trying to translate lyrics...");
string translated = string.Empty;
try
{
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
if (token.IsCancellationRequested) return;
if (translated == string.Empty) return;
_lyricsDataArr.FirstOrDefault()?.SetTranslation(translated);
TranslationSearchProvider = Enums.TranslationSearchProvider.LibreTranslate;
}
catch (Exception)
{
ToastHelper.ShowToast("LibreTranslateFailed", null, InfoBarSeverity.Error);
}
}
}
}
private async Task SetPhoneticTextAsync(CancellationToken token)
{
_logger.LogInformation("Showing phonetic text for lyrics...");
string targetPhoneticCode = "";
_logger.LogInformation("Target phonetic code: {TargetPhonetic}", targetPhoneticCode);
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
if (originalText == null) return;
string? originalLangCode = LanguageHelper.DetectLanguageCode(originalText);
_logger.LogInformation("Original phonetic code: {OriginalLangCode}", originalLangCode);
if (originalLangCode == "zh" && _settingsService.AppSettings.TranslationSettings.IsChineseRomanizationEnabled)
{
targetPhoneticCode = _settingsService.AppSettings.TranslationSettings.ChineseRomanization.ToPhoneticCode();
}
else if (originalLangCode == "ja" && _settingsService.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled)
{
targetPhoneticCode = PhoneticHelper.RomanCode;
}
if (targetPhoneticCode == "")
{
_lyricsDataArr.FirstOrDefault()?.ClearPhoneticText();
}
// Try get phonetic text from itself
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr, targetPhoneticCode);
if (found >= 0)
{
_logger.LogInformation("Found phonetic text in lyrics data at index {FoundIndex}", found);
_lyricsDataArr.FirstOrDefault()?.SetPhoneticText(_lyricsDataArr[found], 50);
}
}
private async Task RefreshLyricsAsync(CancellationToken token)
{
_logger.LogInformation("RefreshLyricsAsync");
CurrentLyricsSearchResult = null;
_lyricsDataArr = [LyricsData.GetLoadingPlaceholder()];
App.Current.Resources.DispatcherQueue.TryEnqueue(SetCurrentLyricsData);
CurrentLyricsData = LyricsData.GetLoadingPlaceholder();
if (CurrentSongInfo != null)
{
CurrentLyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(
CurrentSongInfo,
!_settingsService.AppSettings.GeneralSettings.IgnoreCacheWhenSearching,
true,
CurrentMediaSourceProviderInfo?.LyricsSearchType,
token),
token);
if (token.IsCancellationRequested) return;
var lyricsParser = new LyricsParser();
lyricsParser.Parse(CurrentSongInfo, CurrentLyricsSearchResult);
_lyricsDataArr = lyricsParser.LyricsDataArr;
ApplyChinesePreference();
}
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _lyricsDataArr.Count);
// Show original first while loading phonetic and translated
ApplyChinesePreference();
App.Current.Resources.DispatcherQueue.TryEnqueue(SetCurrentLyricsData);
UpdateTranslations();
}
private void ApplyChinesePreference()
{
var traditionalChinesePreferred = _settingsService.AppSettings.TranslationSettings.IsTraditionalChineseEnabled;
var found = _lyricsDataArr.FindIndex(x => x.LanguageCode == "zh");
if (found >= 0)
{
foreach (var item in _lyricsDataArr[found].LyricsLines)
if (CurrentLyricsSearchResult != null)
{
item.OriginalText = traditionalChinesePreferred ? ChineseHelper.ToTC(item.OriginalText) : ChineseHelper.ToSC(item.OriginalText);
var lyricsParser = new LyricsParser();
(CurrentLyricsData, CurrentLyricsSearchResult.TransliterationProvider, CurrentLyricsSearchResult.TranslationProvider) =
await Task.Run(async () => await lyricsParser.Parse(
_translateService,
_settingsService.AppSettings.TranslationSettings,
CurrentLyricsSearchResult,
token),
token);
}
}
}
public void UpdateLyrics()
public async void UpdateLyrics()
{
_ = _refreshLyricsRunner.RunAsync(RefreshLyricsAsync);
await _refreshLyricsRunner.RunAsync(async (token) =>
{
await RefreshLyricsAsync(token);
});
}
public void UpdateTranslations()
{
_ = _refreshTranslationRunner.RunAsync(RefreshTranslationAsync);
}
}
}

View File

@@ -12,7 +12,6 @@ using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.DiscordService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
@@ -25,10 +24,8 @@ using CommunityToolkit.WinUI;
using EvtSource;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
@@ -36,7 +33,6 @@ using System.Threading.Tasks;
using Vanara.Windows.Shell;
using Windows.Media.Control;
using Windows.Storage.Streams;
using Windows.UI;
using WindowsMediaController;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
@@ -44,13 +40,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>,
IRecipient<PropertyChangedMessage<PaletteGeneratorType>>,
IRecipient<PropertyChangedMessage<ChineseRomanization>>,
IRecipient<PropertyChangedMessage<List<string>>>,
IRecipient<PropertyChangedMessage<Color>>,
IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<LyricsFontColorType>>
IRecipient<PropertyChangedMessage<ChineseRomanization>>
{
private EventSourceReader? _sse = null;
private readonly MediaManager _mediaManager = new();
@@ -61,7 +51,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private readonly ITranslateService _translateService;
private readonly ISettingsService _settingsService;
private readonly ILibWatcherService _libWatcherService;
private readonly ILiveStatesService _liveStatesService;
private readonly IDiscordService _discordService;
private readonly ILogger<MediaSessionsService> _logger;
@@ -79,19 +68,17 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public MediaSessionsService(
ISettingsService settingsService,
IAlbumArtSearchService albumArtSearchService,
ILyricsSearchService musicSearchService,
ILyricsSearchService lyricsSearchService,
ILibWatcherService libWatcherService,
ILiveStatesService liveStatesService,
IDiscordService discordService,
ITranslateService libreTranslateService,
ILogger<MediaSessionsService> logger)
{
_settingsService = settingsService;
_albumArtSearchService = albumArtSearchService;
_lyrcsSearchService = musicSearchService;
_lyrcsSearchService = lyricsSearchService;
_libWatcherService = libWatcherService;
_translateService = libreTranslateService;
_liveStatesService = liveStatesService;
_discordService = discordService;
_logger = logger;
@@ -108,7 +95,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
InitMediaManager();
InitPlaybackShortcuts();
}
private void MappedSongSearchQueries_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
@@ -121,44 +107,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
UpdateLyrics();
}
public void InitPlaybackShortcuts()
{
UpdatePlayOrPauseSongShortcut();
UpdatePreviousSongShortcut();
UpdateNextSongShortcut();
}
private void UpdatePlayOrPauseSongShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
{
if (CurrentIsPlaying)
{
_ = PauseAsync();
}
else
{
_ = PlayAsync();
}
}));
}
private void UpdatePreviousSongShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.PreviousSong, _settingsService.AppSettings.GeneralSettings.PreviousSongShortcut, () =>
{
_ = PreviousAsync();
});
}
private void UpdateNextSongShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.NextSong, _settingsService.AppSettings.GeneralSettings.NextSongShortcut, () =>
{
_ = NextAsync();
});
}
private void LocalMediaFolders_ItemPropertyChanged(object? sender, ItemPropertyChangedEventArgs e)
{
UpdateAlbumArt();
@@ -206,7 +154,22 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private bool IsMediaSourceEnabled(string id)
{
return _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true;
var found = _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id);
if (_settingsService.AppSettings.MusicGallerySettings.LyricsWindowStatus.IsOpened)
{
if (PlayerIDHelper.IsBetterLyrics(found?.Provider))
{
return found?.IsEnabled ?? true;
}
else
{
return false;
}
}
else
{
return found?.IsEnabled ?? true;
}
}
private bool IsMediaSourceTimelineSyncEnabled(string? id)
@@ -239,7 +202,11 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties? timelineProperties)
{
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
if (mediaSession == null)
{
CurrentPosition = TimeSpan.Zero;
return;
}
var desiredSession = GetCurrentSession();
@@ -263,7 +230,11 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, (() =>
{
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
if (mediaSession == null)
{
CurrentIsPlaying = false;
return;
}
var desiredSession = GetCurrentSession();
@@ -501,14 +472,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProps = null;
var desiredSession = GetCurrentSession();
//if (desiredSession == null || desiredSession.ControlSession == null) return;
try
{
mediaProps = await desiredSession?.ControlSession?.TryGetMediaPropertiesAsync();
}
catch (Exception) { }
//if (desiredSession == null || desiredSession.ControlSession == null) return;
MediaManager_OnAnyTimelinePropertyChanged(desiredSession, desiredSession?.ControlSession?.GetTimelineProperties());
MediaManager_OnAnyMediaPropertyChanged(desiredSession, mediaProps);
@@ -690,42 +659,30 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
if (message.PropertyName == nameof(TranslationSettings.IsLibreTranslateEnabled))
{
UpdateTranslations();
UpdateLyrics();
}
else if (message.PropertyName == nameof(TranslationSettings.IsTranslationEnabled))
{
UpdateTranslations();
UpdateLyrics();
}
else if (message.PropertyName == nameof(TranslationSettings.IsChineseRomanizationEnabled))
{
UpdateTranslations();
UpdateLyrics();
}
else if (message.PropertyName == nameof(TranslationSettings.IsJapaneseRomanizationEnabled))
{
UpdateTranslations();
UpdateLyrics();
}
else if (message.PropertyName == nameof(TranslationSettings.IsTraditionalChineseEnabled))
{
UpdateLyrics();
}
}
}
public void Receive(PropertyChangedMessage<List<string>> message)
{
if (message.Sender is GeneralSettings)
else if (message.Sender is LyricsWindowStatus)
{
if (message.PropertyName == nameof(GeneralSettings.PlayOrPauseShortcut))
if (message.PropertyName == nameof(MusicGallerySettings.LyricsWindowStatus.IsOpened))
{
UpdatePlayOrPauseSongShortcut();
}
else if (message.PropertyName == nameof(GeneralSettings.PreviousSongShortcut))
{
UpdatePreviousSongShortcut();
}
else if (message.PropertyName == nameof(GeneralSettings.NextSongShortcut))
{
UpdateNextSongShortcut();
MediaManager_OnFocusedSessionChanged(null);
}
}
}
@@ -737,101 +694,22 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageCode))
{
_logger.LogInformation("Target LibreTranslate language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
UpdateTranslations();
UpdateLyrics();
}
}
}
public void Receive(PropertyChangedMessage<LyricsWindowStatus> message)
{
if (message.Sender is LiveStates)
{
if (message.PropertyName == nameof(LiveStates.LyricsWindowStatus))
{
UpdateAlbumArtThemeColors();
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<ChineseRomanization> message)
{
if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.ChineseRomanization))
{
UpdateTranslations();
UpdateLyrics();
}
}
}
public void Receive(PropertyChangedMessage<PaletteGeneratorType> message)
{
if (message.Sender is LyricsBackgroundSettings)
{
if (message.PropertyName == nameof(LyricsBackgroundSettings.PaletteGeneratorType))
{
UpdateAlbumArt();
}
}
}
public void Receive(PropertyChangedMessage<Color> message)
{
if (message.Sender is NowPlayingWindowViewModel)
{
if (message.PropertyName == nameof(NowPlayingWindowViewModel.BackdropAccentColor))
{
_envColor = message.NewValue;
UpdateAlbumArtThemeColors();
}
}
else if (message.Sender is LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomBgFontColor))
{
UpdateAlbumArtThemeColors();
}
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomFgFontColor))
{
UpdateAlbumArtThemeColors();
}
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsCustomStrokeFontColor))
{
UpdateAlbumArtThemeColors();
}
}
}
public void Receive(PropertyChangedMessage<ElementTheme> message)
{
if (message.Sender is LyricsBackgroundSettings)
{
if (message.PropertyName == nameof(LyricsBackgroundSettings.LyricsBackgroundTheme))
{
UpdateAlbumArtThemeColors();
}
}
}
public void Receive(PropertyChangedMessage<LyricsFontColorType> message)
{
if (message.Sender is LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.LyricsBgFontColorType))
{
UpdateAlbumArtThemeColors();
}
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsFgFontColorType))
{
UpdateAlbumArtThemeColors();
}
else if (message.PropertyName == nameof(LyricsStyleSettings.LyricsStrokeFontColorType))
{
UpdateAlbumArtThemeColors();
}
}
}
}
}

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Collections;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
@@ -19,7 +20,7 @@ namespace BetterLyrics.WinUI3.Services.SettingsService
// 新建一个 AppSettings 类
public partial class SettingsService : BaseViewModel, ISettingsService
{
private DispatcherQueueTimer _writeAppSettingsTimer;
private readonly DispatcherQueueTimer _writeAppSettingsTimer;
public AppSettings AppSettings { get; set; }

View File

@@ -153,9 +153,6 @@
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>Default</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>Dependencies</value>
</data>
<data name="DesktopMode" xml:space="preserve">
<value>Desktop mode (transparent)</value>
</data>
@@ -198,6 +195,9 @@
<data name="Jyutping" xml:space="preserve">
<value>Cantonese pinyin</value>
</data>
<data name="KeepAtLeastOneStatusDefault" xml:space="preserve">
<value>Please make sure at least one status is set to default</value>
</data>
<data name="LastFMAuthFailed" xml:space="preserve">
<value>Authorization failed, please try again</value>
</data>
@@ -240,22 +240,22 @@
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<value>Lyrics provider</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSearch.Text" xml:space="preserve">
<value>Manually search lyrics</value>
</data>
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSettings.Text" xml:space="preserve">
<value>Lyrics Window Management Shortcut Settings</value>
</data>
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
<value>Match percentage</value>
</data>
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPagePlaybackSource.Text" xml:space="preserve">
<value>Play source shortcut settings</value>
</data>
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
<value>Reset to 0 when switching songs</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageSettings.Text" xml:space="preserve">
<value>Settings</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
@@ -276,6 +276,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>Translation provider</value>
</data>
<data name="LyricsPageTransliterationProviderPrefix.Header" xml:space="preserve">
<value>Transliteration source</value>
</data>
<data name="LyricsParseError" xml:space="preserve">
<value>Lyrics parsing failed</value>
</data>
@@ -342,8 +345,8 @@
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
<value>Immersive mode</value>
</data>
<data name="LyricsWindowSettingsControlCurrentLyricsWindowConfig.Text" xml:space="preserve">
<value>Current lyrics window config</value>
<data name="LyricsWindowSettingsControlLyricsWindowConfig.Text" xml:space="preserve">
<value>Configuration</value>
</data>
<data name="LyricsWindowSettingsControlLyricsWindowMode.Header" xml:space="preserve">
<value>Lyrics window mode</value>
@@ -487,7 +490,7 @@
<value>Single loop</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>Search for song title, artist, album or folder path</value>
<value>Search for the song name, artist, album, file name, or folder path where the file is located</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>Album</value>
@@ -513,6 +516,12 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>Music gallery - BetterLyrics</value>
</data>
<data name="MusicGalleryWindowDownButtonToolTip.Content" xml:space="preserve">
<value>Retract</value>
</data>
<data name="MusicGalleryWindowUpButtonToolTip.Content" xml:space="preserve">
<value>Unwrap</value>
</data>
<data name="NarrowMode" xml:space="preserve">
<value>Narrow mode</value>
</data>
@@ -534,6 +543,9 @@
<data name="SetingsPageContributors.Text" xml:space="preserve">
<value>Contributors</value>
</data>
<data name="SetingsPageDeps.Text" xml:space="preserve">
<value>Code Reference</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>Donation</value>
</data>
@@ -543,9 +555,15 @@
<data name="SetingsPageInstructions.Text" xml:space="preserve">
<value>Instructions</value>
</data>
<data name="SetingsPageSpecialThanks.Text" xml:space="preserve">
<value>Special Thanks</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>If you like this project, please consider supporting it by donating. Your support will help keep the project alive and encourage further development.</value>
</data>
<data name="SetingsPageUIUXRef.Text" xml:space="preserve">
<value>UI/UX Design Reference</value>
</data>
<data name="SettingsPage3DLyrics.Header" xml:space="preserve">
<value>[Experimental] Depth effect</value>
</data>
@@ -639,6 +657,9 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>Automatic adjustment</value>
</data>
<data name="SettingsPageAutoOpenLyricsWindow.Header" xml:space="preserve">
<value>Automatically opens the default lyrics window when the program starts</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>Open the music library window when the app starts</value>
</data>
@@ -660,6 +681,9 @@
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
<value>Lyrics background</value>
</data>
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
<value>Blur amount</value>
</data>
<data name="SettingsPageBorderless.Header" xml:space="preserve">
<value>Borderless window</value>
</data>
@@ -705,6 +729,9 @@
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
<value>Click-through shortcut keys</value>
</data>
<data name="SettingsPageCloseStatus.Text" xml:space="preserve">
<value>Close</value>
</data>
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
<value>Collapse dropdown</value>
</data>
@@ -717,6 +744,9 @@
<data name="SettingsPageConfigName.Header" xml:space="preserve">
<value>Configuration name</value>
</data>
<data name="SettingsPageConfigPlaybackSource.Text" xml:space="preserve">
<value>Configure playback source</value>
</data>
<data name="SettingsPageCreateFromCurrent.Text" xml:space="preserve">
<value>Copy</value>
</data>
@@ -813,6 +843,12 @@
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>Smooth step</value>
</data>
<data name="SettingsPageEffectLineStartToCurrentChar.Content" xml:space="preserve">
<value>Line start to current char</value>
</data>
<data name="SettingsPageEffectScopeLongDurationSyllable.Content" xml:space="preserve">
<value>Long duration syllable</value>
</data>
<data name="SettingsPageEN.Content" xml:space="preserve">
<value>English</value>
</data>
@@ -831,6 +867,9 @@
<data name="SettingsPageEnvColorSampleInner.Content" xml:space="preserve">
<value>Inside the window</value>
</data>
<data name="SettingsPageExitOnGalleryWindowClosed.Header" xml:space="preserve">
<value>Exit the program when closing the music library window</value>
</data>
<data name="SettingsPageExitOnLyricsWindowClosed.Header" xml:space="preserve">
<value>Exit the program when you close the lyrics window</value>
</data>
@@ -885,6 +924,9 @@
<data name="SettingsPageHeight.Header" xml:space="preserve">
<value>Height</value>
</data>
<data name="SettingsPageHello.Text" xml:space="preserve">
<value>Hello</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>Automatically hide/show lyric window when music is not playing</value>
</data>
@@ -987,6 +1029,9 @@
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>Log record</value>
</data>
<data name="SettingsPageLongSyllableDuration.Header" xml:space="preserve">
<value>Long syllable threshold</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX Music Server</value>
</data>
@@ -1057,7 +1102,7 @@
<value>Custom</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>Lyrics font family</value>
<value>Font family</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>Font size</value>
@@ -1068,9 +1113,6 @@
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>Font weight</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Description" xml:space="preserve">
<value>Enable glow effect for syllables with long duration</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>Glow effect</value>
</data>
@@ -1101,6 +1143,9 @@
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>Normal</value>
</data>
<data name="SettingsPageLyricsOpacity.Header" xml:space="preserve">
<value>Font opacity</value>
</data>
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
<value>Lyrics area height factor</value>
</data>
@@ -1158,6 +1203,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>Top and bottom edge opacity</value>
</data>
<data name="SettingsPageLyricsWindow.Text" xml:space="preserve">
<value>Lyrics window</value>
</data>
<data name="SettingsPageLyricsWindowManager.Text" xml:space="preserve">
<value>Lyrics window manager</value>
</data>
@@ -1200,6 +1248,15 @@
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>Play test music</value>
</data>
<data name="SettingsPageMultiNowPlayingWindows.Header" xml:space="preserve">
<value>Multi-window coexistence mode</value>
</data>
<data name="SettingsPageMusicGallery.Text" xml:space="preserve">
<value>Music gallery</value>
</data>
<data name="SettingsPageMusicGalleryLyrics.Header" xml:space="preserve">
<value>Embedded lyrics window in music library</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>Add folders storing music or lyrics</value>
</data>
@@ -1248,12 +1305,18 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>The path cannot be found on your computer</value>
</data>
<data name="SettingsPagePatrons.Text" xml:space="preserve">
<value>Sponsor</value>
</data>
<data name="SettingsPagePhonetic.Text" xml:space="preserve">
<value>Lyric annotation</value>
</data>
<data name="SettingsPagePhoneticText.Header" xml:space="preserve">
<value>Phonetic symbols</value>
</data>
<data name="SettingsPagePinToTaskbar.Header" xml:space="preserve">
<value>Pin to taskbar</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>Pinyin of Mandarin</value>
</data>
@@ -1263,9 +1326,6 @@
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
<value>No playback source captured</value>
</data>
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>Play</value>
</data>
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
<value>Playback source</value>
</data>
@@ -1371,6 +1431,9 @@
<data name="SettingsPageShareHub.Content" xml:space="preserve">
<value>Browse online resources sharing hub</value>
</data>
<data name="SettingsPageShortcut.Text" xml:space="preserve">
<value>Play</value>
</data>
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
<value>This hotkey was not successfully registered</value>
</data>
@@ -1443,6 +1506,9 @@
<data name="SettingsPageStandardMode.Text" xml:space="preserve">
<value>Standard mode</value>
</data>
<data name="SettingsPageStartup.Text" xml:space="preserve">
<value>Start</value>
</data>
<data name="SettingsPageStrokeFontColor.Header" xml:space="preserve">
<value>Stroke color</value>
</data>
@@ -1452,12 +1518,24 @@
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>Target language</value>
</data>
<data name="SettingsPageTaskbarMode.Text" xml:space="preserve">
<value>Taskbar mode</value>
</data>
<data name="SettingsPageTaskbarPlacement.Header" xml:space="preserve">
<value>Taskbar fixed position</value>
</data>
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageThanksForPurchasing.Text" xml:space="preserve">
<value>Thank you for purchasing BetterLyrics</value>
</data>
<data name="SettingsPageThanksList.Header" xml:space="preserve">
<value>Credits</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>Lyrics background theme</value>
</data>
@@ -1503,6 +1581,9 @@
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>Transparent</value>
</data>
<data name="SettingsPageUserWhoPurchased.Text" xml:space="preserve">
<value>And buy users who support BetterLyrics</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>Version</value>
</data>
@@ -1527,6 +1608,9 @@
<data name="SettingsPageWorkAreaHeight.Header" xml:space="preserve">
<value>Work area height</value>
</data>
<data name="SettingsPageYouNowUsing.Text" xml:space="preserve">
<value>You who are using the software</value>
</data>
<data name="StandardMode" xml:space="preserve">
<value>Standard mode</value>
</data>
@@ -1557,6 +1641,9 @@
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>Lyrics window switcher</value>
</data>
<data name="TaskbarMode" xml:space="preserve">
<value>Taskbar mode</value>
</data>
<data name="TermsOfService.Content" xml:space="preserve">
<value>Terms of service</value>
</data>

View File

@@ -153,9 +153,6 @@
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>デフォルト</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>依存関係</value>
</data>
<data name="DesktopMode" xml:space="preserve">
<value>デスクトップモード(透明) </value>
</data>
@@ -198,6 +195,9 @@
<data name="Jyutping" xml:space="preserve">
<value>広東語のピンイン</value>
</data>
<data name="KeepAtLeastOneStatusDefault" xml:space="preserve">
<value>少なくとも1つのステータスがデフォルトに設定されていることを確認してください</value>
</data>
<data name="LastFMAuthFailed" xml:space="preserve">
<value>承認が失敗しました、もう一度やり直してください</value>
</data>
@@ -240,22 +240,22 @@
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<value>歌詞プロバイダー</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSearch.Text" xml:space="preserve">
<value>手動で歌詞を検索します</value>
</data>
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSettings.Text" xml:space="preserve">
<value>歌詞ウィンドウ管理ショートカット設定</value>
</data>
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
<value>マッチ率</value>
</data>
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPagePlaybackSource.Text" xml:space="preserve">
<value>再生ソースクイック設定</value>
</data>
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
<value>曲を切り替えるときに0にリセットします</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageSettings.Text" xml:space="preserve">
<value>設定</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
@@ -276,6 +276,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻訳プロバイダー</value>
</data>
<data name="LyricsPageTransliterationProviderPrefix.Header" xml:space="preserve">
<value>文字起こしソース</value>
</data>
<data name="LyricsParseError" xml:space="preserve">
<value>歌詞の解析に失敗しました</value>
</data>
@@ -342,8 +345,8 @@
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
<value>没入モード</value>
</data>
<data name="LyricsWindowSettingsControlCurrentLyricsWindowConfig.Text" xml:space="preserve">
<value>現在の歌詞ウィンドウ設定</value>
<data name="LyricsWindowSettingsControlLyricsWindowConfig.Text" xml:space="preserve">
<value>構成</value>
</data>
<data name="LyricsWindowSettingsControlLyricsWindowMode.Header" xml:space="preserve">
<value>歌詞ウィンドウモード</value>
@@ -487,7 +490,7 @@
<value>シングルループ</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>曲名、アーティスト、アルバム、フォルダパスを検索してください</value>
<value>ファイルが置かれている曲名、アーティスト、アルバム、ファイル名、またはフォルダパスを検索します</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>アルバム</value>
@@ -513,6 +516,12 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音楽ギャラリー - BetterLyrics</value>
</data>
<data name="MusicGalleryWindowDownButtonToolTip.Content" xml:space="preserve">
<value>取り戻せ</value>
</data>
<data name="MusicGalleryWindowUpButtonToolTip.Content" xml:space="preserve">
<value>展開</value>
</data>
<data name="NarrowMode" xml:space="preserve">
<value>狭い</value>
</data>
@@ -534,6 +543,9 @@
<data name="SetingsPageContributors.Text" xml:space="preserve">
<value>投稿者</value>
</data>
<data name="SetingsPageDeps.Text" xml:space="preserve">
<value>コードリファレンス</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>寄付</value>
</data>
@@ -543,9 +555,15 @@
<data name="SetingsPageInstructions.Text" xml:space="preserve">
<value>手順</value>
</data>
<data name="SetingsPageSpecialThanks.Text" xml:space="preserve">
<value>スペシャルサンクス</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>このプロジェクトが気に入っている場合は、寄付してサポートすることを検討してください。あなたのサポートは、プロジェクトを生かし続け、さらなる開発を促進するのに役立ちます。</value>
</data>
<data name="SetingsPageUIUXRef.Text" xml:space="preserve">
<value>UI/UXデザインリファレンス</value>
</data>
<data name="SettingsPage3DLyrics.Header" xml:space="preserve">
<value>[実験的] 奥行き効果</value>
</data>
@@ -639,6 +657,9 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageAutoOpenLyricsWindow.Header" xml:space="preserve">
<value>プログラムが開始されると、デフォルトの歌詞ウィンドウが自動的に開きます</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>アプリが起動したら、音楽ライブラリウィンドウを開きます</value>
</data>
@@ -660,6 +681,9 @@
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
<value>歌詞の背景</value>
</data>
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
<value>あいまい</value>
</data>
<data name="SettingsPageBorderless.Header" xml:space="preserve">
<value>ボーダーレスウィンドウ</value>
</data>
@@ -705,6 +729,9 @@
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
<value>クリックスルーショートカットキー</value>
</data>
<data name="SettingsPageCloseStatus.Text" xml:space="preserve">
<value>閉じる</value>
</data>
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
<value>ドロップダウンを折りたたみます</value>
</data>
@@ -717,6 +744,9 @@
<data name="SettingsPageConfigName.Header" xml:space="preserve">
<value>設定名</value>
</data>
<data name="SettingsPageConfigPlaybackSource.Text" xml:space="preserve">
<value>再生ソースを設定します</value>
</data>
<data name="SettingsPageCreateFromCurrent.Text" xml:space="preserve">
<value>コピー</value>
</data>
@@ -813,6 +843,12 @@
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>スムーズなステップ</value>
</data>
<data name="SettingsPageEffectLineStartToCurrentChar.Content" xml:space="preserve">
<value>現在の文字への行開始</value>
</data>
<data name="SettingsPageEffectScopeLongDurationSyllable.Content" xml:space="preserve">
<value>長時間の音節</value>
</data>
<data name="SettingsPageEN.Content" xml:space="preserve">
<value>English</value>
</data>
@@ -831,6 +867,9 @@
<data name="SettingsPageEnvColorSampleInner.Content" xml:space="preserve">
<value>窓の中だ</value>
</data>
<data name="SettingsPageExitOnGalleryWindowClosed.Header" xml:space="preserve">
<value>音楽ライブラリウィンドウを閉じてプログラムを終了してください</value>
</data>
<data name="SettingsPageExitOnLyricsWindowClosed.Header" xml:space="preserve">
<value>歌詞ウィンドウを閉じてプログラムを終了してください</value>
</data>
@@ -885,6 +924,9 @@
<data name="SettingsPageHeight.Header" xml:space="preserve">
<value>高さ</value>
</data>
<data name="SettingsPageHello.Text" xml:space="preserve">
<value>こんにちは</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>音楽が再生されていないときに、自動的に歌詞ウィンドウを非表示/表示します</value>
</data>
@@ -987,6 +1029,9 @@
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>ログレコード</value>
</data>
<data name="SettingsPageLongSyllableDuration.Header" xml:space="preserve">
<value>長い音節のしきい値</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX Music Server</value>
</data>
@@ -1057,7 +1102,7 @@
<value>カスタマイズ</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>歌詞フォントファミリー</value>
<value>フォントファミリー</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>フォントサイズ</value>
@@ -1068,9 +1113,6 @@
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>フォント重量</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Description" xml:space="preserve">
<value>長時間の音節にグロウ効果を有効にします</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>グロー効果</value>
</data>
@@ -1101,6 +1143,9 @@
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>普通</value>
</data>
<data name="SettingsPageLyricsOpacity.Header" xml:space="preserve">
<value>フォントの不透明度</value>
</data>
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
<value>歌詞エリアの高さ係数</value>
</data>
@@ -1158,6 +1203,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上端と下端の不透明度</value>
</data>
<data name="SettingsPageLyricsWindow.Text" xml:space="preserve">
<value>歌詞ウィンドウ</value>
</data>
<data name="SettingsPageLyricsWindowManager.Text" xml:space="preserve">
<value>歌詞ウィンドウマネージャー</value>
</data>
@@ -1200,6 +1248,15 @@
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>テスト音楽を再生します</value>
</data>
<data name="SettingsPageMultiNowPlayingWindows.Header" xml:space="preserve">
<value>マルチウィンドウ共存モード</value>
</data>
<data name="SettingsPageMusicGallery.Text" xml:space="preserve">
<value>ミュージックギャラリー</value>
</data>
<data name="SettingsPageMusicGalleryLyrics.Header" xml:space="preserve">
<value>歌詞ウィンドウが埋め込まれた音楽ライブラリ</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>音楽や歌詞を保存するフォルダーを追加します</value>
</data>
@@ -1248,12 +1305,18 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>パスはコンピューターでは見つかりません</value>
</data>
<data name="SettingsPagePatrons.Text" xml:space="preserve">
<value>後援しました</value>
</data>
<data name="SettingsPagePhonetic.Text" xml:space="preserve">
<value>リリックアノテーション</value>
</data>
<data name="SettingsPagePhoneticText.Header" xml:space="preserve">
<value>発音</value>
</data>
<data name="SettingsPagePinToTaskbar.Header" xml:space="preserve">
<value>タスクバーに移動</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>マンダリンのピンイン</value>
</data>
@@ -1263,9 +1326,6 @@
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
<value>キャプチャされた再生ソースはありません</value>
</data>
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>遊ぶ</value>
</data>
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
<value>再生ソース</value>
</data>
@@ -1371,6 +1431,9 @@
<data name="SettingsPageShareHub.Content" xml:space="preserve">
<value>オンラインリソース共有ハブを閲覧しましょう</value>
</data>
<data name="SettingsPageShortcut.Text" xml:space="preserve">
<value>ショートカットキー</value>
</data>
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
<value>このホットキーは正常に登録されていません</value>
</data>
@@ -1443,6 +1506,9 @@
<data name="SettingsPageStandardMode.Text" xml:space="preserve">
<value>標準モード</value>
</data>
<data name="SettingsPageStartup.Text" xml:space="preserve">
<value>起動</value>
</data>
<data name="SettingsPageStrokeFontColor.Header" xml:space="preserve">
<value>ストロークカラー</value>
</data>
@@ -1452,12 +1518,24 @@
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>ターゲット言語</value>
</data>
<data name="SettingsPageTaskbarMode.Text" xml:space="preserve">
<value>タスクバーモード</value>
</data>
<data name="SettingsPageTaskbarPlacement.Header" xml:space="preserve">
<value>タスクバーの位置が固定されました</value>
</data>
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageThanksForPurchasing.Text" xml:space="preserve">
<value>BetterLyrics をご購入いただきありがとうございます</value>
</data>
<data name="SettingsPageThanksList.Header" xml:space="preserve">
<value>ありがとうございました</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>歌詞の背景テーマ</value>
</data>
@@ -1503,6 +1581,9 @@
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>透明</value>
</data>
<data name="SettingsPageUserWhoPurchased.Text" xml:space="preserve">
<value>そして、BetterLyricsをサポートするユーザーを購入しましょう</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>バージョン</value>
</data>
@@ -1527,6 +1608,9 @@
<data name="SettingsPageWorkAreaHeight.Header" xml:space="preserve">
<value>作業エリアの高さ</value>
</data>
<data name="SettingsPageYouNowUsing.Text" xml:space="preserve">
<value>ソフトウェアを使用しているあなた</value>
</data>
<data name="StandardMode" xml:space="preserve">
<value>標準モード</value>
</data>
@@ -1557,6 +1641,9 @@
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌詞ウィンドウスイッチャー</value>
</data>
<data name="TaskbarMode" xml:space="preserve">
<value>タスクバーモード</value>
</data>
<data name="TermsOfService.Content" xml:space="preserve">
<value>利用規約</value>
</data>

View File

@@ -153,9 +153,6 @@
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>기본</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>의존성</value>
</data>
<data name="DesktopMode" xml:space="preserve">
<value>데스크톱 모드 (투명)</value>
</data>
@@ -198,6 +195,9 @@
<data name="Jyutping" xml:space="preserve">
<value>광둥어 병음</value>
</data>
<data name="KeepAtLeastOneStatusDefault" xml:space="preserve">
<value>하나 이상의 상태가 기본값으로 설정되어 있는지 확인하십시오</value>
</data>
<data name="LastFMAuthFailed" xml:space="preserve">
<value>승인이 실패했습니다. 다시 시도하십시오</value>
</data>
@@ -240,22 +240,22 @@
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<value>가사 제공자</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSearch.Text" xml:space="preserve">
<value>가사를 수동으로 검색합니다</value>
</data>
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSettings.Text" xml:space="preserve">
<value>가사 창 관리 바로 가기 설정</value>
</data>
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
<value>매치 퍼센트</value>
</data>
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPagePlaybackSource.Text" xml:space="preserve">
<value>재생 소스 빠른 설정</value>
</data>
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
<value>노래를 전환 할 때 0 으로 재설정하십시오</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageSettings.Text" xml:space="preserve">
<value>설정</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
@@ -276,6 +276,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>번역 제공자</value>
</data>
<data name="LyricsPageTransliterationProviderPrefix.Header" xml:space="preserve">
<value>음역 소스</value>
</data>
<data name="LyricsParseError" xml:space="preserve">
<value>가사 구문 분석에 실패했습니다</value>
</data>
@@ -342,8 +345,8 @@
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
<value>몰입 형 모드</value>
</data>
<data name="LyricsWindowSettingsControlCurrentLyricsWindowConfig.Text" xml:space="preserve">
<value>현재 가사 창 구성</value>
<data name="LyricsWindowSettingsControlLyricsWindowConfig.Text" xml:space="preserve">
<value>배 치</value>
</data>
<data name="LyricsWindowSettingsControlLyricsWindowMode.Header" xml:space="preserve">
<value>가사 창 모드</value>
@@ -487,7 +490,7 @@
<value>단일 루프</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>노래 제목, 아티스트, 앨범 또는 폴더 경로를 검색하세요</value>
<value>파일이 있는 노래 이름, 아티스트, 앨범, 파일 이름 또는 폴더 경로를 검색하세요</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>앨범</value>
@@ -513,6 +516,12 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>음악 갤러리 - BetterLyrics</value>
</data>
<data name="MusicGalleryWindowDownButtonToolTip.Content" xml:space="preserve">
<value>다시 가져가세요</value>
</data>
<data name="MusicGalleryWindowUpButtonToolTip.Content" xml:space="preserve">
<value>펼치기</value>
</data>
<data name="NarrowMode" xml:space="preserve">
<value>협폭 조명</value>
</data>
@@ -534,6 +543,9 @@
<data name="SetingsPageContributors.Text" xml:space="preserve">
<value>기여자</value>
</data>
<data name="SetingsPageDeps.Text" xml:space="preserve">
<value>코드 참조</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>기부</value>
</data>
@@ -543,9 +555,15 @@
<data name="SetingsPageInstructions.Text" xml:space="preserve">
<value>지침</value>
</data>
<data name="SetingsPageSpecialThanks.Text" xml:space="preserve">
<value>특별 감사 인사</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>이 프로젝트가 마음에 들면 기부하여 지원을 고려하십시오. 귀하의 지원은 프로젝트를 계속 유지하고 추가 개발을 장려하는 데 도움이 될 것입니다.</value>
</data>
<data name="SetingsPageUIUXRef.Text" xml:space="preserve">
<value>UI/UX 디자인 참조</value>
</data>
<data name="SettingsPage3DLyrics.Header" xml:space="preserve">
<value>[실험] 깊이 효과</value>
</data>
@@ -639,6 +657,9 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>자동 조정</value>
</data>
<data name="SettingsPageAutoOpenLyricsWindow.Header" xml:space="preserve">
<value>프로그램이 시작되면 기본 가사 창이 자동으로 열립니다</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>앱이 시작되면 음악 라이브러리 창을 여세요</value>
</data>
@@ -660,6 +681,9 @@
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
<value>가사 배경</value>
</data>
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
<value>모호함</value>
</data>
<data name="SettingsPageBorderless.Header" xml:space="preserve">
<value>창문</value>
</data>
@@ -705,6 +729,9 @@
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
<value>바로 가기 키를 클릭하세요</value>
</data>
<data name="SettingsPageCloseStatus.Text" xml:space="preserve">
<value>닫기</value>
</data>
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
<value>드롭다운 접기</value>
</data>
@@ -717,6 +744,9 @@
<data name="SettingsPageConfigName.Header" xml:space="preserve">
<value>환경 설정</value>
</data>
<data name="SettingsPageConfigPlaybackSource.Text" xml:space="preserve">
<value>재생 소스 구성</value>
</data>
<data name="SettingsPageCreateFromCurrent.Text" xml:space="preserve">
<value>접수</value>
</data>
@@ -813,6 +843,12 @@
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>부드러운 단계</value>
</data>
<data name="SettingsPageEffectLineStartToCurrentChar.Content" xml:space="preserve">
<value>현재 문자로 줄 시작</value>
</data>
<data name="SettingsPageEffectScopeLongDurationSyllable.Content" xml:space="preserve">
<value>긴 지속 시간 음절</value>
</data>
<data name="SettingsPageEN.Content" xml:space="preserve">
<value>English</value>
</data>
@@ -831,6 +867,9 @@
<data name="SettingsPageEnvColorSampleInner.Content" xml:space="preserve">
<value>창문 안쪽</value>
</data>
<data name="SettingsPageExitOnGalleryWindowClosed.Header" xml:space="preserve">
<value>음악 라이브러리 창을 닫을 때 프로그램을 종료하십시오</value>
</data>
<data name="SettingsPageExitOnLyricsWindowClosed.Header" xml:space="preserve">
<value>가사 창을 닫으면 프로그램을 종료하세요</value>
</data>
@@ -885,6 +924,9 @@
<data name="SettingsPageHeight.Header" xml:space="preserve">
<value>신장</value>
</data>
<data name="SettingsPageHello.Text" xml:space="preserve">
<value>안녕</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>음악이 재생되지 않을 때 자동으로 가사 창 숨기기/표시</value>
</data>
@@ -987,6 +1029,9 @@
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>로그 레코드</value>
</data>
<data name="SettingsPageLongSyllableDuration.Header" xml:space="preserve">
<value>긴 음절 임계값</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX 음악 서버</value>
</data>
@@ -1057,7 +1102,7 @@
<value>사용자 정의하십시오</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>가사 글꼴 가족</value>
<value>폰트 패밀리</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>글꼴 크기</value>
@@ -1068,9 +1113,6 @@
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>글꼴 무게</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Description" xml:space="preserve">
<value>지속 시간이 긴 음절에 대해 발광 효과를 활성화합니다</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>글로우 효과</value>
</data>
@@ -1101,6 +1143,9 @@
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>정상</value>
</data>
<data name="SettingsPageLyricsOpacity.Header" xml:space="preserve">
<value>글꼴 불투명도</value>
</data>
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
<value>가사 영역 높이 계수</value>
</data>
@@ -1158,6 +1203,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>상단 및 하단 가장자리 불투명도</value>
</data>
<data name="SettingsPageLyricsWindow.Text" xml:space="preserve">
<value>가사 창</value>
</data>
<data name="SettingsPageLyricsWindowManager.Text" xml:space="preserve">
<value>가사 창 관리자</value>
</data>
@@ -1200,6 +1248,15 @@
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>테스트 음악을 재생하십시오</value>
</data>
<data name="SettingsPageMultiNowPlayingWindows.Header" xml:space="preserve">
<value>다중 창 공존 모드</value>
</data>
<data name="SettingsPageMusicGallery.Text" xml:space="preserve">
<value>음악 갤러리</value>
</data>
<data name="SettingsPageMusicGalleryLyrics.Header" xml:space="preserve">
<value>가사 창이 내장된 음악 라이브러리</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>음악이나 가사를 저장하는 폴더 추가</value>
</data>
@@ -1248,12 +1305,18 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>경로는 컴퓨터에서 찾을 수 없습니다</value>
</data>
<data name="SettingsPagePatrons.Text" xml:space="preserve">
<value>스폰서</value>
</data>
<data name="SettingsPagePhonetic.Text" xml:space="preserve">
<value>가사 주석</value>
</data>
<data name="SettingsPagePhoneticText.Header" xml:space="preserve">
<value>발성</value>
</data>
<data name="SettingsPagePinToTaskbar.Header" xml:space="preserve">
<value>작업 표시줄에 고정</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>만다린의 피니 인</value>
</data>
@@ -1263,9 +1326,6 @@
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
<value>재생 소스가 캡처되지 않았습니다</value>
</data>
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>놀다</value>
</data>
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
<value>재생 소스</value>
</data>
@@ -1371,6 +1431,9 @@
<data name="SettingsPageShareHub.Content" xml:space="preserve">
<value>온라인 리소스 공유 허브를 둘러보세요</value>
</data>
<data name="SettingsPageShortcut.Text" xml:space="preserve">
<value>단축키</value>
</data>
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
<value>이 핫키는 성공적으로 등록되지 않았습니다</value>
</data>
@@ -1443,6 +1506,9 @@
<data name="SettingsPageStandardMode.Text" xml:space="preserve">
<value>표준 모드</value>
</data>
<data name="SettingsPageStartup.Text" xml:space="preserve">
<value>시작</value>
</data>
<data name="SettingsPageStrokeFontColor.Header" xml:space="preserve">
<value>윤곽선 색상</value>
</data>
@@ -1452,12 +1518,24 @@
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>대상 언어</value>
</data>
<data name="SettingsPageTaskbarMode.Text" xml:space="preserve">
<value>작업 표시줄</value>
</data>
<data name="SettingsPageTaskbarPlacement.Header" xml:space="preserve">
<value>작업 표시줄 고정 위치</value>
</data>
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageThanksForPurchasing.Text" xml:space="preserve">
<value>BetterLyrics 를 구매해 주셔서 감사합니다</value>
</data>
<data name="SettingsPageThanksList.Header" xml:space="preserve">
<value>감사합니다</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>가사 배경 테마</value>
</data>
@@ -1503,6 +1581,9 @@
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>투명한</value>
</data>
<data name="SettingsPageUserWhoPurchased.Text" xml:space="preserve">
<value>그리고 BetterLyrics를 지원하는 사용자를 구입하세요</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>버전</value>
</data>
@@ -1527,6 +1608,9 @@
<data name="SettingsPageWorkAreaHeight.Header" xml:space="preserve">
<value>작업 영역 높이</value>
</data>
<data name="SettingsPageYouNowUsing.Text" xml:space="preserve">
<value>소프트웨어를 사용하는 여러분</value>
</data>
<data name="StandardMode" xml:space="preserve">
<value>표준 모드</value>
</data>
@@ -1557,6 +1641,9 @@
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>가사 창 전환기</value>
</data>
<data name="TaskbarMode" xml:space="preserve">
<value>작업 표시줄</value>
</data>
<data name="TermsOfService.Content" xml:space="preserve">
<value>서비스 약관</value>
</data>

View File

@@ -153,9 +153,6 @@
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>默认</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>依赖</value>
</data>
<data name="DesktopMode" xml:space="preserve">
<value>桌面模式(透明)</value>
</data>
@@ -198,6 +195,9 @@
<data name="Jyutping" xml:space="preserve">
<value>粤语拼音</value>
</data>
<data name="KeepAtLeastOneStatusDefault" xml:space="preserve">
<value>请确保至少有一个状态被设为默认</value>
</data>
<data name="LastFMAuthFailed" xml:space="preserve">
<value>授权失败,请重试</value>
</data>
@@ -240,22 +240,22 @@
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<value>歌词来源</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSearch.Text" xml:space="preserve">
<value>手动检索歌词</value>
</data>
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSettings.Text" xml:space="preserve">
<value>歌词窗口管理快捷设置</value>
</data>
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
<value>匹配百分比</value>
</data>
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPagePlaybackSource.Text" xml:space="preserve">
<value>播放源快捷设置</value>
</data>
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
<value>切换歌曲时重置为 0</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageSettings.Text" xml:space="preserve">
<value>设置</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
@@ -276,6 +276,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻译来源</value>
</data>
<data name="LyricsPageTransliterationProviderPrefix.Header" xml:space="preserve">
<value>音译来源</value>
</data>
<data name="LyricsParseError" xml:space="preserve">
<value>歌词解析失败</value>
</data>
@@ -342,8 +345,8 @@
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
<value>沉浸模式</value>
</data>
<data name="LyricsWindowSettingsControlCurrentLyricsWindowConfig.Text" xml:space="preserve">
<value>当前歌词窗口配置</value>
<data name="LyricsWindowSettingsControlLyricsWindowConfig.Text" xml:space="preserve">
<value>配置</value>
</data>
<data name="LyricsWindowSettingsControlLyricsWindowMode.Header" xml:space="preserve">
<value>歌词窗口模式</value>
@@ -487,7 +490,7 @@
<value>单曲循环</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>搜索歌曲名、艺术家、专辑所在文件夹路径</value>
<value>搜索歌曲名、艺术家、专辑、文件名或文件所在文件夹路径</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>专辑</value>
@@ -513,6 +516,12 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音乐库 - BetterLyrics</value>
</data>
<data name="MusicGalleryWindowDownButtonToolTip.Content" xml:space="preserve">
<value>收回</value>
</data>
<data name="MusicGalleryWindowUpButtonToolTip.Content" xml:space="preserve">
<value>展开</value>
</data>
<data name="NarrowMode" xml:space="preserve">
<value>窄屏模式</value>
</data>
@@ -534,6 +543,9 @@
<data name="SetingsPageContributors.Text" xml:space="preserve">
<value>贡献者</value>
</data>
<data name="SetingsPageDeps.Text" xml:space="preserve">
<value>代码参考</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>捐贈</value>
</data>
@@ -543,9 +555,15 @@
<data name="SetingsPageInstructions.Text" xml:space="preserve">
<value>操作指南</value>
</data>
<data name="SetingsPageSpecialThanks.Text" xml:space="preserve">
<value>特别鸣谢</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜欢这个项目,请考虑通过捐赠来支持它。您的支持将有助于保持项目的生命并鼓励进一步的发展。</value>
</data>
<data name="SetingsPageUIUXRef.Text" xml:space="preserve">
<value>UI/UX 设计参考</value>
</data>
<data name="SettingsPage3DLyrics.Header" xml:space="preserve">
<value>[实验性] 纵深效果</value>
</data>
@@ -639,6 +657,9 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自动调整</value>
</data>
<data name="SettingsPageAutoOpenLyricsWindow.Header" xml:space="preserve">
<value>程序启动时自动打开默认歌词窗口</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>应用程序启动时打开音乐库窗口</value>
</data>
@@ -660,6 +681,9 @@
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
<value>歌词背景</value>
</data>
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
<value>模糊度</value>
</data>
<data name="SettingsPageBorderless.Header" xml:space="preserve">
<value>无边框窗口</value>
</data>
@@ -705,6 +729,9 @@
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
<value>点击穿透快捷键</value>
</data>
<data name="SettingsPageCloseStatus.Text" xml:space="preserve">
<value>关闭</value>
</data>
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
<value>折叠下拉列表</value>
</data>
@@ -717,6 +744,9 @@
<data name="SettingsPageConfigName.Header" xml:space="preserve">
<value>配置名称</value>
</data>
<data name="SettingsPageConfigPlaybackSource.Text" xml:space="preserve">
<value>配置播放源</value>
</data>
<data name="SettingsPageCreateFromCurrent.Text" xml:space="preserve">
<value>拷贝</value>
</data>
@@ -813,6 +843,12 @@
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>平滑步进</value>
</data>
<data name="SettingsPageEffectLineStartToCurrentChar.Content" xml:space="preserve">
<value>行开始到当前字符</value>
</data>
<data name="SettingsPageEffectScopeLongDurationSyllable.Content" xml:space="preserve">
<value>长时间音节</value>
</data>
<data name="SettingsPageEN.Content" xml:space="preserve">
<value>English</value>
</data>
@@ -831,6 +867,9 @@
<data name="SettingsPageEnvColorSampleInner.Content" xml:space="preserve">
<value>窗口内部</value>
</data>
<data name="SettingsPageExitOnGalleryWindowClosed.Header" xml:space="preserve">
<value>关闭音乐库窗口时退出程序</value>
</data>
<data name="SettingsPageExitOnLyricsWindowClosed.Header" xml:space="preserve">
<value>关闭歌词窗口时退出程序</value>
</data>
@@ -885,6 +924,9 @@
<data name="SettingsPageHeight.Header" xml:space="preserve">
<value>高度</value>
</data>
<data name="SettingsPageHello.Text" xml:space="preserve">
<value>您好</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>音乐未播放/播放时自动隐藏/显示歌词窗口</value>
</data>
@@ -987,6 +1029,9 @@
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>日志记录</value>
</data>
<data name="SettingsPageLongSyllableDuration.Header" xml:space="preserve">
<value>长音节阈值</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX 音乐服务器</value>
</data>
@@ -1057,7 +1102,7 @@
<value>自定义</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>歌词字体</value>
<value>字体家族</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>字体大小</value>
@@ -1068,9 +1113,6 @@
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>字体粗细</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Description" xml:space="preserve">
<value>为持续时间较长的音节启用发光效果</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>辉光效果</value>
</data>
@@ -1101,6 +1143,9 @@
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>常规</value>
</data>
<data name="SettingsPageLyricsOpacity.Header" xml:space="preserve">
<value>字体不透明度</value>
</data>
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
<value>歌词区域高度因子</value>
</data>
@@ -1158,6 +1203,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上下边缘不透明度</value>
</data>
<data name="SettingsPageLyricsWindow.Text" xml:space="preserve">
<value>歌词窗口</value>
</data>
<data name="SettingsPageLyricsWindowManager.Text" xml:space="preserve">
<value>歌词窗口管理</value>
</data>
@@ -1200,6 +1248,15 @@
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>播放测试音乐</value>
</data>
<data name="SettingsPageMultiNowPlayingWindows.Header" xml:space="preserve">
<value>多窗口共存模式</value>
</data>
<data name="SettingsPageMusicGallery.Text" xml:space="preserve">
<value>音乐库</value>
</data>
<data name="SettingsPageMusicGalleryLyrics.Header" xml:space="preserve">
<value>音乐库内嵌歌词窗口</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>添加存放音乐或歌词的文件夹</value>
</data>
@@ -1248,12 +1305,18 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>无法在您的计算机中找到该路径</value>
</data>
<data name="SettingsPagePatrons.Text" xml:space="preserve">
<value>赞助</value>
</data>
<data name="SettingsPagePhonetic.Text" xml:space="preserve">
<value>歌词注音</value>
</data>
<data name="SettingsPagePhoneticText.Header" xml:space="preserve">
<value>注音</value>
</data>
<data name="SettingsPagePinToTaskbar.Header" xml:space="preserve">
<value>固定到任务栏</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>普通话拼音</value>
</data>
@@ -1263,9 +1326,6 @@
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
<value>没有捕获的播放源</value>
</data>
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>播放</value>
</data>
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
<value>播放源</value>
</data>
@@ -1371,6 +1431,9 @@
<data name="SettingsPageShareHub.Content" xml:space="preserve">
<value>浏览在线资源共享中心</value>
</data>
<data name="SettingsPageShortcut.Text" xml:space="preserve">
<value>快捷键</value>
</data>
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
<value>该热键未成功注册</value>
</data>
@@ -1443,6 +1506,9 @@
<data name="SettingsPageStandardMode.Text" xml:space="preserve">
<value>标准模式</value>
</data>
<data name="SettingsPageStartup.Text" xml:space="preserve">
<value>启动</value>
</data>
<data name="SettingsPageStrokeFontColor.Header" xml:space="preserve">
<value>描边颜色</value>
</data>
@@ -1452,12 +1518,24 @@
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>目标语言</value>
</data>
<data name="SettingsPageTaskbarMode.Text" xml:space="preserve">
<value>任务栏模式</value>
</data>
<data name="SettingsPageTaskbarPlacement.Header" xml:space="preserve">
<value>任务栏固定位置</value>
</data>
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageThanksForPurchasing.Text" xml:space="preserve">
<value>感谢您购买 BetterLyrics</value>
</data>
<data name="SettingsPageThanksList.Header" xml:space="preserve">
<value>致谢名单</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>歌词背景主题</value>
</data>
@@ -1503,6 +1581,9 @@
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>透明</value>
</data>
<data name="SettingsPageUserWhoPurchased.Text" xml:space="preserve">
<value>以及购买支持 BetterLyrics 的用户</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>版本号</value>
</data>
@@ -1527,6 +1608,9 @@
<data name="SettingsPageWorkAreaHeight.Header" xml:space="preserve">
<value>工作区高度</value>
</data>
<data name="SettingsPageYouNowUsing.Text" xml:space="preserve">
<value>正在使用该软件的你</value>
</data>
<data name="StandardMode" xml:space="preserve">
<value>标准模式</value>
</data>
@@ -1557,6 +1641,9 @@
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌词窗口切换器</value>
</data>
<data name="TaskbarMode" xml:space="preserve">
<value>任务栏模式</value>
</data>
<data name="TermsOfService.Content" xml:space="preserve">
<value>服务条款</value>
</data>

View File

@@ -153,9 +153,6 @@
<data name="DemoWindowControlDefault.Text" xml:space="preserve">
<value>預設</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>依賴</value>
</data>
<data name="DesktopMode" xml:space="preserve">
<value>桌面模式(透明)</value>
</data>
@@ -198,6 +195,9 @@
<data name="Jyutping" xml:space="preserve">
<value>粵語拼音</value>
</data>
<data name="KeepAtLeastOneStatusDefault" xml:space="preserve">
<value>請確保至少有一個狀態被設為預設</value>
</data>
<data name="LastFMAuthFailed" xml:space="preserve">
<value>授權失敗,請重試</value>
</data>
@@ -240,22 +240,22 @@
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
<value>歌詞來源</value>
</data>
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSearch.Text" xml:space="preserve">
<value>手動檢索歌詞</value>
</data>
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageLyricsSettings.Text" xml:space="preserve">
<value>歌詞視窗管理快捷設定</value>
</data>
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
<value>匹配百分比</value>
</data>
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPagePlaybackSource.Text" xml:space="preserve">
<value>播放源快捷設置</value>
</data>
<data name="LyricsPagePositionOffsetHint.Header" xml:space="preserve">
<value>切換歌曲時重置為 0</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<data name="LyricsPageSettings.Text" xml:space="preserve">
<value>設定</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
@@ -276,6 +276,9 @@
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
<value>翻譯來源</value>
</data>
<data name="LyricsPageTransliterationProviderPrefix.Header" xml:space="preserve">
<value>音譯來源</value>
</data>
<data name="LyricsParseError" xml:space="preserve">
<value>歌詞解析失敗</value>
</data>
@@ -342,8 +345,8 @@
<data name="LyricsWindowImmersiveButtonToolTip.Content" xml:space="preserve">
<value>沉浸模式</value>
</data>
<data name="LyricsWindowSettingsControlCurrentLyricsWindowConfig.Text" xml:space="preserve">
<value>目前歌詞視窗設定</value>
<data name="LyricsWindowSettingsControlLyricsWindowConfig.Text" xml:space="preserve">
<value>配置</value>
</data>
<data name="LyricsWindowSettingsControlLyricsWindowMode.Header" xml:space="preserve">
<value>歌詞視窗模式</value>
@@ -487,7 +490,7 @@
<value>單曲循環</value>
</data>
<data name="MusicGalleryPageSongSearchBox.PlaceholderText" xml:space="preserve">
<value>搜尋歌曲名稱、藝人、專輯所在資料夾路徑</value>
<value>搜尋歌曲名稱、藝人、專輯、檔案名稱或檔案所在資料夾路徑</value>
</data>
<data name="MusicGalleryPageSortByAlbum.Content" xml:space="preserve">
<value>專輯</value>
@@ -513,6 +516,12 @@
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>音樂庫 - BetterLyrics</value>
</data>
<data name="MusicGalleryWindowDownButtonToolTip.Content" xml:space="preserve">
<value>收回</value>
</data>
<data name="MusicGalleryWindowUpButtonToolTip.Content" xml:space="preserve">
<value>展開</value>
</data>
<data name="NarrowMode" xml:space="preserve">
<value>窄螢幕模式</value>
</data>
@@ -534,6 +543,9 @@
<data name="SetingsPageContributors.Text" xml:space="preserve">
<value>貢獻者</value>
</data>
<data name="SetingsPageDeps.Text" xml:space="preserve">
<value>代碼參考</value>
</data>
<data name="SetingsPageDonation.Text" xml:space="preserve">
<value>捐贈</value>
</data>
@@ -543,9 +555,15 @@
<data name="SetingsPageInstructions.Text" xml:space="preserve">
<value>說明</value>
</data>
<data name="SetingsPageSpecialThanks.Text" xml:space="preserve">
<value>特别鸣谢</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜歡這個項目,請考慮通過捐贈來支持它。您的支持將有助於保持項目的生命並鼓勵進一步的發展。</value>
</data>
<data name="SetingsPageUIUXRef.Text" xml:space="preserve">
<value>UI/UX 設計參考</value>
</data>
<data name="SettingsPage3DLyrics.Header" xml:space="preserve">
<value>[實驗性] 縱深效果</value>
</data>
@@ -639,6 +657,9 @@
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
<value>自動調整</value>
</data>
<data name="SettingsPageAutoOpenLyricsWindow.Header" xml:space="preserve">
<value>程式啟動時自動開啟預設歌詞視窗</value>
</data>
<data name="SettingsPageAutoOpenMusicGalleryWindow.Header" xml:space="preserve">
<value>應用程式啟動時開啟音樂庫視窗</value>
</data>
@@ -660,6 +681,9 @@
<data name="SettingsPageBackgroundOverlay.Text" xml:space="preserve">
<value>歌詞背景</value>
</data>
<data name="SettingsPageBlurAmount.Header" xml:space="preserve">
<value>模糊度</value>
</data>
<data name="SettingsPageBorderless.Header" xml:space="preserve">
<value>無邊框窗口</value>
</data>
@@ -705,6 +729,9 @@
<data name="SettingsPageClickThroughHotKey.Header" xml:space="preserve">
<value>點選穿透快捷鍵</value>
</data>
<data name="SettingsPageCloseStatus.Text" xml:space="preserve">
<value>關閉</value>
</data>
<data name="SettingsPageCollapseDropdown.Content" xml:space="preserve">
<value>收起下拉列表</value>
</data>
@@ -717,6 +744,9 @@
<data name="SettingsPageConfigName.Header" xml:space="preserve">
<value>配置名稱</value>
</data>
<data name="SettingsPageConfigPlaybackSource.Text" xml:space="preserve">
<value>配置播放來源</value>
</data>
<data name="SettingsPageCreateFromCurrent.Text" xml:space="preserve">
<value>複製</value>
</data>
@@ -813,6 +843,12 @@
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>平滑步進</value>
</data>
<data name="SettingsPageEffectLineStartToCurrentChar.Content" xml:space="preserve">
<value>行首到當前字符</value>
</data>
<data name="SettingsPageEffectScopeLongDurationSyllable.Content" xml:space="preserve">
<value>長時間的音節</value>
</data>
<data name="SettingsPageEN.Content" xml:space="preserve">
<value>English</value>
</data>
@@ -831,6 +867,9 @@
<data name="SettingsPageEnvColorSampleInner.Content" xml:space="preserve">
<value>視窗內部</value>
</data>
<data name="SettingsPageExitOnGalleryWindowClosed.Header" xml:space="preserve">
<value>關閉音樂庫視窗時退出程式</value>
</data>
<data name="SettingsPageExitOnLyricsWindowClosed.Header" xml:space="preserve">
<value>關閉歌詞視窗時退出程式</value>
</data>
@@ -885,6 +924,9 @@
<data name="SettingsPageHeight.Header" xml:space="preserve">
<value>高度</value>
</data>
<data name="SettingsPageHello.Text" xml:space="preserve">
<value>您好</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>音樂未播放/播放時自動隱藏/顯示歌詞視窗</value>
</data>
@@ -987,6 +1029,9 @@
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>日誌記錄</value>
</data>
<data name="SettingsPageLongSyllableDuration.Header" xml:space="preserve">
<value>長音節閾值</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX 音樂服務器</value>
</data>
@@ -1057,7 +1102,7 @@
<value>自定義</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>歌詞字體</value>
<value>字體家族</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>字體大小</value>
@@ -1068,9 +1113,6 @@
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>字體粗細</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Description" xml:space="preserve">
<value>為持續時間較長的音節啟用發光效果</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>輝光效果</value>
</data>
@@ -1101,6 +1143,9 @@
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>常規</value>
</data>
<data name="SettingsPageLyricsOpacity.Header" xml:space="preserve">
<value>字體不透明度</value>
</data>
<data name="SettingsPageLyricsRowFactor.Header" xml:space="preserve">
<value>歌詞區域高度因子</value>
</data>
@@ -1158,6 +1203,9 @@
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>上下邊緣不透明度</value>
</data>
<data name="SettingsPageLyricsWindow.Text" xml:space="preserve">
<value>歌詞視窗</value>
</data>
<data name="SettingsPageLyricsWindowManager.Text" xml:space="preserve">
<value>歌詞視窗管理</value>
</data>
@@ -1200,6 +1248,15 @@
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>播放測試音樂</value>
</data>
<data name="SettingsPageMultiNowPlayingWindows.Header" xml:space="preserve">
<value>多視窗共存模式</value>
</data>
<data name="SettingsPageMusicGallery.Text" xml:space="preserve">
<value>音樂庫</value>
</data>
<data name="SettingsPageMusicGalleryLyrics.Header" xml:space="preserve">
<value>音樂庫內嵌歌詞視窗</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>新增存放音樂或歌詞的資料夾</value>
</data>
@@ -1222,7 +1279,7 @@
<value>激進</value>
</data>
<data name="SettingsPageOpacity.Header" xml:space="preserve">
<value>專輯背景層不透明度</value>
<value>不透明度</value>
</data>
<data name="SettingsPageOpenFolderButton.Content" xml:space="preserve">
<value>在檔案總管中開啟</value>
@@ -1248,12 +1305,18 @@
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>無法在您的電腦中找到該路徑</value>
</data>
<data name="SettingsPagePatrons.Text" xml:space="preserve">
<value>贊助</value>
</data>
<data name="SettingsPagePhonetic.Text" xml:space="preserve">
<value>歌詞注音</value>
</data>
<data name="SettingsPagePhoneticText.Header" xml:space="preserve">
<value>注音</value>
</data>
<data name="SettingsPagePinToTaskbar.Header" xml:space="preserve">
<value>釘選到任務列</value>
</data>
<data name="SettingsPagePinyin.Content" xml:space="preserve">
<value>普通話拼音</value>
</data>
@@ -1263,9 +1326,6 @@
<data name="SettingsPagePlaybackNotFound.Text" xml:space="preserve">
<value>沒有捕獲的播放源</value>
</data>
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
<value>播放</value>
</data>
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
<value>播放源</value>
</data>
@@ -1371,6 +1431,9 @@
<data name="SettingsPageShareHub.Content" xml:space="preserve">
<value>瀏覽線上資源分享中心</value>
</data>
<data name="SettingsPageShortcut.Text" xml:space="preserve">
<value>快速鍵</value>
</data>
<data name="SettingsPageShortcutRegFailInfo" xml:space="preserve">
<value>該熱鍵未成功註冊</value>
</data>
@@ -1443,6 +1506,9 @@
<data name="SettingsPageStandardMode.Text" xml:space="preserve">
<value>標準模式</value>
</data>
<data name="SettingsPageStartup.Text" xml:space="preserve">
<value>啟動</value>
</data>
<data name="SettingsPageStrokeFontColor.Header" xml:space="preserve">
<value>描邊顏色</value>
</data>
@@ -1452,12 +1518,24 @@
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>目標語言</value>
</data>
<data name="SettingsPageTaskbarMode.Text" xml:space="preserve">
<value>任務列模式</value>
</data>
<data name="SettingsPageTaskbarPlacement.Header" xml:space="preserve">
<value>工作列固定位置</value>
</data>
<data name="SettingsPageTC.Content" xml:space="preserve">
<value>繁體中文</value>
</data>
<data name="SettingsPageTelegram.Content" xml:space="preserve">
<value>Telegram</value>
</data>
<data name="SettingsPageThanksForPurchasing.Text" xml:space="preserve">
<value>感謝您購買 BetterLyrics</value>
</data>
<data name="SettingsPageThanksList.Header" xml:space="preserve">
<value>致謝名單</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>歌詞背景主題</value>
</data>
@@ -1503,6 +1581,9 @@
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>透明</value>
</data>
<data name="SettingsPageUserWhoPurchased.Text" xml:space="preserve">
<value>以及購買支援BetterLyrics 的用戶</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>版本號</value>
</data>
@@ -1527,6 +1608,9 @@
<data name="SettingsPageWorkAreaHeight.Header" xml:space="preserve">
<value>工作區域高度</value>
</data>
<data name="SettingsPageYouNowUsing.Text" xml:space="preserve">
<value>正在使用該軟體的你</value>
</data>
<data name="StandardMode" xml:space="preserve">
<value>標準模式</value>
</data>
@@ -1557,6 +1641,9 @@
<data name="SystemTraySwitch.Text" xml:space="preserve">
<value>歌詞視窗切換器</value>
</data>
<data name="TaskbarMode" xml:space="preserve">
<value>任務列模式</value>
</data>
<data name="TermsOfService.Content" xml:space="preserve">
<value>服務條款</value>
</data>

View File

@@ -2,7 +2,6 @@
using BetterLyrics.WinUI3.Helper.BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.ResourceService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -16,27 +15,20 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class AboutControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly IResourceService _resourceService;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsDebugOverlayEnabled { get; set; } = false;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public AboutControlViewModel(ISettingsService settingsService, IResourceService resourceService)
public AboutControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
_resourceService = resourceService;
AppSettings = _settingsService.AppSettings;
}
[RelayCommand]
private async Task LaunchProjectGitHubPageAsync()
{
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GitHub));
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.BetterLyricsGitHub));
}
[RelayCommand]

View File

@@ -180,8 +180,7 @@ namespace BetterLyrics.WinUI3.ViewModels
if (value?.Raw != null)
{
var lyricsParser = new LyricsParser();
lyricsParser.Parse(_mediaSessionsService.CurrentSongInfo, value);
LyricsDataArr = [.. lyricsParser.LyricsDataArr];
LyricsDataArr = [.. lyricsParser.Parse(value)];
}
else
{

View File

@@ -1,30 +1,27 @@
using BetterLyrics.WinUI3.Extensions;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System.Linq;
using System.Numerics;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class LyricsWindowSettingsControlViewModel : BaseViewModel
public partial class LyricsWindowSettingsControlViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<bool>>
{
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
[ObservableProperty]
public partial LiveStates LiveStates { get; set; }
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
[ObservableProperty]
public partial object ListViewSelectedItemTag { get; set; } = "General";
public partial object SelectorBarSelectedItemTag { get; set; } = "AlbumArtStyle";
[ObservableProperty]
public partial bool IsConfigPanelOpened { get; set; } = false;
@@ -35,13 +32,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial double DisplayPanelHeight { get; set; } = 0;
public LyricsWindowSettingsControlViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
public LyricsWindowSettingsControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
_liveStatesService = liveStatesService;
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
}
[RelayCommand]
@@ -75,7 +70,12 @@ namespace BetterLyrics.WinUI3.ViewModels
}
[RelayCommand]
private void OpenConfigPanel()
private void CreateTaskbarLyricsWindowStatus()
{
AppSettings.WindowBoundsRecords.Add(LyricsWindowStatusExtensions.TaskbarMode());
}
public void OpenConfigPanel()
{
IsConfigPanelOpened = true;
ConfigPanelTranslation = new();
@@ -99,5 +99,27 @@ namespace BetterLyrics.WinUI3.ViewModels
CloseConfigPanel();
}
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.MultiNowPlayingWindowMode))
{
if (!message.NewValue && AppSettings.WindowBoundsRecords.Count(x => x.IsOpened) > 0)
{
var windows = WindowHook.GetWindows<NowPlayingWindow>();
var latest = windows.Last();
foreach (var item in windows)
{
if (item != latest)
{
item.CloseWindow();
}
}
}
}
}
}
}
}

View File

@@ -1,6 +1,4 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
@@ -9,21 +7,14 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class LyricsWindowSwitchControlViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
[ObservableProperty]
public partial LiveStates LiveStates { get; set; }
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public LyricsWindowSwitchControlViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
public LyricsWindowSwitchControlViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
_liveStatesService = liveStatesService;
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
}
}

View File

@@ -30,7 +30,7 @@ using Windows.Storage;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class MusicGalleryViewModel : BaseViewModel
public partial class MusicGalleryPageViewModel : BaseViewModel
{
private readonly ILibWatcherService _libWatcherService;
private readonly ISettingsService _settingsService;
@@ -72,6 +72,9 @@ namespace BetterLyrics.WinUI3.ViewModels
public PlayQueueItem? PlayingQueueItem => TrackPlayingQueue.ElementAtOrDefault(AppSettings.MusicGallerySettings.PlayQueueIndex);
[ObservableProperty]
public partial Track? PlayingTrack { get; set; } = null;
[ObservableProperty]
public partial CommonSongProperty SongOrderType { get; set; } = CommonSongProperty.Title;
@@ -92,7 +95,7 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial string SongSearchQuery { get; set; } = string.Empty;
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IResourceService resourceService)
public MusicGalleryPageViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService, IResourceService resourceService)
{
_refreshSongsTimer = _dispatcherQueue.CreateTimer();
@@ -127,11 +130,6 @@ namespace BetterLyrics.WinUI3.ViewModels
_libWatcherService = libWatcherService;
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
if (AppSettings.MusicGallerySettings.AutoPlay)
{
_ = PlayTrackAtAsync(AppSettings.MusicGallerySettings.PlayQueueIndex);
}
}
private void TrackPlayingQueue_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
@@ -376,6 +374,9 @@ namespace BetterLyrics.WinUI3.ViewModels
t.Title.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
t.Artist.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
t.Album.Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
// 文件名(包含后缀)
t.GetFileName().Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase) ||
// 文件所在文件夹的路径
t.GetParentFolderPath().Contains(SongSearchQuery, StringComparison.OrdinalIgnoreCase)).ToList();
}
@@ -440,20 +441,20 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else
{
var track = playQueueItem.Track;
PlayingTrack = playQueueItem.Track;
var updater = _smtc.DisplayUpdater;
updater.ClearAll();
_smtc.IsEnabled = true;
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(PlayingTrack.Path));
var storageFile = await StorageFile.GetFileFromPathAsync(track.Path);
var storageFile = await StorageFile.GetFileFromPathAsync(PlayingTrack.Path);
await updater.CopyFromFileAsync(MediaPlaybackType.Music, storageFile);
updater.AppMediaId = Package.Current.Id.FullName;
updater.MusicProperties.AlbumTitle = track.Album;
updater.MusicProperties.Genres.Add($"{ExtendedGenreFiled.FileName}{Path.GetFileNameWithoutExtension(track.Path)}");
updater.MusicProperties.AlbumTitle = PlayingTrack.Album;
updater.MusicProperties.Genres.Add($"{ExtendedGenreFiled.FileName}{Path.GetFileNameWithoutExtension(PlayingTrack.Path)}");
updater.Update();
}
}

View File

@@ -0,0 +1,22 @@
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class MusicGalleryWindowViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
public partial AppSettings AppSettings { get; set; }
public MusicGalleryWindowViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
AppSettings = _settingsService.AppSettings;
}
}
}

View File

@@ -0,0 +1,89 @@
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class NowPlayingBarViewModel : BaseViewModel
{
public IMediaSessionsService MediaSessionsService { get; private set; }
[ObservableProperty]
public partial int Volume { get; set; }
[ObservableProperty]
public partial float TimelineSliderThumbOpacity { get; set; } = 0f;
[ObservableProperty]
public partial LyricsLine? TimelineSliderThumbLyricsLine { get; set; }
[ObservableProperty]
public partial double TimelineSliderThumbSeconds { get; set; } = 0;
[ObservableProperty]
public partial double BottomCommandGridOpacity { get; set; } = 1;
[ObservableProperty]
public partial double BottomCommandFlyoutTriggerOpacity { get; set; }
public NowPlayingBarViewModel(IMediaSessionsService mediaSessionsService)
{
MediaSessionsService = mediaSessionsService;
Volume = SystemVolumeHook.MasterVolume;
SystemVolumeHook.VolumeNotification += SystemVolumeHelper_VolumeNotification;
}
private void SystemVolumeHelper_VolumeNotification(object? sender, int e)
{
Volume = e;
}
partial void OnTimelineSliderThumbSecondsChanged(double value)
{
TimelineSliderThumbLyricsLine = MediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
}
[RelayCommand]
private async Task PlaySongAsync()
{
await MediaSessionsService.PlayAsync();
}
[RelayCommand]
private async Task PauseSongAsync()
{
await MediaSessionsService.PauseAsync();
}
[RelayCommand]
private async Task PreviousSongAsync()
{
await MediaSessionsService.PreviousAsync();
}
[RelayCommand]
private async Task NextSongAsync()
{
await MediaSessionsService.NextAsync();
}
[RelayCommand]
private static void OpenSettingsWindow()
{
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
[RelayCommand]
private static void OpenLyricsSearchWindow()
{
WindowHook.OpenOrShowWindow<LyricsSearchWindow>();
}
}
}

View File

@@ -1,61 +1,20 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class NowPlayingPageViewModel : BaseViewModel
{
public IMediaSessionsService MediaSessionsService { get; private set; }
private readonly ILiveStatesService _liveStatesService;
[ObservableProperty]
public partial LiveStates LiveStates { get; set; }
[ObservableProperty]
public partial int Volume { get; set; }
[ObservableProperty]
public partial double BottomCommandGridOpacity { get; set; }
[ObservableProperty]
public partial double BottomCommandFlyoutTriggerOpacity { get; set; }
[ObservableProperty]
public partial float TimelineSliderThumbOpacity { get; set; } = 0f;
[ObservableProperty]
public partial LyricsLine? TimelineSliderThumbLyricsLine { get; set; }
[ObservableProperty]
public partial double TimelineSliderThumbSeconds { get; set; } = 0;
public NowPlayingPageViewModel(IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
public NowPlayingPageViewModel(IMediaSessionsService mediaSessionsService)
{
_liveStatesService = liveStatesService;
MediaSessionsService = mediaSessionsService;
LiveStates = _liveStatesService.LiveStates;
Volume = SystemVolumeHook.MasterVolume;
SystemVolumeHook.VolumeNotification += SystemVolumeHelper_VolumeNotification;
}
private void SystemVolumeHelper_VolumeNotification(object? sender, int e)
{
Volume = e;
}
partial void OnTimelineSliderThumbSecondsChanged(double value)
{
TimelineSliderThumbLyricsLine = MediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
}
[RelayCommand]
@@ -64,29 +23,5 @@ namespace BetterLyrics.WinUI3.ViewModels
WindowHook.OpenOrShowWindow<SettingsWindow>();
}
[RelayCommand]
private async Task PlaySongAsync()
{
await MediaSessionsService.PlayAsync();
}
[RelayCommand]
private async Task PauseSongAsync()
{
await MediaSessionsService.PauseAsync();
}
[RelayCommand]
private async Task PreviousSongAsync()
{
await MediaSessionsService.PreviousAsync();
}
[RelayCommand]
private async Task NextSongAsync()
{
await MediaSessionsService.NextAsync();
}
}
}

View File

@@ -1,231 +1,28 @@
// 2025/6/23 by Zhe Fang
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Hooks;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using System.Collections.Generic;
using Windows.UI;
using WinRT.Interop;
using WinUIEx;
namespace BetterLyrics.WinUI3
{
public partial class NowPlayingWindowViewModel
: BaseWindowViewModel,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<List<string>>>,
IRecipient<PropertyChangedMessage<AlbumArtThemeColors>>
public partial class NowPlayingWindowViewModel : BaseWindowViewModel
{
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
private ForegroundWindowHook? _fgWindowWatcher = null;
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
public NowPlayingWindowViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
public NowPlayingWindowViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
_liveStatesService = liveStatesService;
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
}
[ObservableProperty] public partial AppSettings AppSettings { get; set; }
[ObservableProperty] public partial LiveStates LiveStates { get; set; }
/// <summary>
/// 歌词窗口所在的背景主题色
/// </summary>
[ObservableProperty][NotifyPropertyChangedRecipients] public partial Color BackdropAccentColor { get; set; }
[ObservableProperty] public partial double TopCommandGridOpacity { get; set; } = 0;
[ObservableProperty] public partial ElementTheme ThemeType { get; set; } = ElementTheme.Default;
[ObservableProperty] public partial double TitleBarFontSize { get; set; } = 12;
[ObservableProperty] public partial double TitleBarFontSize { get; set; } = 14;
public void InitShortcuts()
{
UpdateLyricsWindowBorderlessShortcut();
UpdateLyricsWindowClickThroughShortcut();
UpdateLyricsWindowShowHideShortcut();
UpdateLyricsWindowSwitchShortcut();
}
private void UpdateLyricsWindowShowHideShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.LyricsWindowShowOrHide,
_settingsService.AppSettings.GeneralSettings.ShowOrHideLyricsWindowShortcut,
() =>
{
var window = WindowHook.GetWindow<NowPlayingWindow>();
if (window == null) return;
if (window.Visible)
{
window.Hide();
}
else
{
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
}
}
);
}
private void UpdateLyricsWindowBorderlessShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.Borderless,
_settingsService.AppSettings.GeneralSettings.BorderlessShortcut,
() =>
{
LiveStates.LyricsWindowStatus.IsBorderless = !LiveStates.LyricsWindowStatus.IsBorderless;
}
);
}
private void UpdateLyricsWindowClickThroughShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.ClickThrough,
_settingsService.AppSettings.GeneralSettings.ClickThroughShortcut,
() =>
{
LiveStates.LyricsWindowStatus.IsClickThrough = !LiveStates.LyricsWindowStatus.IsClickThrough;
}
);
}
private void UpdateLyricsWindowSwitchShortcut()
{
GlobalHotKeyHook.UpdateHotKey<NowPlayingWindow>(ShortcutID.LyricsWindowSwitch,
_settingsService.AppSettings.GeneralSettings.LyricsWindowSwitchShortcut,
() =>
{
WindowHook.OpenOrShowWindow<LyricsWindowSwitchWindow>();
}
);
}
public void InitFgWindowWatcher()
{
var window = WindowHook.GetWindow<NowPlayingWindow>();
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
_fgWindowWatcherTimer = _dispatcherQueue.CreateTimer();
_fgWindowWatcher = new ForegroundWindowHook(
hwnd,
fgHwnd =>
{
_fgWindowWatcherTimer.Debounce(() =>
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTop &&
_liveStatesService.LiveStates.LyricsWindowStatus.IsAlwaysOnTopPolling &&
window.AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsAlwaysOnTop = true;
}
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsAdaptToEnvironment)
{
UpdateBackdropAccentColor(hwnd);
}
}, Constants.Time.DebounceTimeout);
}
);
_fgWindowWatcher.Start();
UpdateBackdropAccentColor(hwnd);
}
public void UpdateBackdropAccentColor(nint hwnd)
{
BackdropAccentColor = ColorHelper.GetAccentColor(
hwnd,
_liveStatesService.LiveStates.LyricsWindowStatus.MonitorDeviceName,
_liveStatesService.LiveStates.LyricsWindowStatus.EnvironmentSampleMode);
}
public void ExitOrClose()
{
if (_settingsService.AppSettings.GeneralSettings.ExitOnLyricsWindowClosed)
{
WindowHook.ExitApp();
}
else
{
var window = WindowHook.GetWindow<NowPlayingWindow>();
window?.Hide();
}
}
public void Receive(PropertyChangedMessage<List<string>> message)
{
if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.ClickThroughShortcut))
{
UpdateLyricsWindowClickThroughShortcut();
}
}
else if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.BorderlessShortcut))
{
UpdateLyricsWindowBorderlessShortcut();
}
}
else if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.ShowOrHideLyricsWindowShortcut))
{
UpdateLyricsWindowShowHideShortcut();
}
}
else if (message.Sender is GeneralSettings)
{
if (message.PropertyName == nameof(GeneralSettings.LyricsWindowSwitchShortcut))
{
UpdateLyricsWindowSwitchShortcut();
}
}
}
public void Receive(PropertyChangedMessage<AlbumArtThemeColors> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.AlbumArtThemeColors))
{
ThemeType = message.NewValue.ThemeType;
}
}
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentIsPlaying))
{
WindowHook.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
}
}
}
}
}

View File

@@ -43,12 +43,6 @@ namespace BetterLyrics.WinUI3.ViewModels
WindowHook.OpenOrShowWindow<MusicGalleryWindow>();
}
[RelayCommand]
private static void OpenLyrics()
{
WindowHook.OpenOrShowWindow<NowPlayingWindow>();
}
[RelayCommand]
private static void OpenLyricsWindowSwitch()
{

View File

@@ -25,7 +25,7 @@ namespace BetterLyrics.WinUI3.Views
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHook.CloseWindow<LyricsSearchWindow>();
this.CloseWindow();
}
}

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