add local machine translation func

This commit is contained in:
Zhe Fang
2025-07-05 15:16:34 -04:00
parent 352ceca81d
commit 78a6ba8e1f
31 changed files with 778 additions and 399 deletions

View File

@@ -63,14 +63,16 @@
</Style.Setters>
</Style>
<Style x:Key="TitleBarButtonStyle" TargetType="Button">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="CornerRadius" Value="0" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="16,0" />
<Setter Property="Padding" Value="16,9,16,11" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="Transparent" />
</Style>
<Style x:Key="GhostButtonStyle" TargetType="Button">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Padding" Value="8" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
@@ -86,6 +88,7 @@
<Setter Property="CornerRadius" Value="4" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="8" />
<Setter Property="Background" Value="Transparent" />
</Style>

View File

@@ -23,6 +23,7 @@ namespace BetterLyrics.WinUI3
{
private readonly ILogger<App> _logger;
private readonly ISettingsService _settingsService;
public static new App Current => (App)Application.Current;
public static DispatcherQueue? DispatcherQueue { get; private set; }
@@ -41,7 +42,8 @@ namespace BetterLyrics.WinUI3
AppInfo.EnsureDirectories();
ConfigureServices();
_logger = Ioc.Default.GetService<ILogger<App>>()!;
_logger = Ioc.Default.GetRequiredService<ILogger<App>>();
_settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
UnhandledException += App_UnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
@@ -53,6 +55,7 @@ namespace BetterLyrics.WinUI3
{
WindowHelper.OpenOrShowWindow<LyricsWindow>();
var lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (lyricsWindow == null) return;
string[] commandLineArguments = Environment.GetCommandLineArgs();
if (commandLineArguments.Length > 1)
@@ -66,10 +69,11 @@ namespace BetterLyrics.WinUI3
}
lyricsWindow.AutoSelectLyricsMode();
}
private static void ConfigureServices()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Verbose)
.WriteTo.File(AppInfo.LogFilePattern, rollingInterval: RollingInterval.Day)
.CreateLogger();

View File

@@ -41,6 +41,7 @@
<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="Fluent.LibreTranslate" Version="1.0.6" />
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.0" />
<PackageReference Include="iTunesSearch" Version="1.0.44" />
<PackageReference Include="Lyricify.Lyrics.Helper-NativeAot" Version="0.1.4-alpha.5" />
@@ -49,7 +50,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="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />

View File

