Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0606711023 | ||
|
|
8f997ca3d9 | ||
|
|
042d557eb1 | ||
|
|
153679228d | ||
|
|
5a4fe54ac2 | ||
|
|
14e8d88cd1 | ||
|
|
79e726db33 | ||
|
|
d4f1949833 | ||
|
|
6135f996bf | ||
|
|
5b97621af4 | ||
|
|
0f084d04f8 | ||
|
|
bcb114a171 | ||
|
|
7bbe2a3045 | ||
|
|
fab4e8fe22 | ||
|
|
bc4a06577e | ||
|
|
22a790fff0 | ||
|
|
5e3d0cb78f | ||
|
|
59ee2a6fc3 | ||
|
|
fd5e752b43 | ||
|
|
df31d03da7 | ||
|
|
a7ebba4a62 | ||
|
|
6ca1a84a54 | ||
|
|
bab5a827f6 | ||
|
|
3b1e0389aa | ||
|
|
ae05a55d31 | ||
|
|
4b6d417db3 |
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [jayfunc]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: founchoo
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
69
.github/workflows/pages.yml
vendored
@@ -1,69 +0,0 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
|
||||
name: Deploy Jekyll site to Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dev", "stable"]
|
||||
paths:
|
||||
- "docs/**"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Build job
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: docs
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: "3.3"
|
||||
bundler-cache: true
|
||||
cache-version: 0
|
||||
working-directory: "${{ github.workspace }}/docs"
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Build with Jekyll
|
||||
# Outputs to the './_site' directory by default
|
||||
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
|
||||
env:
|
||||
JEKYLL_ENV: production
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs/_site/
|
||||
|
||||
# Deployment job
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
6
.github/workflows/releases-to-discord.yml
vendored
@@ -1,6 +1,6 @@
|
||||
on:
|
||||
release:
|
||||
types: [published, edited]
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
github-releases-to-discord:
|
||||
@@ -13,8 +13,8 @@ jobs:
|
||||
with:
|
||||
webhook_url: ${{ secrets.WEBHOOK_URL }}
|
||||
color: "2105893"
|
||||
username: "Release Changelog"
|
||||
avatar_url: "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png"
|
||||
username: "GitHub"
|
||||
avatar_url: "https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png"
|
||||
content: "||@everyone||"
|
||||
footer_title: "Changelog"
|
||||
reduce_headings: true
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.11.0" />
|
||||
Version="1.0.16.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<Setter Property="CornerRadius" Value="4" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="16,0" />
|
||||
<Setter Property="Padding" Value="10,0" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="GhostToggleButtonStyle" TargetType="ToggleButton">
|
||||
@@ -92,11 +92,21 @@
|
||||
<Setter Property="Padding" Value="8" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
<Style x:Key="CardGridStyle" TargetType="Grid">
|
||||
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="CornerRadius" Value="6" />
|
||||
</Style>
|
||||
|
||||
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />
|
||||
|
||||
<!-- Dimensions -->
|
||||
|
||||
<!-- Fonts -->
|
||||
<FontFamily x:Key="IconFontFamily">Segoe Fluent Icons, Segoe MDL2 Assets</FontFamily>
|
||||
<FontFamily x:Key="IconFontFamily">ms-appx:///Assets/Segoe Fluent Icons.ttf#Segoe Fluent Icons</FontFamily>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace BetterLyrics.WinUI3
|
||||
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (lyricsWindow == null) return;
|
||||
|
||||
lyricsWindow.ViewModel.InitLockHotKey();
|
||||
lyricsWindow.AutoSelectLyricsMode();
|
||||
}
|
||||
|
||||
@@ -84,6 +85,7 @@ namespace BetterLyrics.WinUI3
|
||||
.AddSingleton<ILyricsSearchService, LyricsSearchService>()
|
||||
.AddSingleton<ILibWatcherService, LibWatcherService>()
|
||||
.AddSingleton<ITranslateService, TranslateService>()
|
||||
// Manager
|
||||
// ViewModels
|
||||
.AddSingleton<LyricsWindowViewModel>()
|
||||
.AddSingleton<SettingsWindowViewModel>()
|
||||
|
||||
@@ -1,118 +1,127 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>BetterLyrics.WinUI3</RootNamespace>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="ViewModels\Lyrics\**" />
|
||||
<Content Remove="ViewModels\Lyrics\**" />
|
||||
<EmbeddedResource Remove="ViewModels\Lyrics\**" />
|
||||
<None Remove="ViewModels\Lyrics\**" />
|
||||
<Page Remove="ViewModels\Lyrics\**" />
|
||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Core14.profile.xml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Views\SettingsWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Logo.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250513-build.2126" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.6" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.6" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.1.6" />
|
||||
<PackageReference Include="WinUIEx" Version="2.6.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\InAppLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\DesktopLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!--Disable Trimming for Specific Packages-->
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\SettingsWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\SystemTray.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants)</DefineConstants>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Logo.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ShouldCreateLogs>True</ShouldCreateLogs>
|
||||
<AdvancedSettingsExpanded>True</AdvancedSettingsExpanded>
|
||||
<UpdateAssemblyVersion>False</UpdateAssemblyVersion>
|
||||
<UpdateAssemblyFileVersion>False</UpdateAssemblyFileVersion>
|
||||
<UpdateAssemblyInfoVersion>False</UpdateAssemblyInfoVersion>
|
||||
<UpdatePackageVersion>True</UpdatePackageVersion>
|
||||
<AssemblyInfoVersionType>SettingsVersion</AssemblyInfoVersionType>
|
||||
<InheritWinAppVersionFrom>AssemblyVersion</InheritWinAppVersionFrom>
|
||||
<PackageVersionSettings>AssemblyVersion.None.None</PackageVersionSettings>
|
||||
<Version>2025.6.0</Version>
|
||||
<AssemblyVersion>2025.6.18.0110</AssemblyVersion>
|
||||
<FileVersion>2025.6.18.0110</FileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>BetterLyrics.WinUI3</RootNamespace>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="ViewModels\Lyrics\**" />
|
||||
<Content Remove="ViewModels\Lyrics\**" />
|
||||
<EmbeddedResource Remove="ViewModels\Lyrics\**" />
|
||||
<None Remove="ViewModels\Lyrics\**" />
|
||||
<Page Remove="ViewModels\Lyrics\**" />
|
||||
<PRIResource Remove="ViewModels\Lyrics\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Core14.profile.xml" />
|
||||
<None Remove="Assets\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Views\SettingsWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Logo.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="3v.EvtSource" Version="2.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.MarqueeText" Version="0.1.230830" />
|
||||
<PackageReference Include="CommunityToolkit.Labs.WinUI.OpacityMaskView" Version="0.1.250513-build.2126" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Helpers" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Media" Version="8.2.250402" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
|
||||
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
<PackageReference Include="Nito.AsyncEx.Tasks" Version="5.1.2" />
|
||||
<PackageReference Include="NTextCat" Version="0.3.65" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="ShadowViewer.Controls.Notification" Version="1.2.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.6" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.6" />
|
||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
|
||||
<PackageReference Include="Vanara.PInvoke.CoreAudio" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.Gdi32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.1.6" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.1.6" />
|
||||
<PackageReference Include="WinUIEx" Version="2.6.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\InAppLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Rendering\DesktopLyricsRenderer.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!--Disable Trimming for Specific Packages-->
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="TagLibSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Segoe Fluent Icons.ttf">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Views\SettingsWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\SystemTray.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefineConstants>$(DefineConstants)</DefineConstants>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Logo.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ShouldCreateLogs>True</ShouldCreateLogs>
|
||||
<AdvancedSettingsExpanded>True</AdvancedSettingsExpanded>
|
||||
<UpdateAssemblyVersion>False</UpdateAssemblyVersion>
|
||||
<UpdateAssemblyFileVersion>False</UpdateAssemblyFileVersion>
|
||||
<UpdateAssemblyInfoVersion>False</UpdateAssemblyInfoVersion>
|
||||
<UpdatePackageVersion>True</UpdatePackageVersion>
|
||||
<AssemblyInfoVersionType>SettingsVersion</AssemblyInfoVersionType>
|
||||
<InheritWinAppVersionFrom>AssemblyVersion</InheritWinAppVersionFrom>
|
||||
<PackageVersionSettings>AssemblyVersion.None.None</PackageVersionSettings>
|
||||
<Version>2025.6.0</Version>
|
||||
<AssemblyVersion>2025.6.18.0110</AssemblyVersion>
|
||||
<FileVersion>2025.6.18.0110</FileVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum DockPlacement
|
||||
{
|
||||
Top,
|
||||
Bottom
|
||||
}
|
||||
|
||||
public static class DockPlacementExtensions
|
||||
{
|
||||
public static WindowPixelSampleMode ToWindowPixelSampleMode(this DockPlacement placement)
|
||||
{
|
||||
return placement switch
|
||||
{
|
||||
DockPlacement.Top => WindowPixelSampleMode.BelowWindow,
|
||||
DockPlacement.Bottom => WindowPixelSampleMode.AboveWindow,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(placement), placement, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,5 @@ namespace BetterLyrics.WinUI3.Enums
|
||||
AlbumArtOnly,
|
||||
LyricsOnly,
|
||||
SplitView,
|
||||
PlaceholderOnly,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
public enum WindowPixelSampleMode
|
||||
{
|
||||
BelowWindow,
|
||||
AboveWindow,
|
||||
WindowArea,
|
||||
WindowEdge,
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ using Windows.UI;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Events
|
||||
{
|
||||
public class AlbumArtChangedEventArgs : EventArgs
|
||||
public class AlbumArtChangedEventArgs(SoftwareBitmap? albumArtSwBitmap, Color? albumArtAccentColor) : EventArgs
|
||||
{
|
||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = null;
|
||||
public Color? AlbumArtAccentColor { get; set; } = null;
|
||||
public SoftwareBitmap? AlbumArtSwBitmap { get; set; } = albumArtSwBitmap;
|
||||
public Color? AlbumArtAccentColor { get; set; } = albumArtAccentColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
int sampleY = myRect.Bottom + 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
case WindowPixelSampleMode.AboveWindow:
|
||||
{
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int sampleHeight = 1;
|
||||
int sampleY = myRect.Top - 1;
|
||||
return GetAverageColorFromScreenRegion(0, sampleY, screenWidth, sampleHeight);
|
||||
}
|
||||
case WindowPixelSampleMode.WindowArea:
|
||||
{
|
||||
int width = myRect.Right - myRect.Left;
|
||||
|
||||
@@ -5,6 +5,8 @@ using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using WinRT.Interop;
|
||||
@@ -17,8 +19,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
private static readonly Dictionary<IntPtr, bool> _originalTopmostStates = [];
|
||||
private static readonly Dictionary<IntPtr, nint> _oldWndProcs = new();
|
||||
private static readonly Dictionary<IntPtr, (double X, double Y, double Width, double Height)> _originalWindowBounds = [];
|
||||
private static readonly Dictionary<IntPtr, WindowStyle> _originalWindowStyles = [];
|
||||
private static List<Rectangle> _interactiveRects = new();
|
||||
|
||||
private delegate nint WndProcDelegate(nint hWnd, uint msg, nint wParam, nint lParam);
|
||||
|
||||
@@ -90,6 +94,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
int exStyle = User32.GetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE);
|
||||
|
||||
if (enable)
|
||||
{
|
||||
// <20><><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD>ʽ
|
||||
@@ -98,6 +103,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
window.ToggleWindowStyle(true, WindowStyle.Popup | WindowStyle.Visible);
|
||||
User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWL_EXSTYLE, exStyle | (int)User32.WindowStylesEx.WS_EX_TRANSPARENT | (int)User32.WindowStylesEx.WS_EX_LAYERED);
|
||||
|
||||
//// <20><>װ<EFBFBD>Զ<EFBFBD><D4B6><EFBFBD>WndProc
|
||||
//if (!_oldWndProcs.ContainsKey(hwnd))
|
||||
//{
|
||||
// nint newWndProc = Marshal.GetFunctionPointerForDelegate((WndProcDelegate)((hWnd, msg, wParam, lParam) =>
|
||||
// CustomWndProc(hWnd, msg, wParam, lParam, hwnd)
|
||||
// ));
|
||||
// nint oldWndProc = User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWLP_WNDPROC, newWndProc);
|
||||
// _oldWndProcs[hwnd] = oldWndProc;
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -108,7 +123,51 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
window.SetWindowStyle(style);
|
||||
_originalWindowStyles.Remove(hwnd);
|
||||
}
|
||||
|
||||
//// <20>ָ<EFBFBD>ԭWndProc
|
||||
//if (_oldWndProcs.TryGetValue(hwnd, out var oldWndProc))
|
||||
//{
|
||||
// User32.SetWindowLong(hwnd, User32.WindowLongFlags.GWLP_WNDPROC, oldWndProc);
|
||||
// _oldWndProcs.Remove(hwnd);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private static nint CustomWndProc(nint hWnd, uint msg, nint wParam, nint lParam, IntPtr hwnd)
|
||||
{
|
||||
const int WM_NCHITTEST = 0x84;
|
||||
const int HTCLIENT = 1;
|
||||
const int HTTRANSPARENT = -1;
|
||||
|
||||
if (msg == WM_NCHITTEST)
|
||||
{
|
||||
int x = (short)(lParam.ToInt32() & 0xFFFF);
|
||||
int y = (short)((lParam.ToInt32() >> 16) & 0xFFFF);
|
||||
|
||||
// תΪ<D7AA><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
POINT pt = new() { x = x, y = y };
|
||||
User32.ScreenToClient(hWnd, ref pt);
|
||||
|
||||
foreach (var rect in _interactiveRects)
|
||||
{
|
||||
if (rect.Contains(pt.x, pt.y))
|
||||
return HTCLIENT;
|
||||
}
|
||||
return HTTRANSPARENT;
|
||||
}
|
||||
|
||||
// <20><><EFBFBD><EFBFBD>ԭWndProc
|
||||
if (_oldWndProcs.TryGetValue(hwnd, out var oldWndProc))
|
||||
{
|
||||
return User32.CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam);
|
||||
}
|
||||
return User32.DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
public static void SetInteractiveRects(IEnumerable<Rectangle> rects)
|
||||
{
|
||||
_interactiveRects = rects.ToList();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -20,11 +22,16 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public static void Disable(Window window)
|
||||
{
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
|
||||
if (!_registered.Contains(hwnd)) return;
|
||||
|
||||
window.SetIsShownInSwitchers(true);
|
||||
window.ExtendsContentIntoTitleBar = true;
|
||||
window.SetIsAlwaysOnTop(false);
|
||||
|
||||
IntPtr hwnd = WindowNative.GetWindowHandle(window);
|
||||
UnregisterAppBar(hwnd);
|
||||
RefreshWorkArea();
|
||||
|
||||
window.SetWindowStyle(_originalWindowStyle[hwnd]);
|
||||
_originalWindowStyle.Remove(hwnd);
|
||||
@@ -42,11 +49,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
);
|
||||
_originalPositions.Remove(hwnd);
|
||||
}
|
||||
|
||||
UnregisterAppBar(hwnd);
|
||||
}
|
||||
|
||||
public static void Enable(Window window, int appBarHeight)
|
||||
public static void Enable(Window window, int appBarHeight, DockPlacement dockPlacement)
|
||||
{
|
||||
window.SetIsShownInSwitchers(false);
|
||||
window.ExtendsContentIntoTitleBar = false;
|
||||
@@ -68,36 +73,44 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
RegisterAppBar(hwnd, appBarHeight);
|
||||
RegisterAppBar(hwnd, appBarHeight, dockPlacement);
|
||||
|
||||
int screenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN);
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int y = dockPlacement == DockPlacement.Top ? 0 : screenHeight - appBarHeight;
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
0,
|
||||
y,
|
||||
screenWidth,
|
||||
appBarHeight,
|
||||
User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
);
|
||||
|
||||
RefreshWorkArea();
|
||||
}
|
||||
|
||||
private static void RegisterAppBar(IntPtr hwnd, int height)
|
||||
private static void RegisterAppBar(IntPtr hwnd, int height, DockPlacement dockPlacement)
|
||||
{
|
||||
if (_registered.Contains(hwnd)) return;
|
||||
|
||||
var uEdge = dockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int top = dockPlacement == DockPlacement.Top ? 0 : screenHeight - height;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? height : screenHeight;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf<Shell32.APPBARDATA>(),
|
||||
hWnd = hwnd,
|
||||
uEdge = Shell32.ABE.ABE_TOP,
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = 0,
|
||||
Top = 0,
|
||||
Top = top,
|
||||
Right = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
Bottom = height,
|
||||
Bottom = bottom,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -119,40 +132,57 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
};
|
||||
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_REMOVE, ref abd);
|
||||
|
||||
_registered.Remove(hwnd);
|
||||
}
|
||||
|
||||
public static void UpdateAppBarHeight(IntPtr hwnd, int newHeight)
|
||||
private static void RefreshWorkArea()
|
||||
{
|
||||
if (!_registered.Contains(hwnd))
|
||||
return;
|
||||
User32.SendMessage(HWND.HWND_BROADCAST, User32.WindowMessage.WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
public static void UpdateAppBarHeight(IntPtr hwnd, int newHeight, DockPlacement dockPlacement)
|
||||
{
|
||||
App.DispatcherQueueTimer?.Debounce(() =>
|
||||
{
|
||||
cbSize = (uint)Marshal.SizeOf<Shell32.APPBARDATA>(),
|
||||
hWnd = hwnd,
|
||||
uEdge = Shell32.ABE.ABE_TOP,
|
||||
rc = new RECT
|
||||
if (!_registered.Contains(hwnd))
|
||||
return;
|
||||
|
||||
var uEdge = dockPlacement == DockPlacement.Top ? Shell32.ABE.ABE_TOP : Shell32.ABE.ABE_BOTTOM;
|
||||
int screenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYSCREEN);
|
||||
int top = dockPlacement == DockPlacement.Top ? 0 : screenHeight - newHeight;
|
||||
int bottom = dockPlacement == DockPlacement.Top ? newHeight : screenHeight;
|
||||
|
||||
Shell32.APPBARDATA abd = new()
|
||||
{
|
||||
Left = 0,
|
||||
Top = 0,
|
||||
Right = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
Bottom = newHeight,
|
||||
},
|
||||
};
|
||||
cbSize = (uint)Marshal.SizeOf<Shell32.APPBARDATA>(),
|
||||
hWnd = hwnd,
|
||||
uEdge = uEdge,
|
||||
rc = new RECT
|
||||
{
|
||||
Left = 0,
|
||||
Top = top,
|
||||
Right = User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
Bottom = bottom,
|
||||
},
|
||||
};
|
||||
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
Shell32.SHAppBarMessage(Shell32.ABM.ABM_SETPOS, ref abd);
|
||||
|
||||
// 同步窗口实际高度
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
0,
|
||||
User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
newHeight,
|
||||
User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
);
|
||||
// 同步窗口实际高度和位置
|
||||
int y = dockPlacement == DockPlacement.Top ? 0 : screenHeight - newHeight;
|
||||
User32.SetWindowPos(
|
||||
hwnd,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
y,
|
||||
User32.GetSystemMetrics(User32.SystemMetric.SM_CXSCREEN),
|
||||
newHeight,
|
||||
User32.SetWindowPosFlags.SWP_SHOWWINDOW
|
||||
);
|
||||
|
||||
RefreshWorkArea();
|
||||
}, TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.System;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class GlobalHotKeyHelper
|
||||
{
|
||||
private static Dictionary<int, Action> _hotKeyActions = [];
|
||||
private static int _nextId = 0;
|
||||
|
||||
public static void RegisterHotKey(Window window, User32.HotKeyModifiers modifiers, uint key, Action action)
|
||||
{
|
||||
HWND hwnd = WindowNative.GetWindowHandle(window);
|
||||
int id = _nextId++;
|
||||
User32.RegisterHotKey(hwnd, id, modifiers, key);
|
||||
_hotKeyActions[id] = action;
|
||||
}
|
||||
|
||||
public static void UnregisterAllHotKeys(Window window)
|
||||
{
|
||||
HWND hwnd = WindowNative.GetWindowHandle(window);
|
||||
foreach (var id in _hotKeyActions.Keys.ToList())
|
||||
{
|
||||
User32.UnregisterHotKey(hwnd, id);
|
||||
_hotKeyActions.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryInvokeAction(int id)
|
||||
{
|
||||
if (_hotKeyActions.TryGetValue(id, out var action))
|
||||
{
|
||||
action?.Invoke();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,14 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using CommunityToolkit.WinUI.Helpers;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -7,10 +16,6 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI;
|
||||
@@ -30,62 +35,33 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return stream;
|
||||
}
|
||||
|
||||
public static async Task<byte[]> CreateTextPlaceholderBytesAsync(string text, int width, int height)
|
||||
public static async Task<byte[]> CreateTextPlaceholderBytesAsync(int width, int height)
|
||||
{
|
||||
var device = CanvasDevice.GetSharedDevice();
|
||||
var renderTarget = new CanvasRenderTarget(device, width, height, 96);
|
||||
|
||||
// 居中绘制文字
|
||||
// 随机生成渐变色
|
||||
Windows.UI.Color RandomColor()
|
||||
{
|
||||
var rand = new Random(Guid.NewGuid().GetHashCode());
|
||||
double h = rand.NextDouble() * 360;
|
||||
double s = 0.35 + rand.NextDouble() * 0.3; // 0.35~0.65,适中饱和度
|
||||
double l = 0.5 + rand.NextDouble() * 0.3; // 0.5~0.8,明亮
|
||||
return HslToColor(h, s, l);
|
||||
}
|
||||
|
||||
Windows.UI.Color color1 = RandomColor();
|
||||
Windows.UI.Color color2 = RandomColor();
|
||||
|
||||
using (var ds = renderTarget.CreateDrawingSession())
|
||||
{
|
||||
// 背景色
|
||||
ds.Clear(Colors.LightGray);
|
||||
|
||||
// 文字格式
|
||||
var format = new CanvasTextFormat
|
||||
// 绘制线性渐变背景
|
||||
var gradientBrush = new Microsoft.Graphics.Canvas.Brushes.CanvasLinearGradientBrush(ds, color1, color2)
|
||||
{
|
||||
FontSize = Math.Min(width, height) / 6f,
|
||||
FontWeight = Microsoft.UI.Text.FontWeights.SemiBold,
|
||||
HorizontalAlignment = CanvasHorizontalAlignment.Center,
|
||||
VerticalAlignment = CanvasVerticalAlignment.Center,
|
||||
WordWrapping = CanvasWordWrapping.Wrap,
|
||||
TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
|
||||
Options = CanvasDrawTextOptions.Default,
|
||||
StartPoint = new System.Numerics.Vector2(0, 0),
|
||||
EndPoint = new System.Numerics.Vector2(width, height)
|
||||
};
|
||||
|
||||
// 设定边距
|
||||
float margin = Math.Min(width, height) / 12f;
|
||||
float availableWidth = width - 2 * margin;
|
||||
float availableHeight = height - 2 * margin;
|
||||
|
||||
// 计算合适的字体大小以适应内容区域
|
||||
float fontSize = format.FontSize;
|
||||
float minFontSize = 8f;
|
||||
float maxFontSize = format.FontSize;
|
||||
CanvasTextLayout layout;
|
||||
do
|
||||
{
|
||||
format.FontSize = fontSize;
|
||||
layout = new CanvasTextLayout(
|
||||
ds,
|
||||
text,
|
||||
format,
|
||||
availableWidth,
|
||||
availableHeight
|
||||
);
|
||||
if (
|
||||
layout.LayoutBounds.Width <= availableWidth
|
||||
&& layout.LayoutBounds.Height <= availableHeight
|
||||
)
|
||||
break;
|
||||
fontSize -= 1f;
|
||||
} while (fontSize >= minFontSize);
|
||||
|
||||
// 居中绘制文字(在内容区域内居中)
|
||||
var bounds = layout.LayoutBounds;
|
||||
var x = margin + (availableWidth - (float)bounds.Width) / 2f - (float)bounds.X;
|
||||
var y = margin + (availableHeight - (float)bounds.Height) / 2f - (float)bounds.Y;
|
||||
ds.DrawTextLayout(layout, new Vector2(x, y), Colors.DarkGray);
|
||||
ds.FillRectangle(0, 0, width, height, gradientBrush);
|
||||
}
|
||||
|
||||
// 保存为 PNG 并转为 byte[]
|
||||
@@ -100,9 +76,34 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// HSL转Color
|
||||
static Windows.UI.Color HslToColor(double h, double s, double l)
|
||||
{
|
||||
h = h / 360.0;
|
||||
double r = l, g = l, b = l;
|
||||
if (s != 0)
|
||||
{
|
||||
double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
double p = 2 * l - q;
|
||||
r = HueToRgb(p, q, h + 1.0 / 3.0);
|
||||
g = HueToRgb(p, q, h);
|
||||
b = HueToRgb(p, q, h - 1.0 / 3.0);
|
||||
}
|
||||
return Windows.UI.Color.FromArgb(255, (byte)(r * 255), (byte)(g * 255), (byte)(b * 255));
|
||||
}
|
||||
static double HueToRgb(double p, double q, double t)
|
||||
{
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1.0 / 6.0) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0 / 2.0) return q;
|
||||
if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Color> GetAccentColorsFromByte(byte[] bytes)
|
||||
public static List<Windows.UI.Color> GetAccentColorsFromByte(byte[] bytes)
|
||||
{
|
||||
// 使用 ImageSharp 读取图片
|
||||
using var image = SixLabors.ImageSharp.Image.Load<SixLabors.ImageSharp.PixelFormats.Rgba32>(bytes);
|
||||
@@ -137,7 +138,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
//public static async Task<BitmapImage> GetBitmapImageFromBytesAsync(byte[] imageBytes)
|
||||
//{
|
||||
// var stream = new InMemoryRandomAccessStream();
|
||||
@@ -167,9 +167,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static async Task<byte[]> ToByteArrayAsync(IRandomAccessStreamReference streamRef)
|
||||
{
|
||||
using IRandomAccessStream stream = await streamRef.OpenReadAsync();
|
||||
using var memoryStream = new MemoryStream();
|
||||
await stream.AsStreamForRead().CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
using var reader = new DataReader(stream);
|
||||
await reader.LoadAsync((uint)stream.Size);
|
||||
byte[] buffer = new byte[stream.Size];
|
||||
reader.ReadBytes(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static float GetAverageLuminance(CanvasBitmap bitmap)
|
||||
@@ -188,5 +190,35 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
return (float)(sum / (pixels.Length / 4));
|
||||
}
|
||||
|
||||
public static byte[] MakeSquareWithThemeColor(byte[] imageBytes)
|
||||
{
|
||||
using var image = Image.Load<Rgba32>(imageBytes);
|
||||
|
||||
if (image.Width == image.Height)
|
||||
{
|
||||
// 已经是正方形,直接返回
|
||||
return imageBytes;
|
||||
}
|
||||
|
||||
int size = Math.Max(image.Width, image.Height);
|
||||
|
||||
var themeColor = Rgba32.ParseHex(GetAccentColorsFromByte(imageBytes).FirstOrDefault().ToHex());
|
||||
|
||||
// 新建正方形画布
|
||||
using var square = new Image<Rgba32>(size, size, themeColor);
|
||||
|
||||
// 计算居中位置
|
||||
int offsetX = (size - image.Width) / 2;
|
||||
int offsetY = (size - image.Height) / 2;
|
||||
|
||||
// 绘制原图到正方形画布
|
||||
square.Mutate(ctx => ctx.DrawImage(image, new Point(offsetX, offsetY), 1f));
|
||||
|
||||
// 保存为 PNG 字节流
|
||||
using var ms = new MemoryStream();
|
||||
square.Save(ms, new PngEncoder());
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Windows.Globalization;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
@@ -90,28 +91,38 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
public static bool IsCJK(string text)
|
||||
{
|
||||
return DetectLanguageCode(text) switch
|
||||
return DetectLanguageCode(text)?.Substring(0, 2) switch
|
||||
{
|
||||
"zh" or "ja" or "ko" => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static string DetectCountryCode(string? text)
|
||||
public static string ConvertToCountryCode(string? languageCode)
|
||||
{
|
||||
if (text == null) return "en";
|
||||
var code = DetectLanguageCode(text);
|
||||
if (code == null) return "en";
|
||||
// 处理中文简体和繁体
|
||||
if (code == "zh-Hans") return "cn";
|
||||
if (code == "zh-Hant") return "cn";
|
||||
// 其他语言直接返回两字母代码
|
||||
return code;
|
||||
if (languageCode == null) return "us";
|
||||
|
||||
return languageCode switch
|
||||
{
|
||||
"zh" => "cn",
|
||||
"zh-Hans" => "cn",
|
||||
"zh-Hant" => "tw",
|
||||
"ja" => "jp",
|
||||
"ko" => "kr",
|
||||
_ => "us"
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetUserTargetLanguageCode()
|
||||
{
|
||||
return SupportedTargetLanguages[_settingsService.SelectedTargetLanguageIndex].Code;
|
||||
}
|
||||
|
||||
public static int GetDefaultTargetLanguageIndex()
|
||||
{
|
||||
int found = SupportedTargetLanguages.FindIndex(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);
|
||||
if (found == -1) found = 7; // 默认使用英语
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Nito.AsyncEx;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -9,23 +10,34 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class LatestOnlyTaskRunner
|
||||
{
|
||||
private CancellationTokenSource? _cts;
|
||||
private readonly AsyncLock _mutex = new();
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
public async Task RunAsync(Func<CancellationToken, Task> func)
|
||||
public async Task RunAsync(Func<CancellationToken, Task> action)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
CancellationTokenSource oldCts;
|
||||
|
||||
// 使用 AsyncLock 保证线程安全
|
||||
using (await _mutex.LockAsync())
|
||||
{
|
||||
// 取消旧的
|
||||
oldCts = _cts;
|
||||
_cts = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
oldCts?.Cancel();
|
||||
oldCts?.Dispose();
|
||||
|
||||
CancellationToken token = _cts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
await func(token);
|
||||
await action(token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 可以选择忽略取消异常
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
_cts?.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
PostProcessLyricsLines(durationMs.Value);
|
||||
_lyricsDataArr.Add(new LyricsData()); // 为机翻预留
|
||||
return _lyricsDataArr;
|
||||
}
|
||||
|
||||
@@ -113,8 +114,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
// 初始化每种语言的歌词列表
|
||||
_lyricsDataArr.Clear();
|
||||
for (int i = 0; i < languageCount; i++)
|
||||
_lyricsDataArr.Add(new LyricsData());
|
||||
for (int i = 0; i < languageCount; i++) _lyricsDataArr.Add(new LyricsData());
|
||||
|
||||
// 遍历每个时间分组
|
||||
foreach (var group in grouped)
|
||||
@@ -128,9 +128,8 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
var line = new LyricsLine
|
||||
{
|
||||
StartMs = start,
|
||||
EndMs = 0, // 稍后统一修正
|
||||
OriginalText = text,
|
||||
CharTimings = [],
|
||||
LyricsChars = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
@@ -139,11 +138,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
var (charStart, charText) = syllables[j];
|
||||
int startIndex = currentIndex;
|
||||
line.CharTimings.Add(
|
||||
new CharTiming
|
||||
line.LyricsChars.Add(
|
||||
new LyricsChar
|
||||
{
|
||||
StartMs = charStart,
|
||||
EndMs = 0, // Fixed later
|
||||
Text = charText ?? "",
|
||||
StartIndex = startIndex,
|
||||
}
|
||||
@@ -170,7 +168,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
// 句级时间
|
||||
string? pBegin = p.Attribute("begin")?.Value;
|
||||
string? pEnd = p.Attribute("end")?.Value;
|
||||
int pStartMs = ParseTtmlTime(pBegin);
|
||||
int pEndMs = ParseTtmlTime(pEnd);
|
||||
|
||||
// 只获取一级span,且排除ttm:role="x-bg"的span
|
||||
var spans = p.Elements()
|
||||
@@ -197,16 +197,18 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
}
|
||||
|
||||
var originalCharTimings = new List<CharTiming>();
|
||||
var originalCharTimings = new List<LyricsChar>();
|
||||
int originalStartIndex = 0;
|
||||
foreach (var span in originalTextSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
originalCharTimings.Add(new CharTiming
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
originalCharTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = 0,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = originalStartIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
@@ -218,23 +220,25 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
originalLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = 0,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = originalText,
|
||||
CharTimings = originalCharTimings,
|
||||
LyricsChars = originalCharTimings,
|
||||
});
|
||||
|
||||
// 翻译
|
||||
string translationText = string.Concat(translationTextSpans.Select(s => s.Value));
|
||||
var translationCharTimings = new List<CharTiming>();
|
||||
var translationCharTimings = new List<LyricsChar>();
|
||||
int translationStartIndex = 0;
|
||||
foreach (var span in translationTextSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
translationCharTimings.Add(new CharTiming
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
translationCharTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = 0,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = translationStartIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
@@ -245,9 +249,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
translationLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = 0,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = translationText,
|
||||
CharTimings = translationCharTimings,
|
||||
LyricsChars = translationCharTimings,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -336,9 +340,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
var lineWrite = new LyricsLine
|
||||
{
|
||||
StartMs = lineRead.StartTime ?? 0,
|
||||
EndMs = 0,
|
||||
EndMs = lineRead.EndTime ?? 0,
|
||||
OriginalText = lineRead.Text,
|
||||
CharTimings = [],
|
||||
LyricsChars = [],
|
||||
};
|
||||
|
||||
var syllables = (lineRead as SyllableLineInfo)?.Syllables;
|
||||
@@ -352,22 +356,14 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
)
|
||||
{
|
||||
var syllable = syllables[syllableIndex];
|
||||
var charTiming = new CharTiming
|
||||
var charTiming = new LyricsChar
|
||||
{
|
||||
StartMs = syllable.StartTime,
|
||||
EndMs = 0,
|
||||
EndMs = syllable.EndTime,
|
||||
Text = syllable.Text,
|
||||
StartIndex = startIndex,
|
||||
};
|
||||
if (syllableIndex + 1 < syllables.Count)
|
||||
{
|
||||
charTiming.EndMs = syllables[syllableIndex + 1].StartTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
charTiming.EndMs = lineWrite.EndMs;
|
||||
}
|
||||
lineWrite.CharTimings.Add(charTiming);
|
||||
lineWrite.LyricsChars.Add(charTiming);
|
||||
startIndex += syllable.Text.Length;
|
||||
}
|
||||
}
|
||||
@@ -384,34 +380,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
for (int langIdx = 0; langIdx < _lyricsDataArr.Count; langIdx++)
|
||||
{
|
||||
var lines = _lyricsDataArr[langIdx].LyricsLines;
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
if (i + 1 < lines.Count)
|
||||
{
|
||||
lines[i].EndMs = lines[i + 1].StartMs;
|
||||
}
|
||||
else
|
||||
{
|
||||
lines[i].EndMs = durationMs;
|
||||
}
|
||||
|
||||
// 修正 CharTimings 的 EndMs
|
||||
var timings = lines[i].CharTimings;
|
||||
if (timings.Count > 0)
|
||||
{
|
||||
for (int j = 0; j < timings.Count; j++)
|
||||
{
|
||||
if (j + 1 < timings.Count)
|
||||
{
|
||||
timings[j].EndMs = timings[j + 1].StartMs;
|
||||
}
|
||||
else
|
||||
{
|
||||
timings[j].EndMs = lines[i].EndMs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lines.Count > 0)
|
||||
{
|
||||
if (lines[0].StartMs > 0)
|
||||
@@ -423,7 +391,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
StartMs = 0,
|
||||
EndMs = lines[0].StartMs,
|
||||
OriginalText = "● ● ●",
|
||||
CharTimings = [],
|
||||
LyricsChars = [],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
26
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/NetHelper.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class NetHelper
|
||||
{
|
||||
public static async Task<bool> CheckConnectivity(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new System.Net.Http.HttpClient();
|
||||
// Try to reach a reliable endpoint
|
||||
var res = await client.GetAsync(url);
|
||||
return res.IsSuccessStatusCode;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false; // If any exception occurs, assume no connectivity
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using Vanara.Extensions;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.CoreAudio;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class SystemVolumeHelper
|
||||
{
|
||||
private readonly static IMMDeviceEnumerator _deviceEnumerator = new();
|
||||
private static IAudioEndpointVolume? _endpointVolume = null;
|
||||
private static VolumeCallbackImpl? _callbackImpl;
|
||||
private static int _masterVolume = 0;
|
||||
private static DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
public static event Action<int>? VolumeChanged;
|
||||
|
||||
static SystemVolumeHelper()
|
||||
{
|
||||
var device = _deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
|
||||
if (device != null)
|
||||
{
|
||||
device.Activate(typeof(IAudioEndpointVolume).GUID, 0, null, out var obj);
|
||||
if (obj is IAudioEndpointVolume endpointVolume)
|
||||
{
|
||||
_endpointVolume = endpointVolume;
|
||||
_callbackImpl = new VolumeCallbackImpl();
|
||||
_endpointVolume.RegisterControlChangeNotify(_callbackImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前系统主音量(0~100)。
|
||||
/// </summary>
|
||||
public static int GetMasterVolume()
|
||||
{
|
||||
if (_endpointVolume != null)
|
||||
{
|
||||
float level = _endpointVolume.GetMasterVolumeLevelScalar();
|
||||
_masterVolume = (int)(level * 100);
|
||||
}
|
||||
|
||||
return _masterVolume;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前系统主音量(0~100)。
|
||||
/// </summary>
|
||||
public static void SetMasterVolume(int volume)
|
||||
{
|
||||
if (_masterVolume == volume) return;
|
||||
|
||||
_masterVolume = volume;
|
||||
_endpointVolume?.SetMasterVolumeLevelScalar(_masterVolume / 100f, Guid.Empty);
|
||||
}
|
||||
|
||||
// 内部回调实现
|
||||
private class VolumeCallbackImpl : IAudioEndpointVolumeCallback
|
||||
{
|
||||
HRESULT IAudioEndpointVolumeCallback.OnNotify(nint pNotify)
|
||||
{
|
||||
var data = pNotify.ToStructure<AUDIO_VOLUME_NOTIFICATION_DATA>();
|
||||
_masterVolume = (int)(data.fMasterVolume * 100);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
VolumeChanged?.Invoke(_masterVolume);
|
||||
});
|
||||
return HRESULT.S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
while (_activeWindows.Count > 0)
|
||||
{
|
||||
var window = _activeWindows[0];
|
||||
((Window)window).Close();
|
||||
var window = (Window)_activeWindows[0];
|
||||
DockModeHelper.Disable(window);
|
||||
window.Close();
|
||||
_activeWindows.Remove(window);
|
||||
}
|
||||
App.Current.Exit();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class CharTiming
|
||||
public class LyricsChar
|
||||
{
|
||||
public int EndMs { get; set; }
|
||||
public int? EndMs { get; set; }
|
||||
public int StartIndex { get; set; }
|
||||
public int StartMs { get; set; }
|
||||
public string Text { get; set; } = string.Empty;
|
||||
@@ -1,10 +1,12 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using StringHelper = BetterLyrics.WinUI3.Helper.StringHelper;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
@@ -35,7 +37,23 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
else
|
||||
{
|
||||
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}({translationData.LyricsLines[i].OriginalText})";
|
||||
if (translationData.LanguageCode?.Substring(0, 2) == "zh")
|
||||
{
|
||||
string tmp = "";
|
||||
if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hant")
|
||||
{
|
||||
tmp = ChineseConverter.ConvertToTraditionalChinese(translationData.LyricsLines[i].OriginalText);
|
||||
}
|
||||
else if (LanguageHelper.GetUserTargetLanguageCode() == "zh-Hans")
|
||||
{
|
||||
tmp = ChineseConverter.ConvertToSimplifiedChinese(translationData.LyricsLines[i].OriginalText);
|
||||
}
|
||||
line.DisplayedText = $"{line.OriginalText}\n{tmp}";
|
||||
}
|
||||
else
|
||||
{
|
||||
line.DisplayedText = $"{line.OriginalText}\n{translationData.LyricsLines[i].OriginalText}";
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -53,7 +71,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
else
|
||||
{
|
||||
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}({translationArr[i]})";
|
||||
line.DisplayedText = $"{line.OriginalText}{StringHelper.NewLine}{translationArr[i]}";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -67,6 +85,30 @@ namespace BetterLyrics.WinUI3.Models
|
||||
}
|
||||
}
|
||||
|
||||
public LyricsData CreateLyricsDataFrom(string translation)
|
||||
{
|
||||
var result = new LyricsData(LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
}).ToList());
|
||||
List<string> translationArr = translation.Split(StringHelper.NewLine).ToList();
|
||||
int i = 0;
|
||||
foreach (var line in result.LyricsLines)
|
||||
{
|
||||
if (i >= translationArr.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
line.OriginalText = translationArr[i];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LyricsData GetNotfoundPlaceholder(int durationMs)
|
||||
{
|
||||
return new LyricsData([new LyricsLine
|
||||
@@ -74,7 +116,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
StartMs = 0,
|
||||
EndMs = durationMs,
|
||||
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
|
||||
CharTimings = [],
|
||||
LyricsChars = [],
|
||||
}]);
|
||||
}
|
||||
|
||||
@@ -87,7 +129,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
OriginalText = "● ● ●",
|
||||
DisplayedText = "● ● ●",
|
||||
CharTimings = [],
|
||||
LyricsChars = [],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public Vector2 CenterPosition { get; set; }
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public List<CharTiming> CharTimings { get; set; } = [];
|
||||
public List<LyricsChar> LyricsChars { get; set; } = [];
|
||||
|
||||
public int DurationMs => EndMs - StartMs;
|
||||
public int EndMs { get; set; }
|
||||
public int? DurationMs => EndMs - StartMs;
|
||||
public int? EndMs { get; set; }
|
||||
public int StartMs { get; set; }
|
||||
|
||||
public string DisplayedText { get; set; } = "";
|
||||
|
||||
@@ -7,8 +7,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -48,7 +50,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
result = bytesFromSMTC;
|
||||
break;
|
||||
case AlbumArtSearchProvider.iTunes:
|
||||
result = await SearchiTunesAsync(artist, album);
|
||||
foreach (string countryCode in new List<string>() { "us", "cn", "jp", "kr" })
|
||||
{
|
||||
result = await SearchiTunesAsync(artist, album, title, countryCode);
|
||||
if (result != null) break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -82,7 +88,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<byte[]?> SearchiTunesAsync(string artist, string album)
|
||||
private async Task<byte[]?> SearchiTunesAsync(string artist, string album, string title, string countryCode)
|
||||
{
|
||||
// Source: https://gist.github.com/mcworkaholic/82fbf203e3f1043bbe534b5b2974c0ce
|
||||
try
|
||||
@@ -96,10 +102,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
|
||||
// Build the iTunes API URL
|
||||
string url = $"https://itunes.apple.com/search?term=" + artist + "+" + album + "&country=" + LanguageHelper.DetectCountryCode(album + artist) + "&entity=album";
|
||||
url.Replace(" ", "-");
|
||||
// Make a request to the API
|
||||
string url = $"https://itunes.apple.com/search?term=" + WebUtility.UrlEncode($"{artist} {album}").Replace("%20", "+") + "&country=" + countryCode + "&entity=album&media=music&limit=1";
|
||||
|
||||
// Make a request to the API
|
||||
HttpResponseMessage response = await _iTunesHttpClinet.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string responseBody = await response.Content.ReadAsStringAsync();
|
||||
|
||||
@@ -9,6 +9,6 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
public interface ILyricsSearchService
|
||||
{
|
||||
Task<string?> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token);
|
||||
Task<(string?, LyricsSearchProvider?)> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
|
||||
@@ -13,5 +14,13 @@ namespace BetterLyrics.WinUI3.Services
|
||||
event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
|
||||
event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChangedChanged;
|
||||
event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
|
||||
|
||||
Task PlayAsync();
|
||||
Task PauseAsync();
|
||||
Task PreviousAsync();
|
||||
Task NextAsync();
|
||||
|
||||
bool IsPlaying { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
using System.Collections.Generic;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
@@ -39,6 +37,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
string LibreTranslateServer { get; set; }
|
||||
int SelectedTargetLanguageIndex { get; set; }
|
||||
bool ResetPositionOffsetOnSongChanged { get; set; }
|
||||
int PositionOffset { get; set; }
|
||||
// Lyrics lib
|
||||
|
||||
List<LocalLyricsFolder> LocalLyricsFolders { get; set; }
|
||||
@@ -54,6 +54,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
Color LyricsCustomFgFontColor { get; set; }
|
||||
Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
int LyricsBgFontOpacity { get; set; }
|
||||
|
||||
LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsFgFontColorType { get; set; }
|
||||
LyricsFontColorType LyricsStrokeFontColorType { get; set; }
|
||||
@@ -69,6 +71,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
LineRenderingType LyricsGlowEffectScope { get; set; }
|
||||
LineRenderingType LyricsHighlightScope { get; set; }
|
||||
|
||||
bool IsLyricsFloatAnimationEnabled { get; set; }
|
||||
|
||||
float LyricsLineSpacingFactor { get; set; }
|
||||
|
||||
List<LyricsSearchProviderInfo> LyricsSearchProvidersInfo { get; set; }
|
||||
@@ -83,9 +87,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
bool IgnoreFullscreenWindow { get; set; }
|
||||
|
||||
bool IsTranslationEnabled { get; set; }
|
||||
bool ShowTranslationOnly { get; set; }
|
||||
|
||||
LyricsDisplayType PreferredDisplayType { get; set; }
|
||||
LyricsDisplayType DisplayType { get; set; }
|
||||
|
||||
int TimelineSyncThreshold { get; set; }
|
||||
int LockHotKeyIndex { get; set; }
|
||||
bool IsImmersiveMode { get; set; }
|
||||
string LXMusicServer { get; set; }
|
||||
DockPlacement DockPlacement { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
public async Task<(string?, LyricsSearchProvider?)> SearchAsync(string title, string artist, string album, double durationMs, CancellationToken token)
|
||||
{
|
||||
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
if (!string.IsNullOrWhiteSpace(cachedLyrics))
|
||||
{
|
||||
return cachedLyrics;
|
||||
return (cachedLyrics, provider.Provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,11 +155,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
|
||||
}
|
||||
|
||||
return searchedLyrics;
|
||||
return (searchedLyrics, provider.Provider);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
|
||||
private async Task<string?> SearchFile(string title, string artist, LyricsFormat format)
|
||||
@@ -327,6 +327,17 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.QQMusicApi.GetLyricsAsync(qqResult.Id);
|
||||
var original = response?.Lyrics;
|
||||
var translated = response?.Trans;
|
||||
if (!string.IsNullOrEmpty(translated))
|
||||
{
|
||||
FileHelper.WriteLyricsCache(
|
||||
title,
|
||||
artist,
|
||||
translated,
|
||||
LyricsFormat.Lrc,
|
||||
PathHelper.QQTranslationCacheDirectory
|
||||
);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
else if (result is NeteaseSearchResult neteaseResult)
|
||||
|
||||
@@ -7,13 +7,16 @@ using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using EvtSource;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
@@ -29,14 +32,21 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
private readonly IAlbumArtSearchService _albumArtSearchService;
|
||||
private readonly ILogger<PlaybackService> _logger;
|
||||
|
||||
private readonly string _lxMusicId = "cn.toside.music.desktop";
|
||||
|
||||
private bool _cachedIsPlaying = false;
|
||||
|
||||
private EventSourceReader? _sse = null;
|
||||
|
||||
private readonly MediaManager _mediaManager = new();
|
||||
private readonly LatestOnlyTaskRunner _AlbumArtRefreshRunner = new();
|
||||
private readonly LatestOnlyTaskRunner _OnAnyMediaPropertyChangedRunner = new();
|
||||
|
||||
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
|
||||
private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new();
|
||||
|
||||
private SongInfo? _cachedSongInfo;
|
||||
private List<MediaSourceProviderInfo> _mediaSourceProvidersInfo;
|
||||
private byte[]? _SMTCAlbumArtBytes = null;
|
||||
private AlbumArtChangedEventArgs _albumArtChangedEventArgs = new AlbumArtChangedEventArgs();
|
||||
|
||||
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
|
||||
public event EventHandler<PositionChangedEventArgs>? PositionChanged;
|
||||
@@ -53,6 +63,8 @@ namespace BetterLyrics.WinUI3.Services
|
||||
InitMediaManager();
|
||||
}
|
||||
|
||||
public bool IsPlaying => _cachedIsPlaying;
|
||||
|
||||
private bool IsMediaSourceEnabled(string id)
|
||||
{
|
||||
return _mediaSourceProvidersInfo.FirstOrDefault(s => s.Provider == id)?.IsEnabled ?? true;
|
||||
@@ -80,7 +92,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(async () =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -88,11 +100,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
MediaManager_OnAnyMediaPropertyChanged(mediaSession, props);
|
||||
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "TryGetMediaPropertiesAsync failed");
|
||||
SendNullMessages();
|
||||
}
|
||||
catch (Exception) { }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -115,55 +123,68 @@ namespace BetterLyrics.WinUI3.Services
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(playbackInfo.PlaybackStatus switch
|
||||
{
|
||||
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
|
||||
_ => false,
|
||||
}));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
|
||||
{
|
||||
_ = _OnAnyMediaPropertyChangedRunner.RunAsync(async token =>
|
||||
string id = mediaSession.ControlSession.SourceAppUserModelId;
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
|
||||
await _onAnyMediaPropertyChangedRunner.RunAsync(async token =>
|
||||
{
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
|
||||
|
||||
RecordMediaSourceProviderInfo(mediaSession);
|
||||
string id = mediaSession.ControlSession.SourceAppUserModelId;
|
||||
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_cachedSongInfo = new SongInfo
|
||||
if (id == _lxMusicId)
|
||||
{
|
||||
Title = mediaProperties.Title,
|
||||
Artist = mediaProperties.Artist,
|
||||
Album = mediaProperties.AlbumTitle,
|
||||
DurationMs = mediaSession.ControlSession.GetTimelineProperties().EndTime.TotalMilliseconds,
|
||||
SourceAppUserModelId = id,
|
||||
};
|
||||
StartSSE();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopSSE();
|
||||
}
|
||||
|
||||
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
|
||||
{
|
||||
_SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
else
|
||||
{
|
||||
_SMTCAlbumArtBytes = null;
|
||||
}
|
||||
|
||||
_ = _AlbumArtRefreshRunner.RunAsync(async tokne =>
|
||||
await _albumArtRefreshRunner.RunAsync(async tokne =>
|
||||
{
|
||||
await UpdateAlbumArtRelated(tokne);
|
||||
});
|
||||
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
});
|
||||
@@ -208,8 +229,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
() =>
|
||||
{
|
||||
_cachedSongInfo = null;
|
||||
_cachedIsPlaying = false;
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(false));
|
||||
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.Zero));
|
||||
});
|
||||
}
|
||||
@@ -232,10 +254,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync($"{_cachedSongInfo!.Artist} - {_cachedSongInfo.Title}", 400, 400);
|
||||
bytes = await ImageHelper.CreateTextPlaceholderBytesAsync(400, 400);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
bytes = ImageHelper.MakeSquareWithThemeColor(bytes);
|
||||
|
||||
using var stream = new InMemoryRandomAccessStream();
|
||||
await stream.WriteAsync(bytes.AsBuffer());
|
||||
token.ThrowIfCancellationRequested();
|
||||
@@ -243,18 +267,105 @@ namespace BetterLyrics.WinUI3.Services
|
||||
var decoder = await BitmapDecoder.CreateAsync(stream);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_albumArtChangedEventArgs.AlbumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
var _albumArtSwBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_albumArtChangedEventArgs.AlbumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault();
|
||||
var _albumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault();
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
|
||||
() =>
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
AlbumArtChangedChanged?.Invoke(this, _albumArtChangedEventArgs);
|
||||
AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(_albumArtSwBitmap, _albumArtAccentColor));
|
||||
});
|
||||
}
|
||||
|
||||
private void StartSSE()
|
||||
{
|
||||
try
|
||||
{
|
||||
_sse = new EventSourceReader(new Uri($"{_settingsService.LXMusicServer}/subscribe-player-status?filter=progress")).Start();
|
||||
_sse.MessageReceived += Sse_MessageReceived;
|
||||
_sse.Disconnected += Sse_Disconnected;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_logger.LogError("Failed to start SSE connection for LX Music.");
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("FailToStartLXMusicServer"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
});
|
||||
StopSSE();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopSSE()
|
||||
{
|
||||
if (_sse != null)
|
||||
{
|
||||
_sse.MessageReceived -= Sse_MessageReceived;
|
||||
_sse.Disconnected -= Sse_Disconnected;
|
||||
_sse.Dispose();
|
||||
_sse = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void Sse_Disconnected(object sender, DisconnectEventArgs e)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(e.ReconnectDelay);
|
||||
if (_sse != null && !_sse.IsDisposed) _sse.Start();
|
||||
});
|
||||
}
|
||||
|
||||
private void Sse_MessageReceived(object sender, EventSourceMessageEventArgs e)
|
||||
{
|
||||
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
|
||||
|
||||
if (data.TryGetDouble(out double positionSeconds))
|
||||
{
|
||||
if (_cachedSongInfo?.SourceAppUserModelId == _lxMusicId)
|
||||
{
|
||||
PositionChanged?.Invoke(this, new PositionChangedEventArgs(TimeSpan.FromSeconds(positionSeconds)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PlayAsync()
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TryPlayAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PauseAsync()
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TryPauseAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PreviousAsync()
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TrySkipPreviousAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task NextAsync()
|
||||
{
|
||||
var focusedSession = _mediaManager.GetFocusedSession();
|
||||
if (focusedSession != null)
|
||||
{
|
||||
await focusedSession.ControlSession.TrySkipNextAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
@@ -268,7 +379,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>> message)
|
||||
public async void Receive(PropertyChangedMessage<ObservableCollection<AlbumArtSearchProviderInfo>> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
@@ -276,7 +387,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
// Album art search providers info changed, re-fetch album art
|
||||
_logger.LogInformation("Album art search providers info changed, refreshing album art.");
|
||||
_ = _AlbumArtRefreshRunner.RunAsync(async tokne =>
|
||||
await _albumArtRefreshRunner.RunAsync(async tokne =>
|
||||
{
|
||||
await UpdateAlbumArtRelated(tokne);
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string StandardWindowHeightKey = "StandardWindowHeight";
|
||||
|
||||
private const string AutoLockOnDesktopModeKey = "AutoLockOnDesktopMode";
|
||||
private const string IsImmersiveModeKey = "IsImmersiveMode";
|
||||
|
||||
private const string IsDynamicCoverOverlayEnabledKey = "IsDynamicCoverOverlayEnabled";
|
||||
private const string IsFanLyricsEnabledKey = "IsFanLyricsEnabled";
|
||||
@@ -71,9 +72,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
private const string MediaSourceProvidersInfoKey = "MediaSourceProvidersInfo";
|
||||
|
||||
private const string IsTranslationEnabledKey = "IsTranslationEnabled";
|
||||
private const string ShowTranslationOnlyKey = "ShowTranslationOnly";
|
||||
private const string LibreTranslateServerKey = "LibreTranslateServer";
|
||||
private const string SelectedTargetLanguageIndexKey = "SelectedTargetLanguageIndex";
|
||||
|
||||
private const string LXMusicServerKey = "LXMusicServer";
|
||||
|
||||
private const string LyricsBackgroundThemeKey = "LyricsBackgroundTheme";
|
||||
private const string IgnoreFullscreenWindowKey = "IgnoreFullscreenWindow";
|
||||
private const string PreferredDisplayTypeKey = "PreferredDisplayTypeKey";
|
||||
@@ -83,6 +87,16 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
public const string TimelineSyncThresholdKey = "TimelineSyncThreshold";
|
||||
|
||||
private const string IsLyricsFloatAnimationEnabledKey = "IsLyricsFloatAnimationEnabled";
|
||||
|
||||
private const string ResetPositionOffsetOnSongChangedKey = "ResetPositionOffsetOnSongChanged";
|
||||
|
||||
private const string PositionOffsetKey = "PositionOffset";
|
||||
|
||||
private const string LockHotKeyIndexKey = "LockHotKeyIndex";
|
||||
private const string DockPlacementKey = "DockPlacement";
|
||||
private const string LyricsBgFontOpacityKey = "LyricsBgFontOpacity";
|
||||
|
||||
private readonly ApplicationDataContainer _localSettings;
|
||||
|
||||
public SettingsService()
|
||||
@@ -152,6 +166,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(StandardWindowWidthKey, 1600);
|
||||
|
||||
SetDefault(AutoLockOnDesktopModeKey, false);
|
||||
SetDefault(IsImmersiveModeKey, false);
|
||||
// App behavior
|
||||
SetDefault(AutoStartWindowTypeKey, (int)AutoStartWindowType.StandardMode);
|
||||
// Album art
|
||||
@@ -185,8 +200,11 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(IsFanLyricsEnabledKey, false);
|
||||
|
||||
SetDefault(LibreTranslateServerKey, "");
|
||||
SetDefault(IsTranslationEnabledKey, false);
|
||||
SetDefault(SelectedTargetLanguageIndexKey, 6);
|
||||
SetDefault(IsTranslationEnabledKey, true);
|
||||
SetDefault(ShowTranslationOnlyKey, false);
|
||||
SetDefault(SelectedTargetLanguageIndexKey, LanguageHelper.GetDefaultTargetLanguageIndex());
|
||||
|
||||
SetDefault(LXMusicServerKey, "");
|
||||
|
||||
SetDefault(LyricsFontStrokeWidthKey, 3);
|
||||
SetDefault(IgnoreFullscreenWindowKey, false);
|
||||
@@ -195,6 +213,38 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SetDefault(LyricsScrollEasingTypeKey, (int)EasingType.EaseInOutQuad);
|
||||
SetDefault(LyricsScrollDurationKey, 500); // 500ms
|
||||
SetDefault(TimelineSyncThresholdKey, 0); // 0ms
|
||||
|
||||
SetDefault(IsLyricsFloatAnimationEnabledKey, true);
|
||||
|
||||
SetDefault(ResetPositionOffsetOnSongChangedKey, false);
|
||||
SetDefault(PositionOffsetKey, 0);
|
||||
SetDefault(LockHotKeyIndexKey, 'U' - 'A');
|
||||
SetDefault(DockPlacementKey, (int)DockPlacement.Top);
|
||||
SetDefault(LyricsBgFontOpacityKey, 30); // 30%
|
||||
}
|
||||
|
||||
public int LyricsBgFontOpacity
|
||||
{
|
||||
get => GetValue<int>(LyricsBgFontOpacityKey);
|
||||
set => SetValue(LyricsBgFontOpacityKey, value);
|
||||
}
|
||||
|
||||
public bool ShowTranslationOnly
|
||||
{
|
||||
get => GetValue<bool>(ShowTranslationOnlyKey);
|
||||
set => SetValue(ShowTranslationOnlyKey, value);
|
||||
}
|
||||
|
||||
public DockPlacement DockPlacement
|
||||
{
|
||||
get => (DockPlacement)GetValue<int>(DockPlacementKey);
|
||||
set => SetValue(DockPlacementKey, (int)value);
|
||||
}
|
||||
|
||||
public int LockHotKeyIndex
|
||||
{
|
||||
get => GetValue<int>(LockHotKeyIndexKey);
|
||||
set => SetValue(LockHotKeyIndexKey, value);
|
||||
}
|
||||
|
||||
public EasingType LyricsScrollEasingType
|
||||
@@ -209,7 +259,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(LyricsScrollDurationKey, value);
|
||||
}
|
||||
|
||||
public LyricsDisplayType PreferredDisplayType
|
||||
public LyricsDisplayType DisplayType
|
||||
{
|
||||
get => (LyricsDisplayType)GetValue<int>(PreferredDisplayTypeKey);
|
||||
set => SetValue(PreferredDisplayTypeKey, (int)value);
|
||||
@@ -511,6 +561,12 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(SelectedTargetLanguageIndexKey, value);
|
||||
}
|
||||
|
||||
public string LXMusicServer
|
||||
{
|
||||
get => GetValue<string>(LXMusicServerKey)!;
|
||||
set => SetValue(LXMusicServerKey, value);
|
||||
}
|
||||
|
||||
public bool IgnoreFullscreenWindow
|
||||
{
|
||||
get => GetValue<bool>(IgnoreFullscreenWindowKey);
|
||||
@@ -523,6 +579,30 @@ namespace BetterLyrics.WinUI3.Services
|
||||
set => SetValue(TimelineSyncThresholdKey, value);
|
||||
}
|
||||
|
||||
public bool IsLyricsFloatAnimationEnabled
|
||||
{
|
||||
get => GetValue<bool>(IsLyricsFloatAnimationEnabledKey);
|
||||
set => SetValue(IsLyricsFloatAnimationEnabledKey, value);
|
||||
}
|
||||
|
||||
public bool ResetPositionOffsetOnSongChanged
|
||||
{
|
||||
get => GetValue<bool>(ResetPositionOffsetOnSongChangedKey);
|
||||
set => SetValue(ResetPositionOffsetOnSongChangedKey, value);
|
||||
}
|
||||
|
||||
public int PositionOffset
|
||||
{
|
||||
get => GetValue<int>(PositionOffsetKey);
|
||||
set => SetValue(PositionOffsetKey, value);
|
||||
}
|
||||
|
||||
public bool IsImmersiveMode
|
||||
{
|
||||
get => GetValue<bool>(IsImmersiveModeKey);
|
||||
set => SetValue(IsImmersiveModeKey, value);
|
||||
}
|
||||
|
||||
private T? GetValue<T>(string key)
|
||||
{
|
||||
if (_localSettings.Values.TryGetValue(key, out object? value))
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public TranslateService(ISettingsService settingsService) :base(settingsService)
|
||||
public TranslateService(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
@@ -76,18 +76,18 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
public int SearchTranslatedLyricsItself(List<LyricsData> lyricsDataArr)
|
||||
{
|
||||
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode();
|
||||
string targetLangCode = LanguageHelper.GetUserTargetLanguageCode().Substring(0, 2);
|
||||
if (lyricsDataArr.Count > 1)
|
||||
{
|
||||
for (int i = 1; i < lyricsDataArr.Count; i++)
|
||||
for (int i = 1; i < lyricsDataArr.Count; i++)
|
||||
{
|
||||
if (lyricsDataArr[i].LanguageCode == targetLangCode)
|
||||
if (lyricsDataArr[i].LanguageCode?.Substring(0, 2) == targetLangCode)
|
||||
{
|
||||
return i; // Translation lyrics data found
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1; // No translation lyrics data found
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,9 +288,6 @@
|
||||
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
|
||||
<value>Glow effect</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsGlowEffectScope.Header" xml:space="preserve">
|
||||
<value>Glow effect scope</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
|
||||
<value>Configure lyrics source</value>
|
||||
</data>
|
||||
@@ -304,7 +301,8 @@
|
||||
<value>Welcome to BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>Hover the mouse over the top or bottom area of the app to display more function options</value>
|
||||
<value>Click on the top-left button to enable immersive mode.
|
||||
If you encounter any problems, please go to the Settings page, About tab, and view the FAQ or contact the author for feedback</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>No music playing now</value>
|
||||
@@ -420,9 +418,15 @@
|
||||
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
|
||||
<value>Dock mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDock.Text" xml:space="preserve">
|
||||
<value>Dock mode</value>
|
||||
</data>
|
||||
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
|
||||
<value>Desktop mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
|
||||
<value>Desktop mode</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
|
||||
<value>Font weight</value>
|
||||
</data>
|
||||
@@ -528,8 +532,8 @@
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>Lock</value>
|
||||
</data>
|
||||
<data name="HostWindowLockToolTip.Content" xml:space="preserve">
|
||||
<value>To unlock after locking, go to the system tray to unlock</value>
|
||||
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
|
||||
<value>To unlock after locking, go to the system tray to unlock or press</value>
|
||||
</data>
|
||||
<data name="SettingsPageFan.Header" xml:space="preserve">
|
||||
<value>Fan lyrics</value>
|
||||
@@ -606,10 +610,10 @@
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>Visit https://github.com/LibreTranslate/LibreTranslate for installation instructions and more information (this software is not affiliated with this translation service in any way)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
|
||||
<value>Server test successful</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
|
||||
<value>Server test failed</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
@@ -649,7 +653,7 @@
|
||||
<value>Translate server is not set, please configure it in settings first</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>Will automatically reset to 0 when switching songs</value>
|
||||
<value>Reset to 0 when switching songs</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
<value>The translation in the lyrics will be read first. If there is no match, the machine translation will be requested from the LibreTranslate server</value>
|
||||
@@ -735,4 +739,40 @@
|
||||
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
|
||||
<value>Join now</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
|
||||
<value>Floating animation</value>
|
||||
</data>
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>Scope</value>
|
||||
</data>
|
||||
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
|
||||
<value>Unlock and lock shortcut keys</value>
|
||||
</data>
|
||||
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
|
||||
<value>LX Music Server</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
|
||||
<value>Dock mode placement</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
|
||||
<value>Top</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
|
||||
<value>Bottom</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
|
||||
<value>Enable translation</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>Show translation only</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>Font opacity (Non-current playback area)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>Frequently asked questions</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>Unable to connect to LX Music server, please go to Settings - Advanced options to check if the link is entered correctly</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -288,9 +288,6 @@
|
||||
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
|
||||
<value>グロー効果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsGlowEffectScope.Header" xml:space="preserve">
|
||||
<value>グローエフェクトスコープ</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
|
||||
<value>歌詞ソースを構成します</value>
|
||||
</data>
|
||||
@@ -304,7 +301,8 @@
|
||||
<value>BetterLyrics へようこそ</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>マウスをアプリの上または下部の領域にホバリングして、より多くの機能オプションを表示します</value>
|
||||
<value>左上ボタンをクリックして、没入型モードを有効にします。
|
||||
問題が発生した場合は、[設定]ページ、[タブについて]にアクセスして、FAQを表示するか、フィードバックについて著者に連絡してください</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>今は音楽が再生されていません</value>
|
||||
@@ -420,9 +418,15 @@
|
||||
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
|
||||
<value>ドックモード</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDock.Text" xml:space="preserve">
|
||||
<value>ドックモード</value>
|
||||
</data>
|
||||
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
|
||||
<value>デスクトップモード</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
|
||||
<value>デスクトップモード</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
|
||||
<value>フォント重量</value>
|
||||
</data>
|
||||
@@ -528,8 +532,8 @@
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>ロック</value>
|
||||
</data>
|
||||
<data name="HostWindowLockToolTip.Content" xml:space="preserve">
|
||||
<value>ロック後にロックを解除するには、システムトレイに移動してロックを解除します</value>
|
||||
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
|
||||
<value>ロック後にロックを解除するには、システムトレイに移動してロックを解除または押します</value>
|
||||
</data>
|
||||
<data name="SettingsPageFan.Header" xml:space="preserve">
|
||||
<value>ファンの歌詞</value>
|
||||
@@ -606,10 +610,10 @@
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>https://github.com/LibreTranslate/LibreTranslate にアクセスしてください。</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
|
||||
<value>サーバーテストが成功しました</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
|
||||
<value>サーバーテストに失敗しました</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
@@ -649,7 +653,7 @@
|
||||
<value>翻訳サーバーは設定されていません。最初に設定で構成してください</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>曲を切り替えると、0 に自動的にリセットされます</value>
|
||||
<value>曲を切り替えるときに0にリセットします</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
<value>歌詞の翻訳は最初に読まれます。一致していない場合、機械の翻訳はLibretranslate Serverから要求されます</value>
|
||||
@@ -735,4 +739,40 @@
|
||||
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
|
||||
<value>今すぐ参加してください</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
|
||||
<value>フローティングアニメーション</value>
|
||||
</data>
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>範囲</value>
|
||||
</data>
|
||||
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
|
||||
<value>ショートカットキーのロックを解除およびロックします</value>
|
||||
</data>
|
||||
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
|
||||
<value>LX Music Server</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
|
||||
<value>ドックモード配置</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
|
||||
<value>トップ</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
|
||||
<value>底</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
|
||||
<value>翻訳を有効にします</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>翻訳のみを表示します</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>フォントの不透明度(非電流再生エリア)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>よくある質問</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Serverに接続できない場合は、設定に移動してください。リンクが正しく入力されているかどうかを確認するための高度なオプションに移動してください</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -288,9 +288,6 @@
|
||||
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
|
||||
<value>글로우 효과</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsGlowEffectScope.Header" xml:space="preserve">
|
||||
<value>글로우 효과 범위</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
|
||||
<value>가사 소스를 구성하십시오</value>
|
||||
</data>
|
||||
@@ -304,7 +301,8 @@
|
||||
<value>Betterlyrics에 오신 것을 환영합니다</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>더 많은 기능 옵션을 표시하려면 앱의 상단 또는 하단 영역 위로 마우스를 가져옵니다.</value>
|
||||
<value>몰입 형 모드를 활성화하려면 왼쪽 상단 버튼을 클릭하십시오.
|
||||
문제가 발생하면 설정 페이지, 탭 정보로 이동하여 FAQ를보고 저자에게 연락하여 피드백을 받으십시오.</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>지금 음악이 재생되지 않습니다</value>
|
||||
@@ -420,9 +418,15 @@
|
||||
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
|
||||
<value>도크 모드</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDock.Text" xml:space="preserve">
|
||||
<value>도크 모드</value>
|
||||
</data>
|
||||
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
|
||||
<value>데스크탑 모드</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
|
||||
<value>데스크탑 모드</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
|
||||
<value>글꼴 무게</value>
|
||||
</data>
|
||||
@@ -528,8 +532,8 @@
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>잠금</value>
|
||||
</data>
|
||||
<data name="HostWindowLockToolTip.Content" xml:space="preserve">
|
||||
<value>잠금 잠금을 해제하려면 시스템 트레이로 이동하여 잠금을 해제하십시오.</value>
|
||||
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
|
||||
<value>잠금 잠금을 해제하려면 시스템 트레이로 이동하여 잠금을 해제하거나 누릅니다.</value>
|
||||
</data>
|
||||
<data name="SettingsPageFan.Header" xml:space="preserve">
|
||||
<value>팬 가사</value>
|
||||
@@ -606,10 +610,10 @@
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>설치 지침 및 자세한 정보는 https://github.com/LibreTranslate/LibreTranslate 를 방문하십시오 (이 소프트웨어는이 번역 서비스와 제휴하지 않습니다).</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
|
||||
<value>서버 테스트 성공</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
|
||||
<value>서버 테스트가 실패했습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
@@ -649,7 +653,7 @@
|
||||
<value>번역 서버가 설정되지 않았습니다. 먼저 설정으로 구성하십시오.</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>노래를 전환 할 때 자동으로 0 으로 재설정됩니다</value>
|
||||
<value>노래를 전환 할 때 0 으로 재설정하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
<value>가사의 번역은 먼저 읽습니다. 일치하지 않으면 LibreTranslate 서버에서 기계 번역이 요청됩니다.</value>
|
||||
@@ -735,4 +739,40 @@
|
||||
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
|
||||
<value>지금 가입하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
|
||||
<value>떠 다니는 애니메이션</value>
|
||||
</data>
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>범위</value>
|
||||
</data>
|
||||
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
|
||||
<value>바로 가기 키를 잠금 해제하고 잠그십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
|
||||
<value>LX 음악 서버</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
|
||||
<value>도크 모드 배치</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
|
||||
<value>맨 위</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
|
||||
<value>맨 아래</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
|
||||
<value>번역 활성화</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>번역 만 표시하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>글꼴 불투명 (비 전류 재생 영역)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>자주 묻는 질문</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>LX Music Server에 연결할 수 없습니다. 설정으로 이동하십시오 - 고급 옵션이 링크가 올바르게 입력되었는지 확인하십시오.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -288,9 +288,6 @@
|
||||
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
|
||||
<value>辉光效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsGlowEffectScope.Header" xml:space="preserve">
|
||||
<value>辉光效果作用范围</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
|
||||
<value>配置歌词源</value>
|
||||
</data>
|
||||
@@ -304,7 +301,8 @@
|
||||
<value>欢迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>将鼠标悬停在应用程序的顶部或底部区域以显示更多功能选项</value>
|
||||
<value>单击左上按钮以启用沉浸式模式。
|
||||
如果遇到任何问题,请转到“设置”页面,关于标签,查看常见问题解答或联系作者以获取反馈</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>当前没有正在播放的音乐</value>
|
||||
@@ -420,9 +418,15 @@
|
||||
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
|
||||
<value>停靠模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDock.Text" xml:space="preserve">
|
||||
<value>停靠模式</value>
|
||||
</data>
|
||||
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
|
||||
<value>桌面模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
|
||||
<value>桌面模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
|
||||
<value>字体粗细</value>
|
||||
</data>
|
||||
@@ -528,8 +532,8 @@
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>锁定</value>
|
||||
</data>
|
||||
<data name="HostWindowLockToolTip.Content" xml:space="preserve">
|
||||
<value>锁定后解锁,请转到系统托盘解锁</value>
|
||||
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
|
||||
<value>锁定后解锁,请转到系统托盘解锁或按下</value>
|
||||
</data>
|
||||
<data name="SettingsPageFan.Header" xml:space="preserve">
|
||||
<value>扇形歌词</value>
|
||||
@@ -606,10 +610,10 @@
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>访问 https://github.com/LibreTranslate/LibreTranslate 获取安装教程及更多信息(本软件与该翻译服务无任何联系)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
|
||||
<value>服务器测试成功</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
|
||||
<value>服务器测试失败</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
@@ -649,7 +653,7 @@
|
||||
<value>未设置翻译服务器,请先在设置中进行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>切换歌曲时将自动重置为 0</value>
|
||||
<value>切换歌曲时重置为 0</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
<value>将优先读取歌词内翻译,若无匹配则向 LibreTranslate 服务器请求机器翻译</value>
|
||||
@@ -735,4 +739,40 @@
|
||||
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
|
||||
<value>立即加入</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
|
||||
<value>浮动动画</value>
|
||||
</data>
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>范围</value>
|
||||
</data>
|
||||
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
|
||||
<value>解锁和锁定快捷键</value>
|
||||
</data>
|
||||
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
|
||||
<value>LX 音乐服务器</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
|
||||
<value>停靠模式位置</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
|
||||
<value>顶部</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
|
||||
<value>底部</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
|
||||
<value>启用翻译</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>仅显示翻译</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>字体不透明度(非当前播放区域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>常见问题与解答</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>无法连接到 LX 音乐服务器,请转到设置 - 高级选项以检查是否正确输入链接</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -288,9 +288,6 @@
|
||||
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
|
||||
<value>輝光效果</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsGlowEffectScope.Header" xml:space="preserve">
|
||||
<value>輝光效果作用範圍</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
|
||||
<value>配置歌詞源</value>
|
||||
</data>
|
||||
@@ -304,7 +301,8 @@
|
||||
<value>歡迎使用 BetterLyrics</value>
|
||||
</data>
|
||||
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
|
||||
<value>將鼠標懸停在應用程序的頂部或底部區域以顯示更多功能選項</value>
|
||||
<value>單擊左上按鈕以啟用沉浸式模式。
|
||||
如果遇到任何問題,請轉到“設置”頁面,關於標籤,查看常見問題解答或聯繫作者以獲取反饋</value>
|
||||
</data>
|
||||
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
|
||||
<value>目前沒有正在播放的音樂</value>
|
||||
@@ -337,7 +335,7 @@
|
||||
<value>適應歌詞背景(灰色)</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
|
||||
<value>专辑区域样式</value>
|
||||
<value>專輯區域樣式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
|
||||
<value>圓角半徑</value>
|
||||
@@ -420,9 +418,15 @@
|
||||
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
|
||||
<value>停靠模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDock.Text" xml:space="preserve">
|
||||
<value>停靠模式</value>
|
||||
</data>
|
||||
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
|
||||
<value>桌面模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
|
||||
<value>桌面模式</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
|
||||
<value>字體粗細</value>
|
||||
</data>
|
||||
@@ -528,8 +532,8 @@
|
||||
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
|
||||
<value>鎖定</value>
|
||||
</data>
|
||||
<data name="HostWindowLockToolTip.Content" xml:space="preserve">
|
||||
<value>鎖定後解鎖,請轉到系統托盤解鎖</value>
|
||||
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
|
||||
<value>鎖定後解鎖,請轉到系統托盤解鎖或按下</value>
|
||||
</data>
|
||||
<data name="SettingsPageFan.Header" xml:space="preserve">
|
||||
<value>扇形歌詞</value>
|
||||
@@ -606,10 +610,10 @@
|
||||
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
|
||||
<value>造訪 https://github.com/LibreTranslate/LibreTranslate 以取得安裝教學及更多資訊(本軟體與此翻譯服務無任何關聯)</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestSuccessInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
|
||||
<value>服務器測試成功</value>
|
||||
</data>
|
||||
<data name="SettingsPageLibreTranslateTestFailedInfo" xml:space="preserve">
|
||||
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
|
||||
<value>服務器測試失敗</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
|
||||
@@ -649,7 +653,7 @@
|
||||
<value>未設定翻譯伺服器,請先在設定中進行配置</value>
|
||||
</data>
|
||||
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
|
||||
<value>將在切換歌曲時自動重設為 0</value>
|
||||
<value>切換歌曲時重置為 0</value>
|
||||
</data>
|
||||
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
|
||||
<value>將優先讀取歌詞內翻譯,若無匹配則向 LibreTranslate 伺服器請求機器翻譯</value>
|
||||
@@ -735,4 +739,40 @@
|
||||
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
|
||||
<value>立即加入</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
|
||||
<value>浮動動畫</value>
|
||||
</data>
|
||||
<data name="SettingsPageScope.Header" xml:space="preserve">
|
||||
<value>範圍</value>
|
||||
</data>
|
||||
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
|
||||
<value>解鎖和鎖定快捷鍵</value>
|
||||
</data>
|
||||
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
|
||||
<value>LX 音樂服務器</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
|
||||
<value>停靠模式位置</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
|
||||
<value>頂部</value>
|
||||
</data>
|
||||
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
|
||||
<value>底部</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
|
||||
<value>啟用翻譯</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>僅顯示翻譯</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
|
||||
<value>字體不透明度(非目前播放區域)</value>
|
||||
</data>
|
||||
<data name="SettingsPageFAQ.Header" xml:space="preserve">
|
||||
<value>常見問題與解答</value>
|
||||
</data>
|
||||
<data name="FailToStartLXMusicServer" xml:space="preserve">
|
||||
<value>無法連接到 LX 音樂服務器,請轉到設置 - 高級選項以檢查是否正確輸入鏈接</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -10,7 +10,7 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
@@ -19,28 +19,65 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
private readonly IPlaybackService _playbackService;
|
||||
|
||||
private LyricsDisplayType? _preferredDisplayTypeBeforeSwitchToNonStandardMode;
|
||||
|
||||
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
|
||||
{
|
||||
IsFirstRun = _settingsService.IsFirstRun;
|
||||
IsTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
PreferredDisplayType = _settingsService.PreferredDisplayType;
|
||||
DisplayType = _settingsService.DisplayType;
|
||||
ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged;
|
||||
PositionOffset = _settingsService.PositionOffset;
|
||||
IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
ShowTranslationOnly = _settingsService.ShowTranslationOnly;
|
||||
|
||||
OnIsImmersiveModeChanged(IsImmersiveMode);
|
||||
|
||||
//Volume = SystemVolumeHelper.GetMasterVolume();
|
||||
//SystemVolumeHelper.VolumeChanged += SystemVolumeHelper_VolumeChanged;
|
||||
|
||||
_playbackService = playbackService;
|
||||
_playbackService.SongInfoChanged += PlaybackService_SongInfoChanged;
|
||||
_playbackService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
|
||||
|
||||
IsSongPlaying = _playbackService.IsPlaying;
|
||||
}
|
||||
|
||||
//private void SystemVolumeHelper_VolumeChanged(int volume)
|
||||
//{
|
||||
// Volume = volume;
|
||||
//}
|
||||
|
||||
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
|
||||
{
|
||||
IsSongPlaying = e.IsPlaying;
|
||||
}
|
||||
|
||||
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
|
||||
{
|
||||
SongInfo = e.SongInfo;
|
||||
PositionOffset = 0; // Reset position offset when song changes
|
||||
TrySwitchToPreferredDisplayType(e.SongInfo);
|
||||
if (ResetPositionOffsetOnSongChanged)
|
||||
{
|
||||
PositionOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//[ObservableProperty]
|
||||
//public partial int Volume { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Vector3 BottomCenterCommandGridTranslation { get; set; } = new Vector3(0, 0, 0);
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsImmersiveMode { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial float BottomCommandGridOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Thickness BottomCommandGridMargin { get; set; } = new Thickness(12);
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsDisplayType DisplayType { get; set; } = LyricsDisplayType.PlaceholderOnly;
|
||||
public partial LyricsDisplayType DisplayType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsFirstRun { get; set; }
|
||||
@@ -48,29 +85,27 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial bool IsWelcomeTeachingTipOpen { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LyricsDisplayType PreferredDisplayType { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial SongInfo? SongInfo { get; set; } = null;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int PositionOffset { get; set; } = 0;
|
||||
public partial int PositionOffset { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsTranslationEnabled { get; set; }
|
||||
|
||||
partial void OnIsTranslationEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsTranslationEnabled = value;
|
||||
}
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool ShowTranslationOnly { get; set; }
|
||||
|
||||
partial void OnPreferredDisplayTypeChanged(LyricsDisplayType value)
|
||||
{
|
||||
_settingsService.PreferredDisplayType = value;
|
||||
}
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool ResetPositionOffsetOnSongChanged { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsSongPlaying { get; set; }
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
@@ -78,13 +113,30 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (message.PropertyName == nameof(LyricsWindowViewModel.IsDockMode))
|
||||
{
|
||||
SetNonStandardModePreferredDisplayType(message.NewValue);
|
||||
TrySwitchToPreferredDisplayType(SongInfo);
|
||||
if (message.NewValue)
|
||||
{
|
||||
DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayType = _settingsService.DisplayType;
|
||||
}
|
||||
BottomCommandGridMargin = message.NewValue ? new Thickness(0) : new Thickness(12);
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
|
||||
{
|
||||
SetNonStandardModePreferredDisplayType(message.NewValue);
|
||||
TrySwitchToPreferredDisplayType(SongInfo);
|
||||
if (message.NewValue)
|
||||
{
|
||||
DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayType = _settingsService.DisplayType;
|
||||
}
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsImmersiveMode))
|
||||
{
|
||||
IsImmersiveMode = message.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,38 +147,28 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
WindowHelper.OpenOrShowWindow<SettingsWindow>();
|
||||
}
|
||||
|
||||
private void SetNonStandardModePreferredDisplayType(bool isEnabled)
|
||||
[RelayCommand]
|
||||
private async Task PlaySongAsync()
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
_preferredDisplayTypeBeforeSwitchToNonStandardMode = PreferredDisplayType;
|
||||
PreferredDisplayType = LyricsDisplayType.LyricsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
PreferredDisplayType = _preferredDisplayTypeBeforeSwitchToNonStandardMode ?? LyricsDisplayType.SplitView;
|
||||
}
|
||||
await _playbackService.PlayAsync();
|
||||
}
|
||||
|
||||
private void TrySwitchToPreferredDisplayType(SongInfo? songInfo)
|
||||
[RelayCommand]
|
||||
private async Task PauseSongAsync()
|
||||
{
|
||||
LyricsDisplayType displayType;
|
||||
await _playbackService.PauseAsync();
|
||||
}
|
||||
|
||||
if (songInfo == null)
|
||||
{
|
||||
displayType = LyricsDisplayType.PlaceholderOnly;
|
||||
}
|
||||
else if (PreferredDisplayType is LyricsDisplayType preferredDisplayType)
|
||||
{
|
||||
displayType = preferredDisplayType;
|
||||
}
|
||||
else
|
||||
{
|
||||
displayType = LyricsDisplayType.SplitView;
|
||||
}
|
||||
|
||||
DisplayType = displayType;
|
||||
[RelayCommand]
|
||||
private async Task PreviousSongAsync()
|
||||
{
|
||||
await _playbackService.PreviousAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task NextSongAsync()
|
||||
{
|
||||
await _playbackService.NextAsync();
|
||||
}
|
||||
|
||||
partial void OnIsFirstRunChanged(bool value)
|
||||
@@ -134,5 +176,37 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
IsWelcomeTeachingTipOpen = value;
|
||||
_settingsService.IsFirstRun = false;
|
||||
}
|
||||
|
||||
partial void OnIsTranslationEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsTranslationEnabled = value;
|
||||
}
|
||||
|
||||
partial void OnPositionOffsetChanged(int value)
|
||||
{
|
||||
_settingsService.PositionOffset = value;
|
||||
}
|
||||
|
||||
partial void OnIsImmersiveModeChanged(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
BottomCommandGridOpacity = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
BottomCommandGridOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnShowTranslationOnlyChanged(bool value)
|
||||
{
|
||||
_settingsService.ShowTranslationOnly = value;
|
||||
}
|
||||
|
||||
//partial void OnVolumeChanged(int value)
|
||||
//{
|
||||
// SystemVolumeHelper.SetMasterVolume(value);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
|
||||
_lyricsFontStrokeWidth = _settingsService.LyricsFontStrokeWidth;
|
||||
_isTranslationEnabled = _settingsService.IsTranslationEnabled;
|
||||
_showTranslationOnly = _settingsService.ShowTranslationOnly;
|
||||
_targetLanguageIndex = _settingsService.SelectedTargetLanguageIndex;
|
||||
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
|
||||
|
||||
@@ -50,6 +51,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
_canvasYScrollTransition.SetDuration(_settingsService.LyricsScrollDuration / 1000f);
|
||||
_canvasYScrollTransition.SetEasingType(_settingsService.LyricsScrollEasingType);
|
||||
_defaultOpacity = _settingsService.LyricsBgFontOpacity / 100f;
|
||||
|
||||
_isLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled;
|
||||
_displayType = _displayTypeReceived = _settingsService.DisplayType;
|
||||
|
||||
_libWatcherService.MusicLibraryFilesChanged +=
|
||||
LibWatcherService_MusicLibraryFilesChanged;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Effects;
|
||||
@@ -8,17 +7,12 @@ using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.UI;
|
||||
using Windows.UI.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
@@ -51,7 +45,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else if (_isDesktopMode)
|
||||
{
|
||||
DrawImmersiveBackground(control, combinedDs, 12f);
|
||||
DrawImmersiveBackground(control, combinedDs, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -74,7 +68,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (currentPlayingLine != null)
|
||||
{
|
||||
GetLinePlayingProgress(
|
||||
currentPlayingLine,
|
||||
_playingLineIndex,
|
||||
out int charStartIndex,
|
||||
out int charLength,
|
||||
out float charProgress
|
||||
@@ -202,7 +196,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
BlurAmount = _albumArtBgBlurAmount,
|
||||
Source = overlappedCovers,
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Quality,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
},
|
||||
};
|
||||
ds.DrawImage(coverOverlayEffect);
|
||||
@@ -286,28 +280,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (line == null) continue;
|
||||
|
||||
var textLayout = line.CanvasTextLayout;
|
||||
|
||||
if (textLayout == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (textLayout == null) continue;
|
||||
|
||||
var position = new Vector2(line.Position.X, line.Position.Y);
|
||||
|
||||
float layoutWidth = (float)textLayout.LayoutBounds.Width;
|
||||
float layoutHeight = (float)textLayout.LayoutBounds.Height;
|
||||
|
||||
if (layoutWidth <= 0 || layoutHeight <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (layoutWidth <= 0 || layoutHeight <= 0) continue;
|
||||
|
||||
float centerX = position.X;
|
||||
float centerY = position.Y + layoutHeight / 2;
|
||||
@@ -329,11 +312,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
break;
|
||||
}
|
||||
|
||||
float xOffset = _lyricsXTransition.Value;
|
||||
float yOffset = _canvasYScrollTransition.Value + _canvasHeight / 2;
|
||||
|
||||
// 组合变换:缩放 -> 旋转 -> 平移
|
||||
ds.Transform =
|
||||
Matrix3x2.CreateScale(line.ScaleTransition.Value, new Vector2(centerX, centerY))
|
||||
* Matrix3x2.CreateRotation(line.AngleTransition.Value, currentPlayingLine.Position)
|
||||
* Matrix3x2.CreateTranslation(_lyricsXTransition.Value, _canvasYScrollTransition.Value + _canvasHeight / 2);
|
||||
* Matrix3x2.CreateTranslation(xOffset, yOffset);
|
||||
|
||||
// Create the background lyrics line with stroke and fill
|
||||
using var bgLyrics = new CanvasCommandList(control.Device);
|
||||
@@ -344,22 +330,23 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
using var fgLyricsDs = fgLyrics.CreateDrawingSession();
|
||||
|
||||
// 创建文字几何体
|
||||
using (var textGeometry = CanvasGeometry.CreateText(textLayout))
|
||||
using var textGeometry = CanvasGeometry.CreateText(textLayout);
|
||||
if (_isDesktopMode)
|
||||
{
|
||||
if (_isDesktopMode)
|
||||
{
|
||||
bgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 背景描边
|
||||
fgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 前景描边
|
||||
}
|
||||
|
||||
bgLyricsDs.FillGeometry(textGeometry, position, _bgFontColor); // 背景填充
|
||||
fgLyricsDs.FillGeometry(textGeometry, position, _fgFontColor); // 前景填充
|
||||
bgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 背景描边
|
||||
fgLyricsDs.DrawGeometry(textGeometry, position, _strokeFontColor, _lyricsFontStrokeWidth); // 前景描边
|
||||
}
|
||||
|
||||
bgLyricsDs.FillGeometry(textGeometry, position, _bgFontColor); // 背景填充
|
||||
fgLyricsDs.FillGeometry(textGeometry, position, _fgFontColor); // 前景填充
|
||||
|
||||
using var combined = new CanvasCommandList(control.Device);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
// Mock gradient blurred lyrics layer
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
|
||||
// Current line will not be blurred
|
||||
ds.DrawImage(
|
||||
combinedDs.DrawImage(
|
||||
new GaussianBlurEffect
|
||||
{
|
||||
Source = new OpacityEffect { Source = bgLyrics, Opacity = line.OpacityTransition.Value * _lyricsOpacityTransition.Value },
|
||||
@@ -381,7 +368,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (i == _playingLineIndex)
|
||||
{
|
||||
GetLinePlayingProgress(
|
||||
line,
|
||||
i,
|
||||
out int charStartIndex,
|
||||
out int charLength,
|
||||
out float charProgress
|
||||
@@ -402,7 +389,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
region.LayoutBounds.Width,
|
||||
region.LayoutBounds.Height
|
||||
);
|
||||
maskDs.FillRectangle(rect, Colors.Black);
|
||||
maskDs.FillRectangle(rect, Color.FromArgb(255, 128, 128, 128));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +434,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
fadingWidth
|
||||
);
|
||||
|
||||
maskDs.FillRectangle(highlightRect, Colors.White);
|
||||
maskDs.FillRectangle(highlightRect, Color.FromArgb(255, 128, 128, 128));
|
||||
maskDs.FillRectangle(fadeOutRect, fadeOutBrush);
|
||||
|
||||
highlightMaskDs.FillRectangle(fadeInRect, fadeInBrush);
|
||||
@@ -456,7 +443,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
else
|
||||
{
|
||||
float height = 0f;
|
||||
var regions = textLayout.GetCharacterRegions(0, string.Join("", line.CharTimings.Select(x => x.Text)).Length);
|
||||
//var regions = textLayout.GetCharacterRegions(0, string.Join("", line.LyricsChars.Select(x => x.Text)).Length);
|
||||
var regions = textLayout.GetCharacterRegions(0, line.OriginalText.Length);
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top;
|
||||
@@ -473,12 +461,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
}
|
||||
|
||||
ds.DrawImage(
|
||||
new OpacityEffect
|
||||
using var opacityEffect = new OpacityEffect
|
||||
{
|
||||
Source = new BlendEffect
|
||||
{
|
||||
Source = new BlendEffect
|
||||
{
|
||||
Background = _isLyricsGlowEffectEnabled
|
||||
Background = _isLyricsGlowEffectEnabled
|
||||
? new GaussianBlurEffect
|
||||
{
|
||||
Source = new AlphaMaskEffect
|
||||
@@ -496,21 +483,49 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
Optimization = EffectOptimization.Quality,
|
||||
}
|
||||
: new CanvasCommandList(control.Device),
|
||||
Foreground = new AlphaMaskEffect
|
||||
Foreground = new AlphaMaskEffect
|
||||
{
|
||||
Source = fgLyrics,
|
||||
AlphaMask = _lyricsHighlightScope switch
|
||||
{
|
||||
Source = fgLyrics,
|
||||
AlphaMask = _lyricsHighlightScope switch
|
||||
{
|
||||
LineRenderingType.CurrentChar => highlightMask,
|
||||
LineRenderingType.LineStartToCurrentChar => mask,
|
||||
LineRenderingType.CurrentLine => fgLyrics,
|
||||
_ => mask,
|
||||
},
|
||||
LineRenderingType.CurrentChar => highlightMask,
|
||||
LineRenderingType.LineStartToCurrentChar => mask,
|
||||
LineRenderingType.CurrentLine => fgLyrics,
|
||||
_ => mask,
|
||||
},
|
||||
},
|
||||
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value,
|
||||
},
|
||||
Opacity = line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value,
|
||||
};
|
||||
|
||||
combinedDs.DrawImage(opacityEffect);
|
||||
|
||||
if (i == _playingLineIndex)
|
||||
{
|
||||
if (_isLyricsFloatAnimationEnabled)
|
||||
{
|
||||
ds.DrawImage(new DisplacementMapEffect
|
||||
{
|
||||
Source = combined,
|
||||
Displacement = mask,
|
||||
XChannelSelect = EffectChannelSelect.Red,
|
||||
YChannelSelect = EffectChannelSelect.Alpha,
|
||||
Amount = 1f
|
||||
});
|
||||
}
|
||||
);
|
||||
else
|
||||
{
|
||||
ds.DrawImage(combined);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ds.DrawImage(combined);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ds.DrawImage(combined);
|
||||
}
|
||||
|
||||
// Reset scale
|
||||
@@ -548,7 +563,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
.Select(stops => new CanvasGradientStop
|
||||
{
|
||||
Position = stops.position,
|
||||
Color = Color.FromArgb((byte)(stops.opacity * 255), 0, 0, 0),
|
||||
Color = Color.FromArgb((byte)(stops.opacity * 255), 128, 128, 128),
|
||||
})
|
||||
.ToArray()
|
||||
)
|
||||
|
||||
@@ -78,6 +78,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isFanLyricsEnabled = message.NewValue;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.IsLyricsFloatAnimationEnabled))
|
||||
{
|
||||
_isLyricsFloatAnimationEnabled = message.NewValue;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsWindowViewModel)
|
||||
{
|
||||
@@ -109,6 +113,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_logger.LogInformation("Translation enabled state changed: {IsEnabled}", _isTranslationEnabled);
|
||||
UpdateTranslations();
|
||||
}
|
||||
else if (message.PropertyName == nameof(LyricsPageViewModel.ShowTranslationOnly))
|
||||
{
|
||||
_showTranslationOnly = message.NewValue;
|
||||
UpdateTranslations();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +213,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_timelineSyncThreshold = message.NewValue;
|
||||
}
|
||||
else if (message.PropertyName == nameof(SettingsPageViewModel.LyricsBgFontOpacity))
|
||||
{
|
||||
_defaultOpacity = message.NewValue / 100f;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
}
|
||||
else if (message.Sender is LyricsPageViewModel)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private bool _isCanvasHeightChanged = false;
|
||||
|
||||
private bool _isDisplayTypeChanged = false;
|
||||
|
||||
|
||||
private bool _isPlayingLineChanged = false;
|
||||
private bool _isVisibleLinesBoundaryChanged = false;
|
||||
|
||||
@@ -92,8 +92,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_lyricsXTransition.StartTransition((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 + _leftMargin + _middleMargin, jumpTo);
|
||||
_albumArtXTransition.StartTransition(_leftMargin + ((_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2 - _albumArtSize) / 2, jumpTo);
|
||||
break;
|
||||
case LyricsDisplayType.PlaceholderOnly:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -297,7 +295,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (_adaptiveGrayedFontColor == _lightColor)
|
||||
{
|
||||
grayedEnvironmentalColor = _darkColor;
|
||||
} else if (_adaptiveGrayedFontColor == _darkColor)
|
||||
}
|
||||
else if (_adaptiveGrayedFontColor == _darkColor)
|
||||
{
|
||||
grayedEnvironmentalColor = _lightColor;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using BetterLyrics.WinUI3.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using Lyricify.Lyrics.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
@@ -33,6 +34,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private TimeSpan _totalTime = TimeSpan.Zero;
|
||||
private TimeSpan _positionOffset = TimeSpan.Zero;
|
||||
|
||||
private int _songDurationMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds;
|
||||
|
||||
private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
|
||||
private SoftwareBitmap? _albumArtSwBitmap = null;
|
||||
|
||||
@@ -55,7 +58,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private float _canvasWidth = 0f;
|
||||
private float _canvasHeight = 0f;
|
||||
|
||||
private readonly float _defaultOpacity = 0.3f;
|
||||
private float _defaultOpacity;
|
||||
private readonly float _highlightedOpacity = 1.0f;
|
||||
|
||||
private readonly float _defaultScale = 0.75f;
|
||||
@@ -131,14 +134,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private bool _isDynamicCoverOverlayEnabled;
|
||||
private bool _isLyricsGlowEffectEnabled;
|
||||
|
||||
private bool _isLyricsFloatAnimationEnabled;
|
||||
|
||||
private bool _isLayoutChanged = true;
|
||||
|
||||
private int _langIndex = 0;
|
||||
|
||||
private List<LyricsData> _lyricsDataArr = [];
|
||||
private List<string> _translationList = [];
|
||||
private bool _isTranslationEnabled = false;
|
||||
private int _targetLanguageIndex = 6;
|
||||
private bool _isTranslationEnabled;
|
||||
private bool _showTranslationOnly;
|
||||
private int _targetLanguageIndex;
|
||||
|
||||
private int _timelineSyncThreshold;
|
||||
|
||||
@@ -169,8 +175,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
private LatestOnlyTaskRunner _refreshLyricsRunner = new();
|
||||
private LatestOnlyTaskRunner _showTranslationsRunner = new();
|
||||
|
||||
private LyricsDisplayType _displayTypeReceived = LyricsDisplayType.PlaceholderOnly;
|
||||
private LyricsDisplayType _displayType = LyricsDisplayType.PlaceholderOnly;
|
||||
private LyricsDisplayType _displayTypeReceived;
|
||||
private LyricsDisplayType _displayType;
|
||||
|
||||
private int _albumArtBgBlurAmount;
|
||||
private int _albumArtBgOpacity;
|
||||
@@ -189,29 +195,38 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++)
|
||||
{
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines[i];
|
||||
if (line == null)
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null) continue;
|
||||
var nextLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i + 1);
|
||||
var totalMs = _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds;
|
||||
if (nextLine != null && line.StartMs <= totalMs && totalMs < nextLine.StartMs)
|
||||
{
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
if (
|
||||
line.StartMs <= _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds
|
||||
&& _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds <= line.EndMs
|
||||
)
|
||||
else if (nextLine == null && line.StartMs <= totalMs)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return GetMaxLyricsLineIndexBoundaries().Item2;
|
||||
}
|
||||
|
||||
private void GetLinePlayingProgress(LyricsLine line, out int charStartIndex, out int charLength, out float charProgress)
|
||||
private void GetLinePlayingProgress(int lineIndex, out int charStartIndex, out int charLength, out float charProgress)
|
||||
{
|
||||
charStartIndex = 0;
|
||||
charLength = 0;
|
||||
charProgress = 0f;
|
||||
|
||||
var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(lineIndex);
|
||||
if (line == null) return;
|
||||
var nextLine = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(lineIndex + 1);
|
||||
|
||||
int lineEndMs;
|
||||
if (line.EndMs != null) lineEndMs = line.EndMs.Value;
|
||||
else if (nextLine != null) lineEndMs = nextLine.StartMs;
|
||||
else lineEndMs = _songDurationMs;
|
||||
|
||||
float now = (float)_totalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds;
|
||||
|
||||
// 1. 还没到本句
|
||||
@@ -221,27 +236,37 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
|
||||
// 2. 已经超过本句
|
||||
if (now > line.EndMs)
|
||||
if (now > lineEndMs)
|
||||
{
|
||||
charProgress = 1f;
|
||||
charStartIndex = line.OriginalText.Length - 1;
|
||||
charLength = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 有逐字时间轴
|
||||
if (line.CharTimings != null && line.CharTimings.Count > 0)
|
||||
if (line.LyricsChars != null && line.LyricsChars.Count > 1)
|
||||
{
|
||||
int charTimingsCount = line.CharTimings.Count;
|
||||
int charTimingsCount = line.LyricsChars.Count;
|
||||
for (int i = 0; i < charTimingsCount; i++)
|
||||
{
|
||||
var timing = line.CharTimings[i];
|
||||
var timing = line.LyricsChars[i];
|
||||
var nextTiming = line.LyricsChars.ElementAtOrDefault(i + 1);
|
||||
|
||||
int timingEndMs;
|
||||
if (timing.EndMs != null) timingEndMs = timing.EndMs.Value;
|
||||
else if (nextTiming != null) timingEndMs = nextTiming.StartMs;
|
||||
else timingEndMs = lineEndMs;
|
||||
|
||||
charStartIndex = timing.StartIndex;
|
||||
charLength = timing.Text.Length;
|
||||
|
||||
// 当前时间在某个字的高亮区间
|
||||
if (now >= timing.StartMs && now <= timing.EndMs)
|
||||
if (now >= timing.StartMs && now <= timingEndMs)
|
||||
{
|
||||
charStartIndex = timing.StartIndex;
|
||||
charLength = timing.Text.Length;
|
||||
if (timing.EndMs != timing.StartMs)
|
||||
if (timingEndMs != timing.StartMs)
|
||||
{
|
||||
charProgress = (now - timing.StartMs) / (timing.EndMs - timing.StartMs);
|
||||
charProgress = (now - timing.StartMs) / (timingEndMs - timing.StartMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -249,15 +274,30 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (now > timingEndMs && (nextTiming == null || now < nextTiming?.StartMs))
|
||||
{
|
||||
charProgress = 1f;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有逐字时间轴,直接线性
|
||||
charProgress = (now - line.StartMs) / line.DurationMs;
|
||||
charProgress = Math.Clamp(charProgress, 0f, 1f);
|
||||
charStartIndex = 0;
|
||||
charLength = line.OriginalText.Length;
|
||||
// 没有逐字时间轴,均匀分配每个字的高亮时间
|
||||
int textLength = line.OriginalText.Length;
|
||||
if (textLength == 0) return;
|
||||
|
||||
float lineProgress = (now - line.StartMs) / (lineEndMs - line.StartMs);
|
||||
lineProgress = Math.Clamp(lineProgress, 0f, 1f);
|
||||
|
||||
// 计算当前高亮到第几个字
|
||||
float charFloatIndex = lineProgress * textLength;
|
||||
int charIndex = (int)charFloatIndex;
|
||||
charStartIndex = Math.Clamp(charIndex, 0, textLength - 1);
|
||||
charLength = 1;
|
||||
|
||||
// 当前字的进度(0~1)
|
||||
charProgress = charFloatIndex - charIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,10 +349,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_lastSongArtist = _songArtist;
|
||||
_songArtist = SongInfo?.Artist;
|
||||
|
||||
_songDurationMs = (int)(SongInfo?.DurationMs ?? TimeSpan.FromMinutes(99).TotalMilliseconds);
|
||||
|
||||
_songInfoOpacityTransition.Reset(0f);
|
||||
_songInfoOpacityTransition.StartTransition(1f);
|
||||
|
||||
_logger.LogInformation("Song info changed: Title={Title}, Artist={Artist}, refreshing lyrics...", _songTitle, _songArtist);
|
||||
Debug.WriteLine($"Song info changed: Title={_songTitle}, Artist={_songArtist}");
|
||||
_ = _refreshLyricsRunner.RunAsync(async token =>
|
||||
{
|
||||
await RefreshLyricsAsync(token);
|
||||
@@ -342,6 +385,9 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
private void UpdateTranslations()
|
||||
{
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_isLayoutChanged = true;
|
||||
|
||||
IsTranslating = true;
|
||||
if (_isTranslationEnabled)
|
||||
{
|
||||
@@ -354,7 +400,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextInOriginalText();
|
||||
_lyricsDataArr.ElementAtOrDefault(0)?.SetDisplayedTextInOriginalText();
|
||||
_langIndex = 0;
|
||||
IsTranslating = false;
|
||||
_isLayoutChanged = true;
|
||||
}
|
||||
@@ -378,14 +425,36 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
int found = _translateService.SearchTranslatedLyricsItself(_lyricsDataArr);
|
||||
if (found >= 0)
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found]);
|
||||
if (_showTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[found].SetDisplayedTextInOriginalText();
|
||||
_langIndex = found;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(_lyricsDataArr[found]);
|
||||
_langIndex = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
|
||||
try
|
||||
{
|
||||
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (_showTranslationOnly)
|
||||
{
|
||||
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
|
||||
_lyricsDataArr[^1].SetDisplayedTextInOriginalText();
|
||||
_langIndex = _lyricsDataArr.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
|
||||
_langIndex = 0;
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,10 +467,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_isLayoutChanged = true;
|
||||
|
||||
string? lyricsRaw = null;
|
||||
LyricsSearchProvider? provider = null;
|
||||
|
||||
if (SongInfo != null)
|
||||
{
|
||||
lyricsRaw = await _lyrcsSearchService.SearchAsync(
|
||||
(lyricsRaw, provider) = await _lyrcsSearchService.SearchAsync(
|
||||
SongInfo.Title,
|
||||
SongInfo.Artist,
|
||||
SongInfo.Album ?? "",
|
||||
@@ -410,13 +480,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
);
|
||||
_logger.LogInformation("Lyrics search result: {LyricsRaw}", lyricsRaw ?? "null");
|
||||
token.ThrowIfCancellationRequested();
|
||||
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
|
||||
FillTranslationFromCache(provider);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
|
||||
}
|
||||
|
||||
_lyricsDataArr = new LyricsParser().Parse(lyricsRaw, (int?)SongInfo?.DurationMs);
|
||||
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _lyricsDataArr.Count);
|
||||
|
||||
// This ensures that original lyrics are always shown while waiting for translations
|
||||
@@ -425,5 +496,43 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
UpdateTranslations();
|
||||
}
|
||||
|
||||
private void FillTranslationFromCache(LyricsSearchProvider? provider)
|
||||
{
|
||||
string? translationRaw = null;
|
||||
switch (provider)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
break;
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalMusicFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalLrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalEslrcFile:
|
||||
break;
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (translationRaw != null)
|
||||
{
|
||||
var translationData = new LyricsParser().Parse(translationRaw, (int?)SongInfo?.DurationMs);
|
||||
foreach (var data in translationData)
|
||||
{
|
||||
data.LyricsLines = data.LyricsLines.Where(line => !string.IsNullOrWhiteSpace(line.OriginalText)).ToList();
|
||||
}
|
||||
_lyricsDataArr = _lyricsDataArr.Concat(translationData).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,11 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.System;
|
||||
using Windows.UI;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
@@ -24,15 +27,24 @@ namespace BetterLyrics.WinUI3
|
||||
public partial class LyricsWindowViewModel
|
||||
: BaseWindowViewModel,
|
||||
IRecipient<PropertyChangedMessage<int>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>,
|
||||
IRecipient<PropertyChangedMessage<ElementTheme>>,
|
||||
IRecipient<PropertyChangedMessage<bool>>
|
||||
IRecipient<PropertyChangedMessage<DockPlacement>>
|
||||
{
|
||||
private ForegroundWindowWatcher? _windowWatcher = null;
|
||||
private bool _ignoreFullscreenWindow = false;
|
||||
private int _dockWindowMinHeight = 96;
|
||||
|
||||
private DockPlacement _dockPlacement;
|
||||
private int _lyricsFontSize;
|
||||
|
||||
public LyricsWindowViewModel(ISettingsService settingsService) : base(settingsService)
|
||||
{
|
||||
_ignoreFullscreenWindow = _settingsService.IgnoreFullscreenWindow;
|
||||
IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
_dockPlacement = _settingsService.DockPlacement;
|
||||
_lyricsFontSize = _settingsService.LyricsFontSize;
|
||||
OnIsImmersiveModeChanged(_settingsService.IsImmersiveMode);
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -51,6 +63,13 @@ namespace BetterLyrics.WinUI3
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsWindowLocked { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsImmersiveMode { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial float TopCommandGridOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ElementTheme ThemeType { get; set; } = ElementTheme.Default;
|
||||
|
||||
@@ -61,6 +80,29 @@ namespace BetterLyrics.WinUI3
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsMouseWithinWindow { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string LockHotKey { get; set; } = "";
|
||||
|
||||
private void UpdateDockWindow()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DockModeHelper.UpdateAppBarHeight(WindowNative.GetWindowHandle(window), Math.Max(_dockWindowMinHeight, _lyricsFontSize * 4), _dockPlacement);
|
||||
}
|
||||
|
||||
partial void OnIsImmersiveModeChanged(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
TopCommandGridOpacity = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
TopCommandGridOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
if (message.Sender is SystemTrayViewModel)
|
||||
@@ -99,18 +141,41 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsFontSize))
|
||||
{
|
||||
if (IsDockMode)
|
||||
_lyricsFontSize = message.NewValue;
|
||||
UpdateDockWindow();
|
||||
}
|
||||
else if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.LockHotKeyIndex))
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DockModeHelper.UpdateAppBarHeight(WindowNative.GetWindowHandle(window), message.NewValue * 4);
|
||||
UpdateLockHotKey(message.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StartWatchWindowColorChange(WindowPixelSampleMode mode)
|
||||
private void UpdateLockHotKey(int hotKeyIndex)
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
GlobalHotKeyHelper.UnregisterAllHotKeys(window);
|
||||
GlobalHotKeyHelper.RegisterHotKey(
|
||||
window,
|
||||
User32.HotKeyModifiers.MOD_CONTROL | User32.HotKeyModifiers.MOD_ALT,
|
||||
(uint)(hotKeyIndex + (int)VirtualKey.A),
|
||||
() =>
|
||||
{
|
||||
if (IsDesktopMode)
|
||||
{
|
||||
ToggleLockWindowCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
LockHotKey = ((VirtualKey)(hotKeyIndex + (int)VirtualKey.A)).ToString();
|
||||
}
|
||||
|
||||
public void StartWatchWindowColorChange()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
@@ -124,11 +189,11 @@ namespace BetterLyrics.WinUI3
|
||||
{
|
||||
presenter.IsAlwaysOnTop = true;
|
||||
}
|
||||
UpdateAccentColor(hwnd, mode);
|
||||
UpdateAccentColor(hwnd);
|
||||
}
|
||||
);
|
||||
_windowWatcher.Start();
|
||||
UpdateAccentColor(hwnd, mode);
|
||||
UpdateAccentColor(hwnd);
|
||||
}
|
||||
|
||||
private void StopWatchWindowColorChange()
|
||||
@@ -137,19 +202,35 @@ namespace BetterLyrics.WinUI3
|
||||
_windowWatcher = null;
|
||||
}
|
||||
|
||||
public void UpdateAccentColor(nint hwnd, WindowPixelSampleMode mode)
|
||||
public void UpdateAccentColor(nint hwnd)
|
||||
{
|
||||
WindowPixelSampleMode mode = IsDesktopMode ? WindowPixelSampleMode.WindowEdge : _dockPlacement.ToWindowPixelSampleMode();
|
||||
ActivatedWindowAccentColor = Helper.ColorHelper.GetAccentColor(hwnd, mode).ToColor();
|
||||
}
|
||||
|
||||
public void InitLockHotKey()
|
||||
{
|
||||
UpdateLockHotKey(_settingsService.LockHotKeyIndex);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LockWindow()
|
||||
private void ToggleLockWindow()
|
||||
{
|
||||
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
|
||||
if (window == null) return;
|
||||
|
||||
DesktopModeHelper.SetClickThrough(window, true);
|
||||
IsLyricsWindowLocked = true;
|
||||
if (IsLyricsWindowLocked)
|
||||
{
|
||||
DesktopModeHelper.SetClickThrough(window, false);
|
||||
IsLyricsWindowLocked = false;
|
||||
IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
DesktopModeHelper.SetClickThrough(window, true);
|
||||
IsLyricsWindowLocked = true;
|
||||
IsImmersiveMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -164,7 +245,7 @@ namespace BetterLyrics.WinUI3
|
||||
if (IsDesktopMode)
|
||||
{
|
||||
DesktopModeHelper.Enable(window);
|
||||
StartWatchWindowColorChange(WindowPixelSampleMode.WindowEdge);
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,13 +264,31 @@ namespace BetterLyrics.WinUI3
|
||||
IsDockMode = !IsDockMode;
|
||||
if (IsDockMode)
|
||||
{
|
||||
DockModeHelper.Enable(window, _settingsService.LyricsFontSize * 4);
|
||||
StartWatchWindowColorChange(WindowPixelSampleMode.BelowWindow);
|
||||
DockModeHelper.Enable(window, Math.Max(_dockWindowMinHeight, _lyricsFontSize * 4), _dockPlacement);
|
||||
StartWatchWindowColorChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
DockModeHelper.Disable(window);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OnImmersiveToggleButtonEnabledChanged()
|
||||
{
|
||||
_settingsService.IsImmersiveMode = IsImmersiveMode;
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<DockPlacement> message)
|
||||
{
|
||||
if (message.Sender is SettingsPageViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(SettingsPageViewModel.DockPlacement))
|
||||
{
|
||||
_dockPlacement = message.NewValue;
|
||||
UpdateDockWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using ShadowViewer.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -89,6 +90,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
LyricsScrollDuration = _settingsService.LyricsScrollDuration;
|
||||
TimelineSyncThreshold = _settingsService.TimelineSyncThreshold;
|
||||
|
||||
IsLyricsFloatAnimationEnabled = _settingsService.IsLyricsFloatAnimationEnabled;
|
||||
ResetPositionOffsetOnSongChanged = _settingsService.ResetPositionOffsetOnSongChanged;
|
||||
LockHotKeyIndex = _settingsService.LockHotKeyIndex;
|
||||
|
||||
LXMusicServer = _settingsService.LXMusicServer;
|
||||
DockPlacement = _settingsService.DockPlacement;
|
||||
LyricsBgFontOpacity = _settingsService.LyricsBgFontOpacity;
|
||||
|
||||
_playbackService.MediaSourceProvidersInfoChanged += PlaybackService_SessionIdsChanged;
|
||||
|
||||
Task.Run(async () =>
|
||||
@@ -102,6 +111,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
MediaSourceProvidersInfo = [.. e.MediaSourceProviersInfo];
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial DockPlacement DockPlacement { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LockHotKeyIndex { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial ElementTheme LyricsBackgroundTheme { get; set; }
|
||||
@@ -185,6 +202,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial Color LyricsCustomStrokeFontColor { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int LyricsBgFontOpacity { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial LyricsFontColorType LyricsBgFontColorType { get; set; }
|
||||
@@ -224,6 +245,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[ObservableProperty]
|
||||
public partial object NavViewSelectedItemTag { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool ResetPositionOffsetOnSongChanged { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsLyricsFloatAnimationEnabled { get; set; }
|
||||
|
||||
public string Version { get; set; } = MetadataHelper.AppVersion;
|
||||
|
||||
public string BuildDate { get; set; } = string.Empty;
|
||||
@@ -258,6 +287,13 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial int TimelineSyncThreshold { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLXMusicServerTesting { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial string LXMusicServer { get; set; }
|
||||
|
||||
public void OnLyricsSearchProvidersReordered()
|
||||
{
|
||||
_settingsService.LyricsSearchProvidersInfo = [.. LyricsSearchProvidersInfo];
|
||||
@@ -399,7 +435,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageLibreTranslateTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
});
|
||||
}
|
||||
@@ -407,13 +443,30 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageLibreTranslateTestFailedInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
|
||||
IsLibreTranslateServerTesting = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void LXMusicServerTest()
|
||||
{
|
||||
IsLXMusicServerTesting = true;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
bool testResult = await NetHelper.CheckConnectivity($"{LXMusicServer}/status");
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
App.Current.SettingsWindowNotificationPanel?.Notify(
|
||||
App.ResourceLoader!.GetString($"SettingsPageServerTest{(testResult ? "Success" : "Failed")}Info"),
|
||||
testResult ? InfoBarSeverity.Success : InfoBarSeverity.Error);
|
||||
IsLXMusicServerTesting = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> ToggleAutoStartupAsync(bool target)
|
||||
{
|
||||
StartupTask startupTask = await StartupTask.GetAsync(_autoStartupTaskId);
|
||||
@@ -446,6 +499,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
return result;
|
||||
}
|
||||
|
||||
partial void OnDockPlacementChanged(DockPlacement value)
|
||||
{
|
||||
_settingsService.DockPlacement = value;
|
||||
}
|
||||
partial void OnLyricsScrollEasingTypeChanged(EasingType value)
|
||||
{
|
||||
_settingsService.LyricsScrollEasingType = value;
|
||||
@@ -474,6 +531,10 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_settingsService.LibreTranslateServer = value;
|
||||
}
|
||||
partial void OnLXMusicServerChanged(string value)
|
||||
{
|
||||
_settingsService.LXMusicServer = value;
|
||||
}
|
||||
partial void OnAutoStartWindowTypeChanged(AutoStartWindowType value)
|
||||
{
|
||||
_settingsService.AutoStartWindowType = value;
|
||||
@@ -597,6 +658,17 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
_settingsService.TimelineSyncThreshold = value;
|
||||
}
|
||||
|
||||
partial void OnIsLyricsFloatAnimationEnabledChanged(bool value)
|
||||
{
|
||||
_settingsService.IsLyricsFloatAnimationEnabled = value;
|
||||
}
|
||||
partial void OnResetPositionOffsetOnSongChangedChanged(bool value)
|
||||
{
|
||||
_settingsService.ResetPositionOffsetOnSongChanged = value;
|
||||
}
|
||||
partial void OnLyricsBgFontOpacityChanged(int value)
|
||||
{
|
||||
_settingsService.LyricsBgFontOpacity = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid">
|
||||
<Grid x:Name="RootGrid" SizeChanged="RootGrid_SizeChanged">
|
||||
<!-- Lyrics area -->
|
||||
<renderer:LyricsRenderer />
|
||||
|
||||
<!-- No music playing placeholder -->
|
||||
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource SolidBackgroundFillColorBaseBrush}">
|
||||
<Grid x:Name="NoMusicPlayingGrid" Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
|
||||
<TextBlock
|
||||
x:Uid="MainPageNoMusicPlaying"
|
||||
HorizontalAlignment="Center"
|
||||
@@ -32,145 +32,298 @@
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.SongInfo, Mode=OneWay}"
|
||||
ComparisonCondition="NotEqual"
|
||||
Value="{x:Null}">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
Padding="12"
|
||||
Margin="{x:Bind ViewModel.BottomCommandGridMargin, Mode=OneWay}"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
Opacity="0"
|
||||
Opacity="{x:Bind ViewModel.BottomCommandGridOpacity, Mode=OneWay}"
|
||||
PointerEntered="BottomCommandGrid_PointerEntered"
|
||||
PointerExited="BottomCommandGrid_PointerExited">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Position offset -->
|
||||
<Button Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Grid Padding="3" HorizontalAlignment="Left">
|
||||
<StackPanel
|
||||
x:Name="BottomLeftCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<!--<Button Style="{StaticResource GhostButtonStyle}">
|
||||
<Grid>
|
||||
-->
|
||||
<!-- Volumn: 0 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel0"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 1-32 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel1"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 33-65 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel2"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
-->
|
||||
<!-- Volumn: 66-100 -->
|
||||
<!--
|
||||
<FontIcon
|
||||
x:Name="VolumeLevel3"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph="">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.Volume, Mode=OneWay}" />
|
||||
<TextBlock Margin="0,0,14,0" VerticalAlignment="Center" />
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
Width="150"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<TextBlock x:Uid="LyricsPagePositionOffsetHint" Opacity="0.5" />
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="None"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</Button>-->
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Translation -->
|
||||
<ToggleButton
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
IsChecked="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}"
|
||||
Style="{StaticResource GhostToggleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</ToggleButton>
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource CardGridStyle}"
|
||||
Translation="{x:Bind ViewModel.BottomCenterCommandGridTranslation, Mode=OneWay}">
|
||||
<Grid.TranslationTransition>
|
||||
<Vector3Transition />
|
||||
</Grid.TranslationTransition>
|
||||
<StackPanel
|
||||
x:Name="BottomCenterCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PreviousSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PauseSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.PlaySongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="True">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
<interactivity:DataTriggerBehavior
|
||||
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
|
||||
ComparisonCondition="Equal"
|
||||
Value="False">
|
||||
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.NextSongCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Display type -->
|
||||
<Button
|
||||
x:Name="DisplayTypeSwitchButton"
|
||||
x:Uid="MainPageDisplayTypeSwitcher"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="PresentationTypeToolTip" x:Uid="LyricsPageDisplayTypeButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="12,2,12,8" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<RadioButtons MaxColumns="1" SelectedIndex="{x:Bind ViewModel.DisplayType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<RadioButton x:Uid="MainPageAlbumArtOnly" Click="AlbumArtOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageLyriscOnly" Click="LyricsOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageSplitView" Click="SplitViewRadioButton_Click" />
|
||||
</RadioButtons>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="BottomRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Settings -->
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="SettingsToolTip" x:Uid="LyricsPageSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<!-- Position offset -->
|
||||
<Button Click="TimelineOffsetButton_Click" Style="{StaticResource GhostButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TimelineOffsetToolTip" x:Uid="LyricsPageTimelineOffsetButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="TimelineOffsetFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<Slider
|
||||
x:Uid="MainPagePositionOffsetSlider"
|
||||
Maximum="5000"
|
||||
Minimum="-5000"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="100"
|
||||
TickFrequency="100"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.PositionOffset, Mode=TwoWay}" />
|
||||
<RelativePanel>
|
||||
<TextBlock
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Text="{x:Bind ViewModel.PositionOffset, Mode=OneWay}" />
|
||||
<Button
|
||||
Click="PositionOffsetResetButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignVerticalCenterWithPanel="True"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</RelativePanel>
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.ResetPositionOffsetOnSongChanged, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="LyricsPagePositionOffsetHint" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
<!-- Translation -->
|
||||
<Button
|
||||
Click="TranslationButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="TranslationToolTip" x:Uid="LyricsPageTranslationButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="TranslationFlyout" ShouldConstrainToRootBounds="False">
|
||||
<StackPanel>
|
||||
<ToggleSwitch x:Uid="LyricsPageTranslationEnabled" IsOn="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}" />
|
||||
<ToggleSwitch
|
||||
x:Uid="LyricsPageTranslationOnly"
|
||||
IsEnabled="{x:Bind ViewModel.IsTranslationEnabled, Mode=OneWay}"
|
||||
IsOn="{x:Bind ViewModel.ShowTranslationOnly, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
<!-- Display type -->
|
||||
<Button
|
||||
x:Name="DisplayTypeSwitchButton"
|
||||
x:Uid="MainPageDisplayTypeSwitcher"
|
||||
Click="DisplayTypeSwitchButton_Click"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="PresentationTypeToolTip" x:Uid="LyricsPageDisplayTypeButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.DataContext>
|
||||
<Flyout x:Name="DisplayTypeSwitchFlyout" ShouldConstrainToRootBounds="false">
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="12,2,12,8" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<RadioButtons MaxColumns="1" SelectedIndex="{x:Bind ViewModel.DisplayType, Mode=OneWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<RadioButton x:Uid="MainPageAlbumArtOnly" Click="AlbumArtOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageLyriscOnly" Click="LyricsOnlyRadioButton_Click" />
|
||||
<RadioButton x:Uid="MainPageSplitView" Click="SplitViewRadioButton_Click" />
|
||||
</RadioButtons>
|
||||
</Flyout>
|
||||
</Button.DataContext>
|
||||
</Button>
|
||||
|
||||
<!-- Settings -->
|
||||
<Button
|
||||
x:Name="SettingsButton"
|
||||
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="SettingsToolTip" x:Uid="LyricsPageSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -184,29 +337,64 @@
|
||||
<uc:SystemTray />
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="MusicPlayingStates">
|
||||
<VisualState x:Name="MusicPlaying">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:IsNotEqualStateTrigger Value="{x:Bind ViewModel.DisplayType, Converter={StaticResource EnumToIntConverter}, Mode=OneWay}" To="3" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Visible" />
|
||||
<Setter Target="NoMusicPlayingGrid.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="NoMusicPlaying">
|
||||
<!--<VisualStateGroup x:Name="VolumeState">
|
||||
<VisualState x:Name="Volume0">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:CompareStateTrigger
|
||||
Comparison="Equal"
|
||||
Value="{x:Bind ViewModel.DisplayType, Converter={StaticResource EnumToIntConverter}, Mode=OneWay}"
|
||||
To="3" />
|
||||
Value="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
To="0" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="DisplayTypeSwitchButton.Visibility" Value="Collapsed" />
|
||||
<Setter Target="NoMusicPlayingGrid.Opacity" Value="1" />
|
||||
<Setter Target="VolumeLevel0.Opacity" Value="1" />
|
||||
<Setter Target="VolumeLevel1.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel2.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel3.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualState x:Name="Volume1">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:CompareStateTrigger
|
||||
Comparison="LessThanOrEqual"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
To="32" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="VolumeLevel0.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel1.Opacity" Value="1" />
|
||||
<Setter Target="VolumeLevel2.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel3.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Volume2">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:CompareStateTrigger
|
||||
Comparison="LessThanOrEqual"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
To="65" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="VolumeLevel0.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel1.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel2.Opacity" Value="1" />
|
||||
<Setter Target="VolumeLevel3.Opacity" Value="0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Volume3">
|
||||
<VisualState.StateTriggers>
|
||||
<ui:CompareStateTrigger
|
||||
Comparison="LessThanOrEqual"
|
||||
Value="{x:Bind ViewModel.Volume, Mode=OneWay}"
|
||||
To="100" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="VolumeLevel0.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel1.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel2.Opacity" Value="0" />
|
||||
<Setter Target="VolumeLevel3.Opacity" Value="1" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>-->
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Services;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -12,6 +13,8 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class LyricsPage : Page
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
|
||||
public LyricsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -28,17 +31,20 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void LyricsOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
ViewModel.DisplayType = LyricsDisplayType.LyricsOnly;
|
||||
_settingsService.DisplayType = ViewModel.DisplayType;
|
||||
}
|
||||
|
||||
private void AlbumArtOnlyRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.AlbumArtOnly;
|
||||
ViewModel.DisplayType = LyricsDisplayType.AlbumArtOnly;
|
||||
_settingsService.DisplayType = ViewModel.DisplayType;
|
||||
}
|
||||
|
||||
private void SplitViewRadioButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.SplitView;
|
||||
ViewModel.DisplayType = LyricsDisplayType.SplitView;
|
||||
_settingsService.DisplayType = ViewModel.DisplayType;
|
||||
}
|
||||
|
||||
private void PositionOffsetResetButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -48,12 +54,45 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
BottomCommandGrid.Opacity = 1;
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
|
||||
{
|
||||
BottomCommandGrid.Opacity = 0;
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
{
|
||||
ViewModel.BottomCommandGridOpacity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayTypeSwitchButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DisplayTypeSwitchFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void TimelineOffsetButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TimelineOffsetFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void TranslationButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TranslationFlyout.ShowAt(BottomRightCommandStackPanel);
|
||||
}
|
||||
|
||||
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (e.NewSize.Width < 500)
|
||||
{
|
||||
ViewModel.BottomCenterCommandGridTranslation = new System.Numerics.Vector3(0, -48, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.BottomCenterCommandGridTranslation = new System.Numerics.Vector3(0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,137 +17,172 @@
|
||||
x:Name="RootGrid"
|
||||
PointerEntered="RootGrid_PointerEntered"
|
||||
PointerExited="RootGrid_PointerExited"
|
||||
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
|
||||
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}"
|
||||
SizeChanged="RootGrid_SizeChanged">
|
||||
|
||||
<local:LyricsPage />
|
||||
|
||||
<!-- Top command -->
|
||||
<Grid
|
||||
x:Name="TopCommandGrid"
|
||||
Margin="6"
|
||||
Margin="12"
|
||||
VerticalAlignment="Top"
|
||||
Opacity="0"
|
||||
Opacity="{x:Bind ViewModel.TopCommandGridOpacity, Mode=OneWay}"
|
||||
PointerEntered="TopCommandGrid_PointerEntered"
|
||||
PointerExited="TopCommandGrid_PointerExited">
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
|
||||
<StackPanel
|
||||
x:Name="TopLeftCommandStackPanel"
|
||||
Margin="12"
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
</StackPanel>
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="TopLeftCommandStackPanel"
|
||||
HorizontalAlignment="Left"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
<ToggleButton
|
||||
x:Name="ImmersiveButton"
|
||||
Command="{x:Bind ViewModel.ImmersiveToggleButtonEnabledChangedCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsImmersiveMode, Mode=TwoWay}"
|
||||
Style="{StaticResource TitleBarToggleButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize}"
|
||||
Glyph="" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel
|
||||
x:Name="TopRightCommandStackPanel"
|
||||
<Grid
|
||||
Padding="3"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
Style="{StaticResource CardGridStyle}">
|
||||
<StackPanel
|
||||
x:Name="TopRightCommandStackPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<StackPanel.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</StackPanel.OpacityTransition>
|
||||
|
||||
<!-- Look -->
|
||||
<Button
|
||||
x:Name="ClickThroughButton"
|
||||
Command="{x:Bind ViewModel.LockWindowCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="LockToolTip" x:Uid="HostWindowLockToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<!-- More -->
|
||||
<Button x:Name="MoreButton" Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="MoreButtonToolTip" x:Uid="HostWindowMoreButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="AOTFlyoutItem"
|
||||
x:Uid="BaseWindowAOTFlyoutItem"
|
||||
Click="AOTFlyoutItem_Click" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="FullScreenFlyoutItem"
|
||||
x:Uid="BaseWindowFullScreenFlyoutItem"
|
||||
Click="FullScreenFlyoutItem_Click" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DockFlyoutItem"
|
||||
x:Uid="HostWindowDockFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDockModeCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsDockMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DesktopFlyoutItem"
|
||||
x:Uid="HostWindowDesktopFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDesktopModeCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsDesktopMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="MiniFlyoutItem"
|
||||
x:Uid="BaseWindowMiniFlyoutItem"
|
||||
Click="MiniFlyoutItem_Click" />
|
||||
<MenuFlyoutItem
|
||||
x:Name="SettingsFlyoutItem"
|
||||
x:Uid="HostWindowSettingsFlyoutItem"
|
||||
Click="SettingsMenuFlyoutItem_Click" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<!-- Look -->
|
||||
<Button
|
||||
x:Name="ClickThroughButton"
|
||||
Command="{x:Bind ViewModel.ToggleLockWindowCommand}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="LockToolTip">
|
||||
<ToolTip.Content>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="HostWindowLockToolTip" />
|
||||
<TextBlock
|
||||
Margin="6,0"
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0.7"
|
||||
Text="Ctrl + Alt + " />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Opacity="0.7"
|
||||
Text="{x:Bind ViewModel.LockHotKey, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</ToolTip.Content>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<!-- More -->
|
||||
<Button x:Name="MoreButton" Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Name="MoreButtonToolTip" x:Uid="HostWindowMoreButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="AOTFlyoutItem"
|
||||
x:Uid="BaseWindowAOTFlyoutItem"
|
||||
Click="AOTFlyoutItem_Click" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="FullScreenFlyoutItem"
|
||||
x:Uid="BaseWindowFullScreenFlyoutItem"
|
||||
Click="FullScreenFlyoutItem_Click" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DockFlyoutItem"
|
||||
x:Uid="HostWindowDockFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDockModeCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsDockMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="DesktopFlyoutItem"
|
||||
x:Uid="HostWindowDesktopFlyoutItem"
|
||||
Command="{x:Bind ViewModel.ToggleDesktopModeCommand}"
|
||||
IsChecked="{x:Bind ViewModel.IsDesktopMode, Mode=OneWay}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Name="MiniFlyoutItem"
|
||||
x:Uid="BaseWindowMiniFlyoutItem"
|
||||
Click="MiniFlyoutItem_Click" />
|
||||
<MenuFlyoutItem
|
||||
x:Name="SettingsFlyoutItem"
|
||||
x:Uid="HostWindowSettingsFlyoutItem"
|
||||
Click="SettingsMenuFlyoutItem_Click" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
|
||||
<!-- Window Minimise -->
|
||||
<Button
|
||||
x:Name="MinimiseButton"
|
||||
Click="MinimiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Maximise -->
|
||||
<Button
|
||||
x:Name="MaximiseButton"
|
||||
Click="MaximiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Restore -->
|
||||
<Button
|
||||
x:Name="RestoreButton"
|
||||
Click="RestoreButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Close -->
|
||||
<Button
|
||||
x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<!-- Window Minimise -->
|
||||
<Button
|
||||
x:Name="MinimiseButton"
|
||||
Click="MinimiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Maximise -->
|
||||
<Button
|
||||
x:Name="MaximiseButton"
|
||||
Click="MaximiseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Restore -->
|
||||
<Button
|
||||
x:Name="RestoreButton"
|
||||
Click="RestoreButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}"
|
||||
Visibility="Collapsed">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
<!-- Window Close -->
|
||||
<Button
|
||||
x:Name="CloseButton"
|
||||
Click="CloseButton_Click"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -10,13 +10,19 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using System.Drawing;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Vanara.PInvoke;
|
||||
using Windows.System;
|
||||
using WinUIEx.Messaging;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class LyricsWindow : Window
|
||||
{
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly WindowMessageMonitor _wmm;
|
||||
|
||||
public LyricsWindow()
|
||||
{
|
||||
@@ -28,6 +34,19 @@ namespace BetterLyrics.WinUI3.Views
|
||||
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Collapsed;
|
||||
Title = App.ResourceLoader!.GetString("LyricsPageTitle");
|
||||
SetTitleBar(TopCommandGrid);
|
||||
//SetTitleBar(RootGrid);
|
||||
|
||||
_wmm = new WindowMessageMonitor(this);
|
||||
_wmm.WindowMessageReceived += Wmm_WindowMessageReceived;
|
||||
}
|
||||
|
||||
private void Wmm_WindowMessageReceived(object? sender, WindowMessageEventArgs e)
|
||||
{
|
||||
if (e.Message.MessageId == (uint)User32.WindowMessage.WM_HOTKEY)
|
||||
{
|
||||
int id = (int)e.Message.WParam;
|
||||
GlobalHotKeyHelper.TryInvokeAction(id);
|
||||
}
|
||||
}
|
||||
|
||||
public LyricsWindowViewModel ViewModel { get; private set; } = Ioc.Default.GetRequiredService<LyricsWindowViewModel>();
|
||||
@@ -54,7 +73,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
ViewModel.ToggleDesktopModeCommand.Execute(null);
|
||||
if (autoLook == null && _settingsService.AutoLockOnDesktopMode)
|
||||
{
|
||||
ViewModel.LockWindowCommand.Execute(null);
|
||||
ViewModel.ToggleLockWindowCommand.Execute(null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -138,6 +157,8 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void UpdateTitleBarWindowButtonsVisibility()
|
||||
{
|
||||
TopCommandGrid.Margin = new Thickness(12);
|
||||
|
||||
switch (AppWindow.Presenter.Kind)
|
||||
{
|
||||
case AppWindowPresenterKind.Default:
|
||||
@@ -147,6 +168,7 @@ namespace BetterLyrics.WinUI3.Views
|
||||
AOTFlyoutItem.Visibility = DesktopFlyoutItem.Visibility = FullScreenFlyoutItem.Visibility = DockFlyoutItem.Visibility =
|
||||
ClickThroughButton.Visibility = Visibility.Collapsed;
|
||||
|
||||
ViewModel.IsImmersiveMode = true;
|
||||
break;
|
||||
case AppWindowPresenterKind.FullScreen:
|
||||
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
|
||||
@@ -157,7 +179,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
DockFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
FullScreenFlyoutItem.IsChecked = true;
|
||||
|
||||
break;
|
||||
case AppWindowPresenterKind.Overlapped:
|
||||
DockFlyoutItem.Visibility = Visibility.Visible;
|
||||
@@ -175,6 +196,8 @@ namespace BetterLyrics.WinUI3.Views
|
||||
MiniFlyoutItem.Visibility =
|
||||
Visibility.Collapsed;
|
||||
|
||||
ViewModel.IsImmersiveMode = true;
|
||||
TopCommandGrid.Margin = new Thickness();
|
||||
}
|
||||
else if (DesktopFlyoutItem.IsChecked)
|
||||
{
|
||||
@@ -189,7 +212,6 @@ namespace BetterLyrics.WinUI3.Views
|
||||
Visibility.Collapsed;
|
||||
|
||||
ClickThroughButton.Visibility = Visibility.Visible;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -217,6 +239,8 @@ namespace BetterLyrics.WinUI3.Views
|
||||
MaximiseButton.Visibility = Visibility.Visible;
|
||||
RestoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
ViewModel.IsImmersiveMode = _settingsService.IsImmersiveMode;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -255,12 +279,18 @@ namespace BetterLyrics.WinUI3.Views
|
||||
|
||||
private void TopCommandGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
TopCommandGrid.Opacity = 1;
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
{
|
||||
ViewModel.TopCommandGridOpacity = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void TopCommandGrid_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
TopCommandGrid.Opacity = 0;
|
||||
if (ViewModel.IsImmersiveMode)
|
||||
{
|
||||
ViewModel.TopCommandGridOpacity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void TipContainerCenter_Loaded(object sender, RoutedEventArgs e)
|
||||
@@ -277,5 +307,21 @@ namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
ViewModel.IsMouseWithinWindow = false;
|
||||
}
|
||||
|
||||
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (ClickThroughButton == null) return;
|
||||
|
||||
// <20><>ȡ<EFBFBD><C8A1><EFBFBD>ؼ<EFBFBD><D8BC>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD>е<EFBFBD>λ<EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͻǣ<CFBD>
|
||||
var transform = ClickThroughButton.TransformToVisual(Content);
|
||||
var point = transform.TransformPoint(new Windows.Foundation.Point(0, 0));
|
||||
var btnRect = new Rectangle(
|
||||
(int)point.X,
|
||||
(int)point.Y,
|
||||
(int)ClickThroughButton.ActualWidth,
|
||||
(int)ClickThroughButton.ActualHeight
|
||||
);
|
||||
DesktopModeHelper.SetInteractiveRects([btnRect]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,12 +134,64 @@
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IgnoreFullscreenWindow, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- Desktop mode -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppDesktop" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageAutoLock" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AutoLockOnDesktopMode, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageIgnoreFullscreenWindow" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IgnoreFullscreenWindow, Mode=TwoWay}" />
|
||||
<controls:SettingsCard x:Uid="SettingsPageLockHotKey" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock
|
||||
Margin="0,0,0,2"
|
||||
VerticalAlignment="Center"
|
||||
Text="Ctrl + Alt + " />
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LockHotKeyIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem Content="A" />
|
||||
<ComboBoxItem Content="B" />
|
||||
<ComboBoxItem Content="C" />
|
||||
<ComboBoxItem Content="D" />
|
||||
<ComboBoxItem Content="E" />
|
||||
<ComboBoxItem Content="F" />
|
||||
<ComboBoxItem Content="G" />
|
||||
<ComboBoxItem Content="H" />
|
||||
<ComboBoxItem Content="I" />
|
||||
<ComboBoxItem Content="J" />
|
||||
<ComboBoxItem Content="K" />
|
||||
<ComboBoxItem Content="L" />
|
||||
<ComboBoxItem Content="M" />
|
||||
<ComboBoxItem Content="N" />
|
||||
<ComboBoxItem Content="O" />
|
||||
<ComboBoxItem Content="P" />
|
||||
<ComboBoxItem Content="Q" />
|
||||
<ComboBoxItem Content="R" />
|
||||
<ComboBoxItem Content="S" />
|
||||
<ComboBoxItem Content="T" />
|
||||
<ComboBoxItem Content="U" />
|
||||
<ComboBoxItem Content="V" />
|
||||
<ComboBoxItem Content="W" />
|
||||
<ComboBoxItem Content="X" />
|
||||
<ComboBoxItem Content="Y" />
|
||||
<ComboBoxItem Content="Z" />
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<!-- Dock mode -->
|
||||
|
||||
<TextBlock x:Uid="SettingsPageAppDock" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageDockPlacement" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.DockPlacement, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementTop" />
|
||||
<ComboBoxItem x:Uid="SettingsPageDockPlacementBottom" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
</StackPanel>
|
||||
@@ -448,6 +500,25 @@
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsBgFontOpacity" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind ViewModel.LyricsBgFontOpacity, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,0,14,0"
|
||||
VerticalAlignment="Center"
|
||||
Text=" %" />
|
||||
<Slider
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
SnapsTo="Ticks"
|
||||
StepFrequency="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="Outside"
|
||||
Value="{x:Bind ViewModel.LyricsBgFontOpacity, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFontStrokeWidth" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock x:Uid="SettingsPageSliderPrefix" VerticalAlignment="Center" />
|
||||
@@ -668,7 +739,7 @@
|
||||
IsExpanded="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=TwoWay}" />
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsGlowEffectScope" IsEnabled="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<controls:SettingsCard x:Uid="SettingsPageScope" IsEnabled="{x:Bind ViewModel.IsLyricsGlowEffectEnabled, Mode=OneWay}">
|
||||
<ComboBox SelectedIndex="{x:Bind ViewModel.LyricsGlowEffectScope, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeCurrentChar" />
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsRendingScopeLineStartToCurrentChar" />
|
||||
@@ -678,6 +749,10 @@
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLyricsFloatAnimation" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsLyricsFloatAnimationEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageFan" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.IsFanLyricsEnabled, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
@@ -825,6 +900,10 @@
|
||||
Glyph=}"
|
||||
IsClickEnabled="True" />
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageFAQ" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<HyperlinkButton Content="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md" NavigateUri="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageQQGroup" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/QQ.png}">
|
||||
<Button x:Uid="SettingsPageJoinNowButton" Click="QQGroupButton_Click" />
|
||||
</controls:SettingsCard>
|
||||
@@ -871,6 +950,20 @@
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="SettingsPageLXMusicServer">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBox
|
||||
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
|
||||
PlaceholderText="http://127.0.0.1:23330"
|
||||
Text="{x:Bind ViewModel.LXMusicServer, Mode=TwoWay}" />
|
||||
<Button
|
||||
x:Uid="SettingsPageServerTestButton"
|
||||
Command="{x:Bind ViewModel.LXMusicServerTestCommand}"
|
||||
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
|
||||
|
||||
</StackPanel>
|
||||
</controls:Case>
|
||||
|
||||
|
||||
@@ -7,14 +7,13 @@ using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using Windows.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Views
|
||||
{
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
private bool _isUserToggle;
|
||||
|
||||
public SettingsPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
---
|
||||
title: FAQ
|
||||
layout: FAQ
|
||||
---
|
||||
|
||||
### I couldn't see any button that I can interact with
|
||||
|
||||
This app is built with immersive experience, just hover your mouse on the top/bottom area of the app and then you'll see everything.
|
||||
@@ -11,17 +6,23 @@ This app is built with immersive experience, just hover your mouse on the top/bo
|
||||
|
||||

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

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

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

|
||||
|
||||
It's in the system tray, right-click on the icon and you'll see "Unlock the window".
|
||||
It's in the system tray, right-click on the icon and you'll see "Unlock the window". Or, alternatively, press `Ctrl + Alt + U`.
|
||||
|
||||
### There's a delay in lyrics timeline
|
||||
|
||||
@@ -41,4 +42,4 @@ Hover your mouse at the very bottom of the app and then click on the very last i
|
||||
|
||||
Go to "Advanced options" section, increase the threshold value (marked with the bigger red rectangle) until the lyrics is working properly.
|
||||
|
||||
----
|
||||
---
|
||||
|
Before Width: | Height: | Size: 510 KiB After Width: | Height: | Size: 510 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 631 KiB After Width: | Height: | Size: 631 KiB |
|
Before Width: | Height: | Size: 599 KiB After Width: | Height: | Size: 599 KiB |
|
Before Width: | Height: | Size: 859 KiB After Width: | Height: | Size: 859 KiB |
|
Before Width: | Height: | Size: 854 KiB After Width: | Height: | Size: 854 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
BIN
FAQ/image-8.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 445 KiB |
140
README.CN.md
@@ -1,7 +1,9 @@
|
||||
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.md">_**Click here to see the English version**_</a>
|
||||
|
||||
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**点此处查看常见问题与解答(FAQ)**_</a>
|
||||
|
||||
<div align="center">
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64"/>
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64"/>
|
||||
</div>
|
||||
|
||||
<h2 align="center">
|
||||
@@ -9,121 +11,141 @@ BetterLyrics
|
||||
</h2>
|
||||
|
||||
<h3 align="center">
|
||||
使用 WinUI 3 构建的流畅动态歌词显示工具
|
||||
基于 WinUI 3 打造的丝滑动态歌词显示工具
|
||||
</h3>
|
||||
|
||||
---
|
||||
|
||||
- QQ[「BetterLyrics」反馈交流群(简体中文)](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) (1054700388)
|
||||
- Discord [「BetterLyrics」反馈交流群(繁体中文/英文)](https://discord.gg/5yAQPnyCKv)
|
||||
- [「BetterLyrics」反馈交流群(简体中文)](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) (QQ群号:1054700388)
|
||||
- [「BetterLyrics」反馈交流群(繁体中文/英文)](https://discord.gg/5yAQPnyCKv) (Discord)
|
||||
|
||||
---
|
||||
|
||||
## 亮点功能
|
||||
🎉 本项目已被少数派精选推荐!欢迎阅读:[BetterLyrics - 一款专为 Windows 打造的沉浸式流畅歌词显示软件](https://sspai.com/post/101028)
|
||||
|
||||
- 动态模糊专辑封面作为背景
|
||||
- 流畅的歌词淡入/淡出、放大/缩小效果
|
||||
- 流畅的用户界面随歌曲切换
|
||||
- 每个字符均支持渐变卡拉 OK(带光晕)效果
|
||||
---
|
||||
|
||||
## 核心特色
|
||||
|
||||
- 动态模糊专辑封面背景
|
||||
- 丝滑的歌词淡入/淡出、缩放效果
|
||||
- 歌曲切换时的流畅界面过渡
|
||||
- 逐字渐变的卡拉OK效果(带光晕)
|
||||
- 沉浸式桌面歌词(停靠模式)
|
||||
- 本地翻译(支持 30 种语言)
|
||||
- 本地化歌词翻译(支持 30 种语言)
|
||||
|
||||
> 该项目目前仍在开发中,最新的开发分支中可能存在错误和意外行为。
|
||||
> 项目仍在开发中,最新分支可能存在未修复的 Bug 或异常行为
|
||||
|
||||
## 支持的歌词来源
|
||||
|
||||
- 来自您的本地存储
|
||||
- 本地资源
|
||||
- 音乐文件(内嵌歌词)
|
||||
- [.lrc](https://en.wikipedia.org/wiki/LRC_(file_format)) 文件(包含核心格式和增强格式)
|
||||
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) 文件(标准格式与增强格式)
|
||||
- [.eslrc](https://github.com/ESLyric/release) 文件
|
||||
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) 文件
|
||||
|
||||
(歌词下载,您可以使用 [LDDC](https://github.com/chenmozhijin/LDDC))
|
||||
(歌词下载推荐工具:[LDDC](https://github.com/chenmozhijin/LDDC))
|
||||
|
||||
- 来自在线歌词提供商
|
||||
- QQ 音乐
|
||||
- 在线歌词源
|
||||
- QQ音乐
|
||||
- 网易云音乐
|
||||
- 酷狗音乐
|
||||
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
|
||||
- [LRCLIB](https://lrclib.net/)
|
||||
|
||||
## 截图
|
||||
## 效果展示
|
||||
|
||||

|
||||
### 标准模式
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
### 停靠模式
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## 演示
|
||||
### 桌面模式
|
||||
|
||||
在 Bilibili 上观看我们的介绍视频(上传于 2025 年 5 月 31 日) [此处](https://b23.tv/QjKkYmL)。
|
||||

|
||||
|
||||

|
||||
|
||||
## 演示视频
|
||||
|
||||
观看 B 站演示视频(2025年7月7日发布)[点击此处](https://www.bilibili.com/video/BV1zjGjzfEXh)
|
||||
|
||||
## 立即体验
|
||||
|
||||
- 稳定版本
|
||||
- 稳定版
|
||||
|
||||
<a href="https://apps.microsoft.com/detail/9P1WCD1P597R?referrer=appbadge&mode=direct">
|
||||
<img src="https://get.microsoft.com/images/en-us%20dark.svg" width="200"/>
|
||||
<img src="https://get.microsoft.com/images/zh-cn%20dark.svg" width="200"/>
|
||||
</a>
|
||||
|
||||
> **最简单**的获取方式。 **无限**免费试用或购买(免费版和付费版**没有区别**,如果您喜欢,可以购买来支持我)
|
||||
> **最便捷**的获取方式,提供**无限制**免费试用或购买(免费版与付费版**功能完全一致**,购买视为对开发者的支持)
|
||||
|
||||
或者您也可以从 Google Drive 获取(链接见 [release](https://github.com/jayfunc/BetterLyrics/releases/latest) 页面)
|
||||
或通过 Google Drive 获取(链接见[发布页](https://github.com/jayfunc/BetterLyrics/releases))
|
||||
|
||||
> 请注意,您正在下载“.zip”文件,有关安装指南,请参考[此文档](How2Install/How2Install.md)。
|
||||
> 下载的是 ".zip" 压缩包,安装指南请参阅[此文档](How2Install/How2Install.md)
|
||||
|
||||
- 最新开发版本
|
||||
- 开发版
|
||||
可通过 `git clone` 拉取项目源码自行编译
|
||||
|
||||
您可以使用 `git clone` 命令克隆此项目并自行构建。
|
||||
## 已测试播放器
|
||||
|
||||
## 已知不支持的音乐播放器
|
||||
- 酷狗音乐
|
||||
- **时间轴同步限制**:酷狗不广播时间轴信息,调整进度时歌词无法实时跟随
|
||||
- Apple Music
|
||||
- 需在设置中调整时间轴阈值至 600 ms 左右(路径:设置→高级选项)
|
||||
- foobar2000
|
||||
- 需配合安装 [foo_mediacontrol](https://github.com/dumbie/foo_mediacontrol) 插件
|
||||
- Spotify
|
||||
- QQ音乐
|
||||
- PotPlayer
|
||||
- 系统自带媒体播放器
|
||||
|
||||
- 网易云音乐
|
||||
|
||||
## 非常感谢
|
||||
## 鸣谢
|
||||
|
||||
- [Lyricify-Lyrics-Helper](https://github.com/WXRIW/Lyricify-Lyrics-Helper)
|
||||
- 提供 QQ、网易、酷狗等平台歌词的获取、解密和解析功能
|
||||
- 提供 QQ/网易云/酷狗歌词获取与解密
|
||||
- [LRCLIB](https://lrclib.net/)
|
||||
- LRCLIB 歌词 API 提供程序
|
||||
- [Audio Tools Library (ATL) for .NET](https://github.com/Zeugma440/atldotnet)
|
||||
- 用于提取音乐文件中的图片
|
||||
- 歌词 API 服务
|
||||
- [ATL.NET](https://github.com/Zeugma440/atldotnet)
|
||||
- 音乐文件封面提取
|
||||
- [WinUIEx](https://github.com/dotMorten/WinUIEx)
|
||||
- 提供访问 Win32 窗口 API 的便捷方法
|
||||
- Win32 窗口 API 封装
|
||||
- [TagLib#](https://github.com/mono/taglib-sharp)
|
||||
- 用于读取原版歌词内容
|
||||
- [Stackoverflow - 如何在 WPF 中为 Margin 属性设置动画](https://stackoverflow.com/a/21542882/11048731)
|
||||
- 歌词内容解析
|
||||
- [Vanara](https://github.com/dahall/Vanara)
|
||||
- Win32 API 封装库
|
||||
- [Stackoverflow - WPF边距动画实现](https://stackoverflow.com/a/21542882/11048731)
|
||||
- [DevWinUI](https://github.com/ghost1372/DevWinUI)
|
||||
- [Bilibili -【WinUI3】SystemBackdropController:定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
|
||||
- [cnblogs - .NET App 与 Windows 系统媒体控制(SMTC)交互](https://www.cnblogs.com/TwilightLemon/p/18279496)
|
||||
- [Win2D中的游戏循环:CanvasAnimatedControl](https://www.cnblogs.com/walterlv/p/10236395.html)
|
||||
- [r2d2rigo/Win2D-Samples](https://github.com/r2d2rigo/Win2D-Samples/blob/master/IrisBlurWin2D/IrisBlurWin2D/MainPage.xaml.cs)
|
||||
- [CommunityToolkit - 从入门到精通](https://mvvm.coldwind.top/)
|
||||
- [B站 - WinUI3系统背景效果教程](https://www.bilibili.com/video/BV1PY4FevEkS)
|
||||
- [博客园 - .NET应用与SMTC交互](https://www.cnblogs.com/TwilightLemon/p/18279496)
|
||||
- [Win2D游戏循环实现](https://www.cnblogs.com/walterlv/p/10236395.html)
|
||||
- [Win2D高级示例](https://github.com/r2d2rigo/Win2D-Samples)
|
||||
- [CommunityToolkit开发指南](https://mvvm.coldwind.top/)
|
||||
|
||||
## 灵感来自
|
||||
## 灵感来源
|
||||
|
||||
- [refined-now-playing-netease](https://github.com/solstice23/refined-now-playing-netease)
|
||||
- [网易云歌词增强](https://github.com/solstice23/refined-now-playing-netease)
|
||||
- [Lyricify-App](https://github.com/WXRIW/Lyricify-App)
|
||||
- [椒盐音乐 Salt Player](https://moriafly.com/program/salt-player)
|
||||
- [MyToolBar](https://github.com/TwilightLemon/MyToolBar)
|
||||
- [椒盐音乐播放器](https://moriafly.com/program/salt-player)
|
||||
- [MyToolBar任务栏工具](https://github.com/TwilightLemon/MyToolBar)
|
||||
|
||||
## Star 历史
|
||||
## 项目星标历史
|
||||
|
||||
[](https://www.star-history.com/#jayfunc/BetterLyrics&Date)
|
||||
[](https://www.star-history.com/#jayfunc/BetterLyrics&Date)
|
||||
|
||||
## 欢迎提出任何问题和 PR
|
||||
## 欢迎反馈与贡献
|
||||
|
||||
如果您发现错误,请提交至 issues;如果您有任何想法,请随时在此处分享。
|
||||
如遇问题请提交 Issue,如有改进建议欢迎提交 PR
|
||||
55
README.md
@@ -1,5 +1,7 @@
|
||||
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.CN.md">_**点此处查看中文说明**_</a>
|
||||
|
||||
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**Click here to view frequently asked questions (FAQ)**_</a>
|
||||
|
||||
<div align="center">
|
||||
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64"/>
|
||||
</div>
|
||||
@@ -19,6 +21,10 @@ Your smooth dynamic lyrics display tool built with WinUI 3
|
||||
|
||||
---
|
||||
|
||||
🎉 This project was featured by SSPAI! Check out the article: [BetterLyrics – An immersive and smooth lyrics display tool designed for Windows](https://sspai.com/post/101028)
|
||||
|
||||
---
|
||||
|
||||
## Highlighted features
|
||||
|
||||
- Dynamic blur album art as background
|
||||
@@ -28,13 +34,13 @@ Your smooth dynamic lyrics display tool built with WinUI 3
|
||||
- Immersive desktop lyrics (dock mode)
|
||||
- Local translation (supporting 30 languages)
|
||||
|
||||
> This project is still under development now, bugs and unexpected behaviors may be existed in the latest dev branch.
|
||||
> This project is still under development, bugs and unexpected behaviors may be existed in the latest branch.
|
||||
|
||||
## Supported lyrics source
|
||||
|
||||
- From your local storage
|
||||
- Music files (with embedded lyrics)
|
||||
- [.lrc](https://en.wikipedia.org/wiki/LRC_(file_format)) files (with both core format and enhanced format)
|
||||
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) files (with both core format and enhanced format)
|
||||
- [.eslrc](https://github.com/ESLyric/release) files
|
||||
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) files
|
||||
|
||||
@@ -49,27 +55,33 @@ Your smooth dynamic lyrics display tool built with WinUI 3
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
### Standard mode
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
### Dock mode
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
### Desktop mode
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Demonstration
|
||||
|
||||
Watch our introduction video (uploaded on 31 May 2025) on Bilibili [here](https://b23.tv/QjKkYmL).
|
||||
Watch our introduction video (uploaded on 7 July 2025) on Bilibili [here](https://www.bilibili.com/video/BV1zjGjzfEXh).
|
||||
|
||||
## Try it now
|
||||
|
||||
@@ -81,7 +93,7 @@ Watch our introduction video (uploaded on 31 May 2025) on Bilibili [here](https:
|
||||
|
||||
> **Easiest** way to get it. **Unlimited** free trail or purchase (there is **no difference** between free and paid version, if you like you can purchase to support me)
|
||||
|
||||
Or alternatively get it from Google Drive (see [release](https://github.com/jayfunc/BetterLyrics/releases/latest) page for the link)
|
||||
Or alternatively get it from Google Drive (see [release](https://github.com/jayfunc/BetterLyrics/releases) page for the link)
|
||||
|
||||
> Please note you are downloading ".zip" file, for guide on how to install it, please kindly follow [this doc](How2Install/How2Install.md).
|
||||
|
||||
@@ -89,9 +101,18 @@ Or alternatively get it from Google Drive (see [release](https://github.com/jayf
|
||||
|
||||
You can `git clone` this project and build it yourself.
|
||||
|
||||
## Known unsupported music player
|
||||
## Tested music player
|
||||
|
||||
- 网易云音乐 NetEase Cloud Music
|
||||
- Kugou Music
|
||||
- No timeline information broadcasted, which means when you change timeline position in Kugou Music, BetterLyrics has no way to detect this change.
|
||||
- Apple Music
|
||||
- Make sure you have set timeline threshold to around 600 ms in settings (Go to "Settings" - "Advanced option" to change), otherwise, the lyrics will be moving forward and afterward constantly.
|
||||
- foobar2000
|
||||
- Make sure you have https://github.com/dumbie/foo_mediacontrol installed with it
|
||||
- Spofity
|
||||
- QQ Music
|
||||
- PotPlayer
|
||||
- Media Player (System)
|
||||
|
||||
## Many thanks to
|
||||
|
||||
@@ -105,6 +126,8 @@ You can `git clone` this project and build it yourself.
|
||||
- Provide easy ways to access Win32 API regarding windowing
|
||||
- [TagLib#](https://github.com/mono/taglib-sharp)
|
||||
- Used for reading original lyrics content
|
||||
- [Vanara](https://github.com/dahall/Vanara)
|
||||
- Win32 API wrapper
|
||||
- [Stackoverflow - How to animate Margin property in WPF](https://stackoverflow.com/a/21542882/11048731)
|
||||
- [DevWinUI](https://github.com/ghost1372/DevWinUI)
|
||||
- [Bilibili -【WinUI3】SystemBackdropController:定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
|
||||
@@ -126,4 +149,4 @@ You can `git clone` this project and build it yourself.
|
||||
|
||||
## Any issues and PRs are welcomed
|
||||
|
||||
If you find a bug please file it in issues or if you have any ideas feel free to share it here.
|
||||
If you find a bug please file it in issues or if you have any ideas feel free to share it here.
|
||||
|
||||
BIN
Screenshots/album-art-only.png
Normal file
|
After Width: | Height: | Size: 560 KiB |
BIN
Screenshots/desktop-1.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
Screenshots/desktop-2.png
Normal file
|
After Width: | Height: | Size: 764 KiB |
BIN
Screenshots/dock-1.png
Normal file
|
After Width: | Height: | Size: 757 KiB |
BIN
Screenshots/dock-2.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
Screenshots/fan.png
Normal file
|
After Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 544 KiB |
BIN
Screenshots/glow-float.gif
Normal file
|
After Width: | Height: | Size: 7.6 MiB |
|
Before Width: | Height: | Size: 51 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
Screenshots/image.png
Normal file
|
After Width: | Height: | Size: 775 KiB |
|
Before Width: | Height: | Size: 6.2 MiB |
BIN
Screenshots/lyrics-only.png
Normal file
|
After Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 947 KiB |
@@ -1,7 +0,0 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "jekyll", "~> 4.4.1" # installed by `gem jekyll`
|
||||
# gem "webrick" # required when using Ruby >= 3 and Jekyll <= 4.2.2
|
||||
|
||||
gem "just-the-docs", "0.10.1" # pinned to the current release
|
||||
# gem "just-the-docs" # always download the latest release
|
||||
@@ -1,97 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
base64 (0.2.0)
|
||||
bigdecimal (3.1.9)
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
csv (3.3.2)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0)
|
||||
eventmachine (1.2.7)
|
||||
ffi (1.17.1-arm64-darwin)
|
||||
ffi (1.17.1-x86_64-linux-gnu)
|
||||
forwardable-extended (2.6.0)
|
||||
google-protobuf (4.29.3-arm64-darwin)
|
||||
bigdecimal
|
||||
rake (>= 13)
|
||||
google-protobuf (4.29.3-x86_64-linux)
|
||||
bigdecimal
|
||||
rake (>= 13)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (4.4.1)
|
||||
addressable (~> 2.4)
|
||||
base64 (~> 0.2)
|
||||
colorator (~> 1.0)
|
||||
csv (~> 3.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 1.0)
|
||||
jekyll-sass-converter (>= 2.0, < 4.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
json (~> 2.6)
|
||||
kramdown (~> 2.3, >= 2.3.1)
|
||||
kramdown-parser-gfm (~> 1.0)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3, >= 0.3.6)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 3.0, < 5.0)
|
||||
safe_yaml (~> 1.0)
|
||||
terminal-table (>= 1.8, < 4.0)
|
||||
webrick (~> 1.7)
|
||||
jekyll-include-cache (0.2.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-sass-converter (3.0.0)
|
||||
sass-embedded (~> 1.54)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
json (2.9.1)
|
||||
just-the-docs (0.10.1)
|
||||
jekyll (>= 3.8.5)
|
||||
jekyll-include-cache
|
||||
jekyll-seo-tag (>= 2.0)
|
||||
rake (>= 12.3.1)
|
||||
kramdown (2.5.1)
|
||||
rexml (>= 3.3.9)
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.4)
|
||||
listen (3.9.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.4.0)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (6.0.1)
|
||||
rake (13.2.1)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.11.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.4.0)
|
||||
rouge (4.5.1)
|
||||
safe_yaml (1.0.5)
|
||||
sass-embedded (1.83.4-arm64-darwin)
|
||||
google-protobuf (~> 4.29)
|
||||
sass-embedded (1.83.4-x86_64-linux-gnu)
|
||||
google-protobuf (~> 4.29)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
unicode-display_width (2.6.0)
|
||||
webrick (1.9.1)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin
|
||||
x86_64-linux-gnu
|
||||
|
||||
DEPENDENCIES
|
||||
jekyll (~> 4.4.1)
|
||||
just-the-docs (= 0.10.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.9
|
||||
@@ -1,8 +0,0 @@
|
||||
title: BetterLyrics Docs
|
||||
description: BetterLyrics Docs
|
||||
theme: just-the-docs
|
||||
|
||||
url: https://jayfunc.github.io/BetterLyrics
|
||||
|
||||
aux_links:
|
||||
Template Repository: https://github.com/just-the-docs/just-the-docs-template
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
title: Home
|
||||
layout: home
|
||||
---
|
||||
|
||||
----
|
||||
|
||||
[^1]: [It can take up to 10 minutes for changes to your site to publish after you push the changes to GitHub](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/creating-a-github-pages-site-with-jekyll#creating-your-site).
|
||||
|
||||
[Just the Docs]: https://just-the-docs.github.io/just-the-docs/
|
||||
[GitHub Pages]: https://docs.github.com/en/pages
|
||||
[README]: https://github.com/just-the-docs/just-the-docs-template/blob/main/README.md
|
||||
[Jekyll]: https://jekyllrb.com
|
||||
[GitHub Pages / Actions workflow]: https://github.blog/changelog/2022-07-27-github-pages-custom-github-actions-workflows-beta/
|
||||
[use this template]: https://github.com/just-the-docs/just-the-docs-template/generate
|
||||