@@ -16,13 +16,33 @@ namespace BetterLyrics.WinUI3.Enums
{
public static LyricsFormat? DetectFormat(this string content)
{
if (string.IsNullOrWhiteSpace(content))
return null;
// TTML
if (content.StartsWith("<?xml") && System.Text.RegularExpressions.Regex.IsMatch(content, @"<tt(:\w+)?\b"))
{
return LyricsFormat.Ttml;
}
// 检测标准LRC和增强型LRC
else if (System.Text.RegularExpressions.Regex.IsMatch(content, @"\[\d{1,2}:\d{2}")
|| System.Text.RegularExpressions.Regex.IsMatch(content, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
// KRC: 检测主内容格式 [start,duration]<offset,duration,0>字...
else if (System.Text.RegularExpressions.Regex.IsMatch(
content,
@"^\[\d+,\d+\](<\d+,\d+,0>.+)+",
System.Text.RegularExpressions.RegexOptions.Multiline))
{
return LyricsFormat.Krc;
}
// QRC: 检测主内容格式 [start,duration]字(offset,duration)
else if (System.Text.RegularExpressions.Regex.IsMatch(
content,
@"^\[\d+,\d+\].*?\(\d+,\d+\)",
System.Text.RegularExpressions.RegexOptions.Multiline))
{
return LyricsFormat.Qrc;
}
// 标准LRC和增强型LRC
else if (System.Text.RegularExpressions.Regex.IsMatch(content, @"\[\d{1,2}:\d{2}") ||
System.Text.RegularExpressions.Regex.IsMatch(content, @"<\d{1,2}:\d{2}\.\d{2,3}>"))
{
return LyricsFormat.Lrc;
}

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using Fluent.LibreTranslate;
using Lyricify.Lyrics.Models;
using System;
using System.Collections.Generic;
@@ -16,7 +17,7 @@ namespace BetterLyrics.WinUI3.Helper
{
private List<List<LyricsLine>> _multiLangLyricsLines = [];
public List<List<LyricsLine>> Parse(string? raw, LyricsFormat? lyricsFormat = null, string? title = null, string? artist = null, int durationMs = 0)
public List<List<LyricsLine>> Parse(string? raw, int durationMs)
{
_multiLangLyricsLines = [];
if (raw == null)
@@ -27,7 +28,7 @@ namespace BetterLyrics.WinUI3.Helper
{
StartMs = 0,
EndMs = durationMs,
Text = App.ResourceLoader!.GetString("LyricsNotFound"),
OriginalText = App.ResourceLoader!.GetString("LyricsNotFound"),
CharTimings = [],
},
]
@@ -35,7 +36,7 @@ namespace BetterLyrics.WinUI3.Helper
}
else
{
switch (lyricsFormat)
switch (raw.DetectFormat())
{
case LyricsFormat.Lrc:
case LyricsFormat.Eslrc:
@@ -136,7 +137,7 @@ namespace BetterLyrics.WinUI3.Helper
{
StartMs = start,
EndMs = 0, // 稍后统一修正
Text = text,
OriginalText = text,
CharTimings = [],
};
if (syllables != null && syllables.Count > 0)
@@ -217,7 +218,7 @@ namespace BetterLyrics.WinUI3.Helper
{
StartMs = pStartMs,
EndMs = 0,
Text = text,
OriginalText = text,
CharTimings = charTimings,
}
);
@@ -306,7 +307,7 @@ namespace BetterLyrics.WinUI3.Helper
{
StartMs = lineRead.StartTime ?? 0,
EndMs = 0,
Text = lineRead.Text,
OriginalText = lineRead.Text,
CharTimings = [],
};
@@ -389,7 +390,7 @@ namespace BetterLyrics.WinUI3.Helper
{
StartMs = 0,
EndMs = linesInSingleLang[0].StartMs,
Text = "● ● ●",
OriginalText = "● ● ●",
CharTimings = [],
}
);

View File

@@ -36,7 +36,7 @@ namespace BetterLyrics.WinUI3.Helper
App.Current.Exit();
}
public static T GetWindowByWindowType<T>()
public static T? GetWindowByWindowType<T>()
{
foreach (var window in _activeWindows)
{
@@ -45,7 +45,7 @@ namespace BetterLyrics.WinUI3.Helper
return castedWindow;
}
}
throw new InvalidOperationException($"No window of type {typeof(T).Name} found.");
return default;
}
public static void OpenOrShowWindow<T>()
{

View File

@@ -1,15 +0,0 @@
// 2025/6/23 by Zhe Fang
using System.Collections.Generic;
namespace BetterLyrics.WinUI3.Models
{
public class LyricsData
{
public int LanguageIndex { get; set; } = 0;
public List<LyricsLine> LyricsLines => MultiLangLyricsLines[LanguageIndex];
public List<List<LyricsLine>> MultiLangLyricsLines { get; set; } = [];
}
}

View File

@@ -24,11 +24,10 @@ namespace BetterLyrics.WinUI3.Models
public List<CharTiming> CharTimings { get; set; } = [];
public int DurationMs => EndMs - StartMs;
public int EndMs { get; set; }
public int StartMs { get; set; }
public string Text { get; set; } = "";
public string DisplayedText { get; set; } = "";
public string OriginalText { get; set; } = "";
}
}

View File

@@ -1,6 +1,7 @@
// 2025/6/23 by Zhe Fang
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BetterLyrics.WinUI3.Enums;
@@ -10,11 +11,12 @@ namespace BetterLyrics.WinUI3.Services
{
Task <byte[]> SearchAlbumArtAsync(string title, string artist, string album);
Task<(string?, LyricsFormat?)> SearchLyricsAsync(
Task<string?> SearchLyricsAsync(
string title,
string artist,
string album = "",
double durationMs = 0.0
string album,
double durationMs,
CancellationToken token
);
}
}

View File

@@ -29,6 +29,12 @@ namespace BetterLyrics.WinUI3.Services
int DesktopWindowTop { get; set; }
int DesktopWindowWidth { get; set; }
int DesktopWindowHeight { get; set; }
int StandardWindowWidth { get; set; }
int StandardWindowHeight { get; set; }
int StandardWindowLeft { get; set; }
int StandardWindowTop { get; set; }
bool AutoLockOnDesktopMode { get; set; }
// Lyrics lib

View File

@@ -3,15 +3,18 @@
using ATL;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using CommunityToolkit.Mvvm.DependencyInjection;
using iTunesSearch.Library;
using Lyricify.Lyrics.Providers.Web.Kugou;
using Lyricify.Lyrics.Searchers;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services
@@ -27,10 +30,13 @@ namespace BetterLyrics.WinUI3.Services
private readonly iTunesSearchManager _iTunesSearchManager;
private readonly ISettingsService _settingsService;
private readonly ILogger _logger;
public MusicSearchService(ISettingsService settingsService)
{
_settingsService = settingsService;
_logger = Ioc.Default.GetRequiredService<ILogger<MusicSearchService>>();
_lrcLibHttpClient = new();
_lrcLibHttpClient.DefaultRequestHeaders.Add(
"User-Agent",
@@ -102,21 +108,21 @@ namespace BetterLyrics.WinUI3.Services
}
}
//var resultItems = await _iTunesSearchManager.GetAlbumsAsync(album, 1, countryCode: GuessCountryCode(album, artist));
//var url = resultItems.Albums.Where(al => Normalize(al.ArtistName).Contains(Normalize(artist)))
// .FirstOrDefault()?.ArtworkUrl100.Replace("100x100bb.jpg", "100000x100000-999.jpg");
//if (url != null)
//{
// return await _iTunesHttpClinet.GetByteArrayAsync(url);
//}
var resultItems = await _iTunesSearchManager.GetAlbumsAsync(album, 1, countryCode: GuessCountryCode(album, artist));
var url = resultItems.Albums.Where(al => Normalize(al.ArtistName).Contains(Normalize(artist)))
.FirstOrDefault()?.ArtworkUrl100.Replace("100x100bb.jpg", "100000x100000-999.jpg");
if (url != null)
{
return await _iTunesHttpClinet.GetByteArrayAsync(url);
}
return await ImageHelper.CreateTextPlaceholderBytesAsync($"{artist} - {title}", 400, 400);
}
public async Task<(string?, LyricsFormat?)> SearchLyricsAsync(
string title, string artist, string album = "", double durationMs = 0.0
)
public async Task<string?> SearchLyricsAsync(string title, string artist, string album, double durationMs, CancellationToken token)
{
_logger.LogInformation("Searching lyrics for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
{
if (!provider.IsEnabled)
@@ -133,7 +139,7 @@ namespace BetterLyrics.WinUI3.Services
cachedLyrics = ReadCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
{
return (cachedLyrics, lyricsFormat);
return cachedLyrics;
}
}
@@ -174,6 +180,8 @@ namespace BetterLyrics.WinUI3.Services
}
}
token.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(searchedLyrics))
{
if (provider.Provider.IsRemote())
@@ -181,11 +189,11 @@ namespace BetterLyrics.WinUI3.Services
WriteCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
}
return (searchedLyrics, lyricsFormat == LyricsFormat.NotSpecified ? searchedLyrics.DetectFormat() : lyricsFormat);
return searchedLyrics;
}
}
return (null, null);
return null;
}
private static bool MusicMatch(string fileName, string title, string artist)
@@ -394,7 +402,7 @@ namespace BetterLyrics.WinUI3.Services
Album = album,
Artists = [artist],
Title = title,
} //, Lyricify.Lyrics.Searchers.Helpers.CompareHelper.MatchType.Perfect
}
);
if (result is QQMusicSearchResult qqResult)
@@ -405,12 +413,12 @@ namespace BetterLyrics.WinUI3.Services
}
else if (result is NeteaseSearchResult neteaseResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyricNew(neteaseResult.Id);
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.NeteaseApi.GetLyric(neteaseResult.Id);
return response?.Lrc.Lyric;
}
else if (result is KugouSearchResult kugouResult)
{
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(kugouResult.Hash);
var response = await Lyricify.Lyrics.Helpers.ProviderHelper.KugouApi.GetSearchLyrics(hash: kugouResult.Hash);
if (response?.Candidates.FirstOrDefault() is SearchLyricsResponse.Candidate candidate)
{
return Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyrics(

View File

@@ -4,27 +4,27 @@ using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Media.Control;
using Windows.Storage.Streams;
using WindowsMediaController;
using static Lyricify.Lyrics.Providers.Web.Musixmatch.GetTokenResponse;
namespace BetterLyrics.WinUI3.Services
{
public partial class PlaybackService : BaseViewModel, IPlaybackService, IRecipient<PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>>>
{
private readonly IMusicSearchService _musicSearchService;
private readonly ILogger<PlaybackService> _logger;
private readonly MediaManager _mediaManager = new();
@@ -40,6 +40,8 @@ namespace BetterLyrics.WinUI3.Services
public PlaybackService(ISettingsService settingsService, IMusicSearchService musicSearchService) : base(settingsService)
{
_musicSearchService = musicSearchService;
_logger = Ioc.Default.GetRequiredService<ILogger<PlaybackService>>();
_mediaSourceProvidersInfo = _settingsService.MediaSourceProvidersInfo;
InitMediaManager();
}
@@ -91,6 +93,7 @@ namespace BetterLyrics.WinUI3.Services
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
{
RecordMediaSourceProviderInfo(mediaSession);
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
@@ -107,6 +110,10 @@ namespace BetterLyrics.WinUI3.Services
private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
{
_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;
@@ -177,7 +184,14 @@ namespace BetterLyrics.WinUI3.Services
private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession)
{
string id = mediaSession.ControlSession.SourceAppUserModelId;
RecordMediaSourceProviderInfo(mediaSession);
}
private void RecordMediaSourceProviderInfo(MediaManager.MediaSession mediaSession)
{
var id = mediaSession?.ControlSession?.SourceAppUserModelId;
if (string.IsNullOrEmpty(id)) return;
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
if (found == null)
{

View File

@@ -32,6 +32,12 @@ namespace BetterLyrics.WinUI3.Services
private const string DesktopWindowTopKey = "DesktopWindowTop";
private const string DesktopWindowWidthKey = "DesktopWindowWidth";
private const string DesktopWindowHeightKey = "DesktopWindowHeight";
private const string StandardWindowLeftKey = "StandardWindowLeft";
private const string StandardWindowTopKey = "StandardWindowTop";
private const string StandardWindowWidthKey = "StandardWindowWidth";
private const string StandardWindowHeightKey = "StandardWindowHeight";
private const string AutoLockOnDesktopModeKey = "AutoLockOnDesktopMode";
private const string IsDynamicCoverOverlayEnabledKey = "IsDynamicCoverOverlayEnabled";
@@ -87,10 +93,17 @@ namespace BetterLyrics.WinUI3.Services
}
// App appearance
SetDefault(LanguageKey, (int)Language.FollowSystem);
SetDefault(DesktopWindowHeightKey, 400);
SetDefault(DesktopWindowLeftKey, 0);
SetDefault(DesktopWindowTopKey, 0);
SetDefault(DesktopWindowWidthKey, 600);
SetDefault(DesktopWindowHeightKey, 500);
SetDefault(DesktopWindowLeftKey, 200);
SetDefault(DesktopWindowTopKey, 200);
SetDefault(DesktopWindowWidthKey, 800);
SetDefault(StandardWindowHeightKey, 800);
SetDefault(StandardWindowLeftKey, 200);
SetDefault(StandardWindowTopKey, 200);
SetDefault(StandardWindowWidthKey, 1000);
SetDefault(AutoLockOnDesktopModeKey, false);
// App behavior
SetDefault(AutoStartWindowTypeKey, (int)AutoStartWindowType.StandardMode);
@@ -144,6 +157,31 @@ namespace BetterLyrics.WinUI3.Services
get => GetValue<int>(DesktopWindowHeightKey);
set => SetValue(DesktopWindowHeightKey, value);
}
public int StandardWindowLeft
{
get => GetValue<int>(StandardWindowLeftKey);
set => SetValue(StandardWindowLeftKey, value);
}
public int StandardWindowTop
{
get => GetValue<int>(StandardWindowTopKey);
set => SetValue(StandardWindowTopKey, value);
}
public int StandardWindowWidth
{
get => GetValue<int>(StandardWindowWidthKey);
set => SetValue(StandardWindowWidthKey, value);
}
public int StandardWindowHeight
{
get => GetValue<int>(StandardWindowHeightKey);
set => SetValue(StandardWindowHeightKey, value);
}
public bool AutoLockOnDesktopMode
{
get => GetValue<bool>(AutoLockOnDesktopModeKey);

View File

@@ -228,27 +228,15 @@
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>Alignment</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>Alignment</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>Center</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>Center</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>Left</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>Left</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>Right</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>Right</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>Lyrics background blur amount</value>
</data>
@@ -552,6 +540,18 @@
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>Auto-lock when activating desktop mode</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>Alignment</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>Center</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>Left</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>Right</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>Album art</value>
</data>
@@ -570,4 +570,13 @@
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>Enable or disable lyrics display for a specified media source</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>Log record</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>Lyrics translation</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>Lyrics timeline offset (ms)</value>
</data>
</root>

View File

@@ -228,27 +228,15 @@
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>アライメント</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>アライメント</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>中心</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>中心</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>左</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>左</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>右</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>右</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>歌詞の背景ぼやけ</value>
</data>
@@ -552,6 +540,18 @@
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>デスクトップモードをアクティブにするときの自動ロック</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>アライメント</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>中心</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>左</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>右</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>アルバムアート</value>
</data>
@@ -570,4 +570,13 @@
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>指定されたメディアソースの歌詞ディスプレイを有効または無効にする</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>ログレコード</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>歌詞翻訳</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>歌詞タイムラインオフセット(ミリ秒)</value>
</data>
</root>

View File

@@ -228,27 +228,15 @@
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>조정</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>조정</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>센터</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>센터</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>왼쪽</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>왼쪽</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>오른쪽</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>오른쪽</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>가사 배경 블러</value>
</data>
@@ -552,6 +540,18 @@
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>데스크탑 모드를 활성화 할 때 자동 잠금</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>조정</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>센터</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>왼쪽</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>오른쪽</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>앨범 아트</value>
</data>
@@ -570,4 +570,13 @@
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>지정된 미디어 소스의 가사 디스플레이 활성화 또는 비활성화</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>로그 레코드</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>가사 번역</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>가사 타임 라인 오프셋 (밀리초)</value>
</data>
</root>

View File

@@ -228,27 +228,15 @@
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>对齐方式</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>对齐方式</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>歌词背景模糊度</value>
</data>
@@ -552,6 +540,18 @@
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>启动桌面模式时随即锁定窗口</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>对齐方式</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>专辑</value>
</data>
@@ -570,4 +570,13 @@
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>为指定媒体源启用或禁用歌词显示</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>日志记录</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>歌词翻译</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>歌词时间轴偏移(毫秒)</value>
</data>
</root>

View File

@@ -228,27 +228,15 @@
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>對齊方式</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>對齊方式</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>歌詞背景模糊度</value>
</data>
@@ -552,6 +540,18 @@
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>啟動桌面模式時隨即鎖定窗口</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>對齊方式</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>居中</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>靠左</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>靠右</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>專輯</value>
</data>
@@ -570,4 +570,13 @@
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>為指定媒體源啟用或禁用歌詞顯示</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>日誌記錄</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>歌詞翻譯</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>歌詞時間軸偏移(毫秒)</value>
</data>
</root>

View File

@@ -9,7 +9,9 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
using System.Diagnostics;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
@@ -33,6 +35,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
SongInfo = e.SongInfo;
PositionOffset = 0; // Reset position offset when song changes
TrySwitchToPreferredDisplayType(e.SongInfo);
}
@@ -44,10 +47,10 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial bool IsFirstRun { get; set; }
[ObservableProperty]
public partial bool IsNotDockMode { get; set; } = true;
public partial bool IsWelcomeTeachingTipOpen { get; set; }
[ObservableProperty]
public partial bool IsWelcomeTeachingTipOpen { get; set; }
public partial Visibility BottomCommandGridVisibility { get; set; } = Visibility.Visible;
[ObservableProperty]
public partial int LyricsFontSize { get; set; }
@@ -58,13 +61,20 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial SongInfo? SongInfo { get; set; } = null;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial int PositionOffset { get; set; } = 0;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsTranslationEnabled { get; set; } = false;
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is LyricsWindowViewModel)
{
if (message.PropertyName == nameof(LyricsWindowViewModel.IsDockMode))
{
IsNotDockMode = !message.NewValue;
SetNonStandardModePreferredDisplayType(message.NewValue);
TrySwitchToPreferredDisplayType(SongInfo);
}

View File

@@ -77,7 +77,7 @@ namespace BetterLyrics.WinUI3.ViewModels
$"[DEBUG]\n" +
$"Cur playing {currentPlayingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" +
$"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
$"Cur time {TotalTime}\n" +
$"Cur time {_totalTime + _positionOffset}\n" +
$"Lang size {_multiLangLyrics.Count}\n" +
$"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}",
new Vector2(10, 10),
@@ -132,8 +132,6 @@ namespace BetterLyrics.WinUI3.ViewModels
float scaleFactor = _albumArtSize / Math.Min(imageWidth, imageHeight);
if (scaleFactor < 0.1f) return;
_albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16;
float cornerRadius = _albumArtCornerRadius / 100f * _albumArtSize / 2;
using var cornerRadiusMask = new CanvasCommandList(control.Device);
@@ -233,8 +231,6 @@ namespace BetterLyrics.WinUI3.ViewModels
private void DrawSingleTitleAndArtist(ICanvasAnimatedControl control, CanvasDrawingSession ds, string? title, string? artist, float opacity)
{
float titleY = _albumArtY + _albumArtSize + 12;
CanvasTextLayout titleLayout = new(
control, title ?? string.Empty,
_titleTextFormat, _albumArtSize, _canvasHeight
@@ -245,11 +241,11 @@ namespace BetterLyrics.WinUI3.ViewModels
);
ds.DrawTextLayout(
titleLayout,
new Vector2(_albumArtXTransition.Value, titleY),
new Vector2(_albumArtXTransition.Value, _titleY),
_fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 255 * opacity)));
ds.DrawTextLayout(
artistLayout,
new Vector2(_albumArtXTransition.Value, titleY + (float)titleLayout.LayoutBounds.Height),
new Vector2(_albumArtXTransition.Value, _titleY + (float)titleLayout.LayoutBounds.Height),
_fontColor.WithAlpha((byte)(_albumArtOpacityTransition.Value * 128 * opacity)));
}
@@ -420,12 +416,19 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else
{
float height = 0f;
var regions = textLayout.GetCharacterRegions(0, string.Join("", line.CharTimings.Select(x => x.Text)).Length);
if (regions.Length > 0)
{
height = (float)regions[^1].LayoutBounds.Bottom - (float)regions[0].LayoutBounds.Top;
}
maskDs.FillRectangle(
new Rect(
textLayout.LayoutBounds.X,
position.Y,
textLayout.LayoutBounds.Width,
textLayout.LayoutBounds.Height
height
),
Colors.White
);

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Models;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System;
using System.Collections.ObjectModel;
using Windows.UI;
@@ -20,26 +21,26 @@ namespace BetterLyrics.WinUI3.ViewModels
IRecipient<PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>>>,
IRecipient<PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>>>
{
public async void Receive(PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message)
public void Receive(PropertyChangedMessage<ObservableCollection<LocalLyricsFolder>> message)
{
if (message.Sender is SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.LocalLyricsFolders))
{
// Music lib changed, re-fetch lyrics
await RefreshLyricsAsync();
RefreshLyricsAsync();
}
}
}
public async void Receive(PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message)
public void Receive(PropertyChangedMessage<ObservableCollection<LyricsSearchProviderInfo>> message)
{
if (message.Sender is SettingsPageViewModel)
{
if (message.PropertyName == nameof(SettingsPageViewModel.LyricsSearchProvidersInfo))
{
// Lyrics search providers info changed, re-fetch lyrics
await RefreshLyricsAsync();
RefreshLyricsAsync();
}
}
}
@@ -78,6 +79,14 @@ namespace BetterLyrics.WinUI3.ViewModels
_isDesktopMode = message.NewValue;
}
}
else if (message.Sender is LyricsPageViewModel)
{
if (message.PropertyName == nameof(LyricsPageViewModel.IsTranslationEnabled))
{
_isTranslationEnabled = message.NewValue;
UpdateTranslationsAsync();
}
}
}
public void Receive(PropertyChangedMessage<Color> message)
@@ -142,6 +151,13 @@ namespace BetterLyrics.WinUI3.ViewModels
LyricsFontSize = message.NewValue;
}
}
else if (message.Sender is LyricsPageViewModel)
{
if (message.PropertyName == nameof(LyricsPageViewModel.PositionOffset))
{
_positionOffset = TimeSpan.FromMilliseconds(message.NewValue);
}
}
}
public void Receive(PropertyChangedMessage<LineRenderingType> message)

View File

@@ -21,11 +21,14 @@ namespace BetterLyrics.WinUI3.ViewModels
_canvasWidth = (float)control.Size.Width;
_canvasHeight = (float)control.Size.Height;
_albumArtY = 36 + (_canvasHeight - 36 * 2) * 3 / 16f;
_displayType = _displayTypeReceived;
if (_isPlaying)
{
TotalTime += args.Timing.ElapsedTime;
_totalTime += args.Timing.ElapsedTime;
}
ElapsedTime = args.Timing.ElapsedTime;
@@ -45,6 +48,8 @@ namespace BetterLyrics.WinUI3.ViewModels
(_canvasWidth - _leftMargin - _middleMargin - _rightMargin) / 2);
_albumArtSize = MathF.Max(0, _albumArtSize);
_titleY = _albumArtY + _albumArtSize * 1.05f;
if (isDisplayTypeChanged || isCanvasWidthChanged)
{
bool jumpTo = !isDisplayTypeChanged && isCanvasWidthChanged;
@@ -130,7 +135,7 @@ namespace BetterLyrics.WinUI3.ViewModels
// Calculate layout bounds
line.CanvasTextLayout = new CanvasTextLayout(
control,
line.Text,
line.DisplayedText,
_lyricsTextFormat,
_maxLyricsWidth,
_canvasHeight

View File

@@ -1,22 +1,27 @@
// 2025/6/23 by Zhe Fang
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using ABI.Microsoft.UI.Xaml;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using Fluent.LibreTranslate;
using Microsoft.Extensions.Logging;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Text;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.UI;
@@ -24,6 +29,9 @@ namespace BetterLyrics.WinUI3.ViewModels
{
public partial class LyricsRendererViewModel : BaseViewModel
{
private TimeSpan _totalTime = TimeSpan.Zero;
private TimeSpan _positionOffset = TimeSpan.Zero;
private SoftwareBitmap? _lastAlbumArtSwBitmap = null;
private SoftwareBitmap? _albumArtSwBitmap = null;
@@ -35,6 +43,8 @@ namespace BetterLyrics.WinUI3.ViewModels
private string? _lastSongTitle;
private string? _songTitle;
private float _titleY = 0f;
private string? _lastSongArtist;
private string? _songArtist;
@@ -57,6 +67,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private readonly IMusicSearchService _musicSearchService;
private readonly ILibWatcherService _libWatcherService;
private readonly IPlaybackService _playbackService;
private readonly ILogger _logger;
private readonly float _leftMargin = 36f;
private readonly float _middleMargin = 36f;
@@ -87,6 +98,8 @@ namespace BetterLyrics.WinUI3.ViewModels
private int _langIndex = 0;
private List<List<LyricsLine>> _multiLangLyrics = [];
private List<string> _translations = [];
private bool _isTranslationEnabled = false;
private CanvasTextFormat _lyricsTextFormat = new()
{
@@ -98,19 +111,26 @@ namespace BetterLyrics.WinUI3.ViewModels
FontSize = 18,
FontWeight = FontWeights.Bold,
HorizontalAlignment = CanvasHorizontalAlignment.Left,
WordWrapping = CanvasWordWrapping.Wrap,
WordWrapping = CanvasWordWrapping.NoWrap,
TrimmingSign = CanvasTrimmingSign.Ellipsis,
TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
};
private CanvasTextFormat _artistTextFormat = new()
{
FontSize = 16,
FontWeight = FontWeights.Bold,
HorizontalAlignment = CanvasHorizontalAlignment.Left,
WordWrapping = CanvasWordWrapping.Wrap,
WordWrapping = CanvasWordWrapping.NoWrap,
TrimmingSign = CanvasTrimmingSign.Ellipsis,
TrimmingGranularity = CanvasTextTrimmingGranularity.Character,
};
private Task? _refreshLyricsTask;
private CancellationTokenSource? _refreshLyricsCts;
private Task? _showTranslationsTask;
private CancellationTokenSource? _showTranslationsCts;
public LyricsRendererViewModel(
ISettingsService settingsService, IPlaybackService playbackService,
IMusicSearchService musicSearchService, ILibWatcherService libWatcherService) : base(settingsService)
@@ -118,6 +138,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_musicSearchService = musicSearchService;
_playbackService = playbackService;
_libWatcherService = libWatcherService;
_logger = Ioc.Default.GetRequiredService<ILogger<LyricsRendererViewModel>>();
_albumArtCornerRadius = _settingsService.CoverImageRadius;
IsDynamicCoverOverlayEnabled = _settingsService.IsDynamicCoverOverlayEnabled;
@@ -134,8 +155,9 @@ namespace BetterLyrics.WinUI3.ViewModels
IsLyricsGlowEffectEnabled = _settingsService.IsLyricsGlowEffectEnabled;
LyricsGlowEffectScope = _settingsService.LyricsGlowEffectScope;
_customFontColor = _settingsService.LyricsCustomFontColor;
_isFanLyricsEnabled = _settingsService.IsFanLyricsEnabled;
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.LyricsAlignmentType.ToCanvasHorizontalAlignment();
_titleTextFormat.HorizontalAlignment = _artistTextFormat.HorizontalAlignment = _settingsService.SongInfoAlignmentType.ToCanvasHorizontalAlignment();
_libWatcherService.MusicLibraryFilesChanged +=
LibWatcherService_MusicLibraryFilesChanged;
@@ -187,8 +209,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[NotifyPropertyChangedRecipients]
public partial ElementTheme ThemeTypeSent { get; set; }
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
private int GetCurrentPlayingLineIndex()
{
for (int i = 0; i < _multiLangLyrics.SafeGet(_langIndex)?.Count; i++)
@@ -199,8 +219,8 @@ namespace BetterLyrics.WinUI3.ViewModels
continue;
}
if (
line.StartMs <= TotalTime.TotalMilliseconds
&& TotalTime.TotalMilliseconds <= line.EndMs
line.StartMs <= _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds
&& _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds <= line.EndMs
)
{
return i;
@@ -216,7 +236,7 @@ namespace BetterLyrics.WinUI3.ViewModels
charLength = 0;
charProgress = 0f;
float now = (float)TotalTime.TotalMilliseconds;
float now = (float)_totalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds;
// 1. 还没到本句
if (now < line.StartMs)
@@ -261,7 +281,7 @@ namespace BetterLyrics.WinUI3.ViewModels
charProgress = (now - line.StartMs) / line.DurationMs;
charProgress = Math.Clamp(charProgress, 0f, 1f);
charStartIndex = 0;
charLength = line.Text.Length;
charLength = line.OriginalText.Length;
}
}
@@ -279,9 +299,9 @@ namespace BetterLyrics.WinUI3.ViewModels
return new Tuple<int, int>(0, _multiLangLyrics[_langIndex].Count - 1);
}
private async void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
private void LibWatcherService_MusicLibraryFilesChanged(object? sender, LibChangedEventArgs e)
{
await RefreshLyricsAsync();
RefreshLyricsAsync();
}
private void PlaybackService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
@@ -291,27 +311,15 @@ namespace BetterLyrics.WinUI3.ViewModels
private void PlaybackService_PositionChanged(object? sender, PositionChangedEventArgs e)
{
TotalTime = e.Position;
if (Math.Abs(_totalTime.TotalMilliseconds - e.Position.TotalMilliseconds) > 300)
{
_totalTime = e.Position;
}
}
private async void PlaybackService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
{
SongInfo = e.SongInfo;
if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist)
{
_lastSongTitle = _songTitle;
_songTitle = SongInfo?.Title;
_lastSongArtist = _songArtist;
_songArtist = SongInfo?.Artist;
_songInfoOpacityTransition.Reset(0f);
_songInfoOpacityTransition.StartTransition(1f);
await RefreshLyricsAsync();
TotalTime = TimeSpan.Zero;
}
if (SongInfo?.AlbumArtSwBitmap != _albumArtSwBitmap)
{
@@ -327,6 +335,22 @@ namespace BetterLyrics.WinUI3.ViewModels
if (!_isDesktopMode && !_isDockMode) _adaptiveFontColor = Helper.ColorHelper.GetForegroundColor(_lyricsWindowBgColor);
UpdateFontColor();
}
if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist)
{
_lastSongTitle = _songTitle;
_songTitle = SongInfo?.Title;
_lastSongArtist = _songArtist;
_songArtist = SongInfo?.Artist;
_songInfoOpacityTransition.Reset(0f);
_songInfoOpacityTransition.StartTransition(1f);
await RefreshLyricsAsync();
_totalTime = TimeSpan.Zero;
}
}
private async Task RefreshLyricsAsync()
@@ -346,34 +370,123 @@ namespace BetterLyrics.WinUI3.ViewModels
await _refreshLyricsTask;
}
private async Task UpdateTranslationsAsync()
{
if (_isTranslationEnabled)
{
await ShowWithTranslationsAsync();
}
else
{
ShowOriginalsOnly();
}
}
private async Task ShowWithTranslationsAsync()
{
_showTranslationsCts?.Cancel();
if (_showTranslationsTask != null)
{
await _showTranslationsTask;
}
var cts = new CancellationTokenSource();
_showTranslationsCts = cts;
var token = cts.Token;
_showTranslationsTask = ShowTranslationsCoreAsync(token);
await _showTranslationsTask;
}
private async Task ShowTranslationsCoreAsync(CancellationToken token)
{
try
{
var text = string.Join("\n", _multiLangLyrics.FirstOrDefault()?.Select(x => x.OriginalText) ?? []);
GlobalLibreTranslateSettings.Server = new LibreTranslateServer("http://localhost:5000");
var translated = await text.TranslateAsync(LanguageCode.Chinese);
token.ThrowIfCancellationRequested();
_translations = translated.Split('\n').ToList();
bool totallySame = true;
foreach (var langLyrics in _multiLangLyrics)
{
int i = 0;
foreach (var line in langLyrics)
{
if (line.OriginalText != _translations[i])
{
totallySame = false;
break;
}
i++;
}
break;
}
if (totallySame) return; // No need to show translations
foreach (var langLyrics in _multiLangLyrics)
{
int i = 0;
foreach (var line in langLyrics)
{
line.DisplayedText = $"{line.OriginalText}\n{_translations[i]}";
i++;
}
break;
}
_isRelayoutNeeded = true;
}
catch (Exception) { }
}
private void ShowOriginalsOnly()
{
foreach (var langLyrics in _multiLangLyrics)
{
foreach (var line in langLyrics)
{
line.DisplayedText = line.OriginalText;
}
}
_isRelayoutNeeded = true;
}
private async Task RefreshLyricsCoreAsync(CancellationToken token)
{
try
{
_logger.LogInformation("Refreshing lyrics...");
SetLyricsLoadingPlaceholder();
string? lyricsRaw = null;
LyricsFormat? lyricsFormat = null;
if (SongInfo != null)
{
(lyricsRaw, lyricsFormat) = await _musicSearchService.SearchLyricsAsync(
lyricsRaw = await _musicSearchService.SearchLyricsAsync(
SongInfo.Title,
SongInfo.Artist,
SongInfo.Album ?? "",
SongInfo.DurationMs ?? 0
SongInfo.DurationMs ?? 0,
token
);
_logger.LogInformation("Lyrics search result: {LyricsRaw}", lyricsRaw ?? "null");
token.ThrowIfCancellationRequested();
}
else
{
_logger.LogWarning("SongInfo is null, cannot search lyrics.");
}
_multiLangLyrics = new LyricsParser().Parse(
lyricsRaw,
lyricsFormat,
SongInfo?.Title,
SongInfo?.Artist,
(int)(SongInfo?.DurationMs ?? 0)
);
_isRelayoutNeeded = true;
lyricsRaw,
(int?)SongInfo?.DurationMs ?? (int)TimeSpan.FromMinutes(99).TotalMilliseconds
);
_logger.LogInformation("Parsed lyrics: {MultiLangLyricsCount} languages", _multiLangLyrics.Count);
ShowOriginalsOnly();
await UpdateTranslationsAsync();
}
catch (Exception) { }
}
@@ -387,7 +500,8 @@ namespace BetterLyrics.WinUI3.ViewModels
{
StartMs = 0,
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
Text = "● ● ●",
OriginalText = "● ● ●",
DisplayedText = "● ● ●",
CharTimings = [],
},
]

View File

@@ -12,6 +12,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Windows.UI;
using WinRT.Interop;
@@ -110,6 +111,8 @@ namespace BetterLyrics.WinUI3
if (IsDockMode)
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
DockModeHelper.UpdateAppBarHeight(
WindowNative.GetWindowHandle(window),
message.NewValue * 3
@@ -121,9 +124,10 @@ namespace BetterLyrics.WinUI3
public void StartWatchWindowColorChange(WindowColorSampleMode mode)
{
var hwnd = WindowNative.GetWindowHandle(
WindowHelper.GetWindowByWindowType<LyricsWindow>()
);
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
var hwnd = WindowNative.GetWindowHandle(window);
_watcherHelper = new ForegroundWindowWatcherHelper(
hwnd,
onWindowChanged =>
@@ -158,6 +162,8 @@ namespace BetterLyrics.WinUI3
private void ToggleDesktopMode()
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
StopWatchWindowColorChange();
IsDesktopMode = !IsDesktopMode;

View File

@@ -97,6 +97,10 @@ namespace BetterLyrics.WinUI3.ViewModels
[NotifyPropertyChangedRecipients]
public partial bool IsDebugOverlayEnabled { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsLogEnabled { get; set; } = false;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial bool IsDynamicCoverOverlayEnabled { get; set; }
@@ -326,11 +330,13 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private async Task SelectAndAddFolderAsync(UIElement sender)
{
var picker = new Windows.Storage.Pickers.FolderPicker();
var window = WindowHelper.GetWindowByWindowType<SettingsWindow>();
if (window == null) return;
var picker = new Windows.Storage.Pickers.FolderPicker();
picker.FileTypeFilter.Add("*");
var hwnd = WindowNative.GetWindowHandle(WindowHelper.GetWindowByWindowType<SettingsWindow>());
var hwnd = WindowNative.GetWindowHandle(window);
InitializeWithWindow.Initialize(picker, hwnd);
var folder = await picker.PickSingleFolderAsync();

View File

@@ -50,6 +50,8 @@ namespace BetterLyrics.WinUI3.ViewModels
private void UnlockWindow()
{
var window = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
DesktopModeHelper.Unlock(window);
IsLyricsWindowLocked = false;
}

View File

@@ -34,26 +34,106 @@
</Grid.OpacityTransition>
</Grid>
<!-- Bottom-right command area -->
<!-- Bottom command area -->
<Grid
x:Name="BottomCommandGrid"
Margin="0,0,4,4"
Padding="12"
VerticalAlignment="Bottom"
Opacity="0"
PointerEntered="BottomCommandGrid_PointerEntered"
PointerExited="BottomCommandGrid_PointerExited"
Visibility="{x:Bind ViewModel.IsNotDockMode, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
Background="Transparent"
Opacity="0">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{x:Bind BottomCommandGrid}"
Value="1" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{x:Bind BottomCommandGrid}"
Value="0" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<TextBlock Margin="12,0,0,0" Text="{x:Bind ViewModel.SongInfo.SourceAppUserModelId, Mode=OneWay}" />
<StackPanel
x:Name="BottomLeftCommandStackPanel"
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="6">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" />
<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="&#xECE7;"
RenderTransformOrigin="0.5,0.5">
<FontIcon.RenderTransform>
<RotateTransform Angle="90" CenterX="0.5" CenterY="0.5" />
</FontIcon.RenderTransform>
</FontIcon>
<Button.Flyout>
<Flyout>
<StackPanel>
<Slider
x:Uid="MainPagePositionOffsetSlider"
Maximum="2000"
Minimum="-2000"
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=&#xE777;}"
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True" />
</RelativePanel>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<!-- Translation -->
<ToggleButton
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE8C1;}"
IsChecked="{x:Bind ViewModel.IsTranslationEnabled, Mode=TwoWay}"
Style="{StaticResource GhostToggleButtonStyle}" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<!-- Display type -->
<Button
x:Name="DisplayTypeSwitchButton"
@@ -85,9 +165,8 @@
<Button
x:Name="SettingsButton"
Command="{x:Bind ViewModel.OpenSettingsWindowCommand}"
Content="{ui:FontIcon FontWeight=Bold,
FontFamily={StaticResource IconFontFamily},
Glyph=&#xF8B0;}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE713;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>

View File

@@ -5,6 +5,8 @@ using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Views
{
@@ -19,24 +21,6 @@ namespace BetterLyrics.WinUI3.Views
public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext;
private void BottomCommandGrid_PointerEntered(
object sender,
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
)
{
if (BottomCommandGrid.Opacity == 0)
BottomCommandGrid.Opacity = .5;
}
private void BottomCommandGrid_PointerExited(
object sender,
Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e
)
{
if (BottomCommandGrid.Opacity == .5)
BottomCommandGrid.Opacity = 0;
}
private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args)
{
ViewModel.IsFirstRun = false;
@@ -56,5 +40,10 @@ namespace BetterLyrics.WinUI3.Views
{
ViewModel.PreferredDisplayType = ViewModel.DisplayType = LyricsDisplayType.SplitView;
}
private void PositionOffsetResetButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.PositionOffset = 0;
}
}
}

View File

@@ -15,38 +15,62 @@
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid
x:Name="RootGrid"
PointerMoved="RootGrid_PointerMoved"
RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
<Grid x:Name="RootGrid" RequestedTheme="{x:Bind ViewModel.ThemeType, Mode=OneWay}">
<local:LyricsPage />
<!-- Top command -->
<Grid
x:Name="TopCommandGrid"
Height="{x:Bind ViewModel.TitleBarHeight, Mode=OneWay}"
VerticalAlignment="Top"
Background="Transparent"
Opacity="0">
<Grid.OpacityTransition>
<ScalarTransition />
</Grid.OpacityTransition>
<interactivity:Interaction.Behaviors>
<interactivity:EventTriggerBehavior EventName="PointerEntered">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{x:Bind TopCommandGrid}"
Value="1" />
</interactivity:EventTriggerBehavior>
<interactivity:EventTriggerBehavior EventName="PointerExited">
<interactivity:ChangePropertyAction
PropertyName="Opacity"
TargetObject="{x:Bind TopCommandGrid}"
Value="0" />
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<StackPanel
x:Name="TopLeftCommandStackPanel"
Margin="12"
HorizontalAlignment="Left"
Orientation="Horizontal"
Spacing="6">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
</StackPanel>
<StackPanel
x:Name="TopRightCommandStackPanel"
HorizontalAlignment="Right"
Orientation="Horizontal">
<StackPanel.OpacityTransition>
<ScalarTransition />
</StackPanel.OpacityTransition>
<!-- Look -->
<Button
x:Name="ClickThroughButton"
Command="{x:Bind ViewModel.LockWindowCommand}"
Style="{StaticResource TitleBarButtonStyle}">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
FontWeight="Bold"
Glyph="&#xE72E;" />
</Grid>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xE72E;" />
<ToolTipService.ToolTip>
<ToolTip x:Name="LockToolTip" x:Uid="HostWindowLockToolTip" />
</ToolTipService.ToolTip>
@@ -65,27 +89,12 @@
</interactivity:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
<!-- More -->
<Button x:Name="MoreButton" Style="{StaticResource TitleBarButtonStyle}">
<Grid>
<FontIcon
Margin="0,0,0,8"
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
FontWeight="ExtraBold"
Glyph="&#xEF2D;" />
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
FontWeight="ExtraBold"
Glyph="&#xEF2D;" />
<FontIcon
Margin="0,8,0,0"
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
FontWeight="ExtraBold"
Glyph="&#xEF2D;" />
</Grid>
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xE712;" />
<Button.Flyout>
<MenuFlyout>
<ToggleMenuFlyoutItem
@@ -126,7 +135,7 @@
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xEF2D;" />
Glyph="&#xE629;" />
</Button>
<!-- Window Maximise -->
<Button
@@ -136,7 +145,7 @@
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xEF2E;" />
Glyph="&#xF16B;" />
</Button>
<!-- Window Restore -->
<Button
@@ -147,7 +156,7 @@
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xEF2F;" />
Glyph="&#xF5ED;" />
</Button>
<!-- Window Close -->
<Button
@@ -157,7 +166,7 @@
<FontIcon
FontFamily="{StaticResource IconFontFamily}"
FontSize="{x:Bind ViewModel.TitleBarFontSize, Mode=OneWay}"
Glyph="&#xEF2C;" />
Glyph="&#xE624;" />
</Button>
</StackPanel>

View File

@@ -3,10 +3,14 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Views
{
@@ -34,6 +38,12 @@ namespace BetterLyrics.WinUI3.Views
switch (type!)
{
case AutoStartWindowType.StandardMode:
AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(
_settingsService.StandardWindowLeft,
_settingsService.StandardWindowTop,
_settingsService.StandardWindowWidth,
_settingsService.StandardWindowHeight
));
break;
case AutoStartWindowType.DockMode:
DockFlyoutItem.IsChecked = true;
@@ -62,24 +72,32 @@ namespace BetterLyrics.WinUI3.Views
{
if (args.DidPresenterChange)
UpdateTitleBarWindowButtonsVisibility();
if (ViewModel.IsDesktopMode && (args.DidPositionChange || args.DidSizeChange))
OnPosOrSizeChanged();
}
private void OnPosOrSizeChanged()
{
var rect = AppWindow.Position;
var size = AppWindow.Size;
if (args.DidPositionChange || args.DidSizeChange)
{
var rect = AppWindow.Position;
var size = AppWindow.Size;
_settingsService.DesktopWindowLeft = rect.X;
_settingsService.DesktopWindowTop = rect.Y;
_settingsService.DesktopWindowWidth = size.Width;
_settingsService.DesktopWindowHeight = size.Height;
}
if (ViewModel.IsDesktopMode)
{
_settingsService.DesktopWindowLeft = rect.X;
_settingsService.DesktopWindowTop = rect.Y;
_settingsService.DesktopWindowWidth = size.Width;
_settingsService.DesktopWindowHeight = size.Height;
}
else if (ViewModel.IsDockMode)
{
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.ExitAllWindows();
}
else
{
_settingsService.StandardWindowLeft = rect.X;
_settingsService.StandardWindowTop = rect.Y;
_settingsService.StandardWindowWidth = size.Width;
_settingsService.StandardWindowHeight = size.Height;
}
}
}
private void FullScreenFlyoutItem_Click(object sender, RoutedEventArgs e)
@@ -101,14 +119,6 @@ namespace BetterLyrics.WinUI3.Views
}
}
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
{
if (AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.Maximize();
}
}
private void MiniFlyoutItem_Click(object sender, RoutedEventArgs e)
{
if (MiniFlyoutItem.IsChecked)
@@ -121,6 +131,112 @@ namespace BetterLyrics.WinUI3.Views
}
}
private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
}
private void UpdateTitleBarWindowButtonsVisibility()
{
switch (AppWindow.Presenter.Kind)
{
case AppWindowPresenterKind.Default:
break;
case AppWindowPresenterKind.CompactOverlay:
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
AOTFlyoutItem.Visibility = DesktopFlyoutItem.Visibility = FullScreenFlyoutItem.Visibility = DockFlyoutItem.Visibility =
ClickThroughButton.Visibility = Visibility.Collapsed;
break;
case AppWindowPresenterKind.FullScreen:
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
ClickThroughButton.Visibility =
DesktopFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
Visibility.Collapsed;
FullScreenFlyoutItem.IsChecked = true;
break;
case AppWindowPresenterKind.Overlapped:
DockFlyoutItem.Visibility = Visibility.Visible;
var overlappedPresenter = (OverlappedPresenter)AppWindow.Presenter;
if (DockFlyoutItem.IsChecked)
{
overlappedPresenter.IsMinimizable =
overlappedPresenter.IsMaximizable = false;
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
DesktopFlyoutItem.Visibility =
ClickThroughButton.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
Visibility.Collapsed;
}
else if (DesktopFlyoutItem.IsChecked)
{
overlappedPresenter.IsMinimizable =
overlappedPresenter.IsMaximizable = false;
MinimiseButton.Visibility = MaximiseButton.Visibility = RestoreButton.Visibility =
DockFlyoutItem.Visibility =
AOTFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
Visibility.Collapsed;
ClickThroughButton.Visibility = Visibility.Visible;
}
else
{
overlappedPresenter.IsMinimizable =
overlappedPresenter.IsMaximizable = true;
MinimiseButton.Visibility =
AOTFlyoutItem.Visibility =
DesktopFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
Visibility.Visible;
FullScreenFlyoutItem.IsChecked = false;
ClickThroughButton.Visibility = Visibility.Collapsed;
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
{
MaximiseButton.Visibility = Visibility.Collapsed;
RestoreButton.Visibility = Visibility.Visible;
}
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
{
MaximiseButton.Visibility = Visibility.Visible;
RestoreButton.Visibility = Visibility.Collapsed;
}
}
break;
default:
break;
}
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
WindowHelper.ExitAllWindows();
}
private void MaximiseButton_Click(object sender, RoutedEventArgs e)
{
if (AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.Maximize();
}
}
private void MinimiseButton_Click(object sender, RoutedEventArgs e)
{
if (AppWindow.Presenter is OverlappedPresenter presenter)
@@ -136,119 +252,5 @@ namespace BetterLyrics.WinUI3.Views
presenter.Restore();
}
}
private void RootGrid_PointerMoved(object sender, PointerRoutedEventArgs e)
{
var point = e.GetCurrentPoint(RootGrid);
double y = point.Position.Y;
if (y >= 0 && y <= TopCommandGrid.ActualHeight + 5)
{
if (TopCommandGrid.Opacity == 0)
{
TopCommandGrid.Opacity = .5;
}
}
else
{
if (TopCommandGrid.Opacity == .5)
{
TopCommandGrid.Opacity = 0;
}
}
}
private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
WindowHelper.OpenOrShowWindow<SettingsWindow>();
}
private void UpdateTitleBarWindowButtonsVisibility()
{
switch (AppWindow.Presenter.Kind)
{
case AppWindowPresenterKind.Default:
break;
case AppWindowPresenterKind.CompactOverlay:
MinimiseButton.Visibility =
MaximiseButton.Visibility =
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
DesktopFlyoutItem.Visibility =
ClickThroughButton.Visibility =
FullScreenFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
Visibility.Collapsed;
break;
case AppWindowPresenterKind.FullScreen:
MinimiseButton.Visibility =
MaximiseButton.Visibility =
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
ClickThroughButton.Visibility =
DesktopFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
Visibility.Collapsed;
FullScreenFlyoutItem.IsChecked = true;
break;
case AppWindowPresenterKind.Overlapped:
DockFlyoutItem.Visibility = Visibility.Visible;
var overlappedPresenter = (OverlappedPresenter)AppWindow.Presenter;
if (DockFlyoutItem.IsChecked)
{
MinimiseButton.Visibility =
MaximiseButton.Visibility =
RestoreButton.Visibility =
AOTFlyoutItem.Visibility =
DesktopFlyoutItem.Visibility =
ClickThroughButton.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
Visibility.Collapsed;
}
else if (DesktopFlyoutItem.IsChecked)
{
MinimiseButton.Visibility =
MaximiseButton.Visibility =
RestoreButton.Visibility =
DockFlyoutItem.Visibility =
AOTFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
Visibility.Collapsed;
ClickThroughButton.Visibility = Visibility.Visible;
}
else
{
MinimiseButton.Visibility =
AOTFlyoutItem.Visibility =
DesktopFlyoutItem.Visibility =
DockFlyoutItem.Visibility =
MiniFlyoutItem.Visibility =
FullScreenFlyoutItem.Visibility =
Visibility.Visible;
FullScreenFlyoutItem.IsChecked = false;
ClickThroughButton.Visibility = Visibility.Collapsed;
AOTFlyoutItem.IsChecked = overlappedPresenter.IsAlwaysOnTop;
if (overlappedPresenter.State == OverlappedPresenterState.Maximized)
{
MaximiseButton.Visibility = Visibility.Collapsed;
RestoreButton.Visibility = Visibility.Visible;
}
else if (overlappedPresenter.State == OverlappedPresenterState.Restored)
{
MaximiseButton.Visibility = Visibility.Visible;
RestoreButton.Visibility = Visibility.Collapsed;
}
}
TopCommandGrid.Opacity = 0;
break;
default:
break;
}
}
}
}

View File

@@ -53,6 +53,11 @@
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xEDC6;}"
Tag="Lyrics" />
<NavigationViewItem
x:Uid="SettingsPageTranslation"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE7F0;}"
Tag="Translation" />
<NavigationViewItem
x:Uid="SettingsPageAbout"
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
@@ -75,7 +80,6 @@
</controls:SwitchPresenter.ContentTransitions>
<!-- App appearance and behavior -->
<controls:Case Value="App">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -125,7 +129,6 @@
</controls:Case>
<!-- Lyrics background -->
<controls:Case Value="Background">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -174,7 +177,6 @@
</controls:Case>
<!-- Album art area style -->
<controls:Case Value="AlbumArtStyle">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -346,7 +348,6 @@
</controls:Case>
<!-- Lyrics style and effect -->
<controls:Case Value="Lyrics">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
@@ -520,8 +521,19 @@
</StackPanel>
</controls:Case>
<!-- About -->
<!-- Lyrics translation -->
<controls:Case Value="Translation">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsExpander>
<StackPanel Orientation="Horizontal">
<TextBox Text="http://127.0.0.1:5000" />
<Button Content="Verify" />
</StackPanel>
</controls:SettingsExpander>
</StackPanel>
</controls:Case>
<!-- About -->
<controls:Case Value="About">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard Header="BetterLyrics" HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/Logo.png}">
@@ -548,6 +560,7 @@
</StackPanel>
</controls:Case>
<!-- Dev -->
<controls:Case Value="Dev">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsCard x:Uid="SettingsPageMockMusicPlaying">