diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest index 3bdff2b..30514b4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3 (Package)/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="1.0.20.0" /> diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml index 2ec0055..a20901d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml @@ -12,6 +12,7 @@ + @@ -48,6 +49,7 @@ + @@ -82,7 +84,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs index 4b96992..c545c08 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/App.xaml.cs @@ -55,7 +55,7 @@ namespace BetterLyrics.WinUI3 protected override void OnLaunched(LaunchActivatedEventArgs args) { - WindowHelper.OpenOrShowWindow(); + WindowHelper.OpenWindow(); var lyricsWindow = WindowHelper.GetWindowByWindowType(); if (lyricsWindow == null) return; @@ -85,13 +85,13 @@ namespace BetterLyrics.WinUI3 .AddSingleton() .AddSingleton() .AddSingleton() - // Manager // ViewModels .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .BuildServiceProvider() ); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj index 530146b..cb27696 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/BetterLyrics.WinUI3.csproj @@ -9,6 +9,7 @@ true enable preview + true @@ -22,6 +23,8 @@ + + @@ -33,7 +36,8 @@ - + + @@ -46,10 +50,10 @@ - - + + - + @@ -58,8 +62,8 @@ - - + + @@ -68,7 +72,7 @@ - + @@ -89,6 +93,16 @@ Always + + + MSBuild:Compile + + + + + MSBuild:Compile + + MSBuild:Compile diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/SecondsToFormattedTimeConverter.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/SecondsToFormattedTimeConverter.cs new file mode 100644 index 0000000..d1c868f --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Converter/SecondsToFormattedTimeConverter.cs @@ -0,0 +1,30 @@ +using Microsoft.UI.Xaml.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Converter +{ + public class SecondsToFormattedTimeConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value is double seconds) + { + return TimeSpan.FromSeconds(seconds).ToString(@"mm\:ss"); + } + else if (value is int secondsInt) + { + return TimeSpan.FromSeconds(secondsInt).ToString(@"mm\:ss"); + } + return value?.ToString() ?? ""; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs index e13955a..7c48322 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/ImageHelper.cs @@ -35,6 +35,17 @@ namespace BetterLyrics.WinUI3.Helper return stream; } + public static RandomAccessStreamReference ByteArrayToRandomAccessStreamReference(byte[] bytes) + { + var stream = new InMemoryRandomAccessStream(); + var writer = new DataWriter(stream); + writer.WriteBytes(bytes); + writer.StoreAsync().GetAwaiter().GetResult(); + writer.FlushAsync().GetAwaiter().GetResult(); + writer.DetachStream(); + return RandomAccessStreamReference.CreateFromStream(stream); + } + public static async Task CreateTextPlaceholderBytesAsync(int width, int height) { var device = CanvasDevice.GetSharedDevice(); diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/WindowHelper.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/WindowHelper.cs index ed10238..27d8c94 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/WindowHelper.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Helper/WindowHelper.cs @@ -31,11 +31,8 @@ namespace BetterLyrics.WinUI3.Helper while (_activeWindows.Count > 0) { var window = (Window)_activeWindows[0]; - DockModeHelper.Disable(window); window.Close(); - _activeWindows.Remove(window); } - App.Current.Exit(); } public static T? GetWindowByWindowType() @@ -49,33 +46,32 @@ namespace BetterLyrics.WinUI3.Helper } return default; } - public static void OpenOrShowWindow() + public static void OpenWindow() { var window = _activeWindows.Find(w => w is T); - if (window != null) + if (window == null) { - var castedWindow = (Window)window; - castedWindow.Restore(); - } - else - { - object newWindow; if (typeof(T) == typeof(LyricsWindow)) { - newWindow = new LyricsWindow(); - ((LyricsWindow)newWindow).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent); + window = new LyricsWindow(); + ((LyricsWindow)window).SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(BackdropType.Transparent); } else if (typeof(T) == typeof(SettingsWindow)) { - newWindow = new SettingsWindow(); + window = new SettingsWindow(); + } + else if (typeof(T) == typeof(MusicGalleryWindow)) + { + window = new MusicGalleryWindow(); } else { throw new ArgumentException("Unsupported window type", nameof(T)); } - ((Window)newWindow).Activate(); - TrackWindow(newWindow); + TrackWindow(window); } + var castedWindow = (Window)window; + castedWindow.Restore(); } public static void RestartApp(string args = "") @@ -101,7 +97,19 @@ namespace BetterLyrics.WinUI3.Helper private static void TrackWindow(object window) { if (!_activeWindows.Contains(window)) + { _activeWindows.Add(window); + var castedWindow = (Window)window; + castedWindow.Closed += WindowHelper_Closed; + } + } + + private static void WindowHelper_Closed(object sender, WindowEventArgs args) + { + if (_activeWindows.Contains(sender)) + { + _activeWindows.Remove(sender); + } } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LocalLyricsFolder.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LocalLyricsFolder.cs index 0236b41..c873826 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LocalLyricsFolder.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/LocalLyricsFolder.cs @@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.ComponentModel; namespace BetterLyrics.WinUI3.Models { - public partial class LocalLyricsFolder : ObservableObject + public partial class LocalMediaFolder : ObservableObject { [ObservableProperty] public partial bool IsEnabled { get; set; } @@ -12,9 +12,9 @@ namespace BetterLyrics.WinUI3.Models [ObservableProperty] public partial string Path { get; set; } - public LocalLyricsFolder() { } + public LocalMediaFolder() { } - public LocalLyricsFolder(string path, bool isEnabled) + public LocalMediaFolder(string path, bool isEnabled) { Path = path; IsEnabled = isEnabled; diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs index 71b3216..d8212af 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/SongInfo.cs @@ -14,6 +14,9 @@ namespace BetterLyrics.WinUI3.Models [ObservableProperty] public partial string Artist { get; set; } + [ObservableProperty] + public partial int? Duration { get; set; } + [ObservableProperty] public partial double? DurationMs { get; set; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/TrimmedTrack.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/TrimmedTrack.cs new file mode 100644 index 0000000..c906f6f --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Models/TrimmedTrack.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BetterLyrics.WinUI3.Models +{ + public class TrimmedTrack + { + public string Title { get; set; } + public string Artist { get; set; } + public string Album { get; set; } + public int? Year { get; set; } + public string Genre { get; set; } + public string FilePath { get; set; } + public int Duration { get; set; } + public byte[]? AlbumArt { get; set; } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs index d9abb5f..71f46bd 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Serialization/SourceGenerationContext.cs @@ -11,7 +11,7 @@ namespace BetterLyrics.WinUI3.Serialization [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] - [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(TranslateResponse))] [JsonSerializable(typeof(JsonElement))] diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs index db92e94..022703e 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/AlbumArtSearchService.cs @@ -67,7 +67,7 @@ namespace BetterLyrics.WinUI3.Services private byte[]? SearchFile(string artist, string album) { - foreach (var folder in _settingsService.LocalLyricsFolders) + foreach (var folder in _settingsService.LocalMediaFolders) { if (Directory.Exists(folder.Path) && folder.IsEnabled) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs index 77019e1..c307f0a 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ILibWatcherService.cs @@ -14,6 +14,6 @@ namespace BetterLyrics.WinUI3.Services { event EventHandler? MusicLibraryFilesChanged; - public void UpdateWatchers(List folders); + public void UpdateWatchers(List folders); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs index 3deef32..4631e23 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/IPlaybackService.cs @@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Services Task PauseAsync(); Task PreviousAsync(); Task NextAsync(); + Task ChangePosition(double seconds); bool IsPlaying { get; } SongInfo? SongInfo { get; } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs index fb9741a..0dc5891 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/ISettingsService.cs @@ -41,7 +41,7 @@ namespace BetterLyrics.WinUI3.Services int PositionOffset { get; set; } // Lyrics lib - List LocalLyricsFolders { get; set; } + List LocalMediaFolders { get; set; } // Lyrics style and effetc diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs index c647bd5..8fcd7d4 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LibWatcherService.cs @@ -18,7 +18,7 @@ namespace BetterLyrics.WinUI3.Services public LibWatcherService(ISettingsService settingsService) { _settingsService = settingsService; - UpdateWatchers(_settingsService.LocalLyricsFolders); + UpdateWatchers(_settingsService.LocalMediaFolders); } public event EventHandler? MusicLibraryFilesChanged; @@ -32,7 +32,7 @@ namespace BetterLyrics.WinUI3.Services _watchers.Clear(); } - public void UpdateWatchers(List folders) + public void UpdateWatchers(List folders) { // 移除不再监听的 foreach (var key in _watchers.Keys.ToList()) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs index 4f879c4..5d97428 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/LyricsSearchService.cs @@ -164,7 +164,7 @@ namespace BetterLyrics.WinUI3.Services private async Task SearchFile(string title, string artist, LyricsFormat format) { - foreach (var folder in _settingsService.LocalLyricsFolders) + foreach (var folder in _settingsService.LocalMediaFolders) { if (Directory.Exists(folder.Path) && folder.IsEnabled) { @@ -186,7 +186,7 @@ namespace BetterLyrics.WinUI3.Services private string? SearchEmbedded(string title, string artist) { - foreach (var folder in _settingsService.LocalLyricsFolders) + foreach (var folder in _settingsService.LocalMediaFolders) { if (Directory.Exists(folder.Path) && folder.IsEnabled) { diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs index a0ab78f..9061620 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/PlaybackService.cs @@ -154,6 +154,8 @@ namespace BetterLyrics.WinUI3.Services SourceAppUserModelId = id, }; + _cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f); + await _onAnyMediaPropertyChangedRunner.RunAsync(async token => { _logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}", @@ -367,6 +369,15 @@ namespace BetterLyrics.WinUI3.Services } } + public async Task ChangePosition(double seconds) + { + var focusedSession = _mediaManager.GetFocusedSession(); + if (focusedSession != null) + { + await focusedSession.ControlSession.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks); + } + } + public void Receive(PropertyChangedMessage> message) { if (message.Sender is SettingsPageViewModel) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs index 977b9a4..4c56c96 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Services/SettingsService.cs @@ -231,7 +231,7 @@ namespace BetterLyrics.WinUI3.Services SetDefault(DockWindowHeightKey, 64); // 64px SetDefault(SelectedFontFamilyIndexKey, 0); SetDefault(LyricsFontFamilyKey, FontHelper.SystemFontFamilies.ElementAtOrDefault(0)); - SetDefault(IsDragEverywhereEnabledKey, true); + SetDefault(IsDragEverywhereEnabledKey, false); } public bool IsDragEverywhereEnabled @@ -420,19 +420,19 @@ namespace BetterLyrics.WinUI3.Services set => SetValue(LanguageKey, (int)value); } - public List LocalLyricsFolders + public List LocalMediaFolders { get => System.Text.Json.JsonSerializer.Deserialize( GetValue(LocalLyricsFoldersKey) ?? "[]", - SourceGenerationContext.Default.ListLocalLyricsFolder + SourceGenerationContext.Default.ListLocalMediaFolder )!; set => SetValue( LocalLyricsFoldersKey, System.Text.Json.JsonSerializer.Serialize( value, - SourceGenerationContext.Default.ListLocalLyricsFolder + SourceGenerationContext.Default.ListLocalMediaFolder ) ); } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw index b50a499..b0f7ef9 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/en-US/Resources.resw @@ -793,4 +793,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi Extend the title bar to the entire page so that the window can be dragged in any non-interactive area + + Music gallery + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw index d238d04..644e367 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ja-JP/Resources.resw @@ -793,4 +793,7 @@ タイトルバーをページ全体に拡張して、ウィンドウを非対話領域でドラッグできるようにします + + 音楽ギャラリー + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw index 6ec6246..8a24fbf 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/ko-KR/Resources.resw @@ -793,4 +793,7 @@ 비 중과 영역에서 창을 드래그 할 수 있도록 제목 표시 줄을 전체 페이지로 확장하십시오. + + 음악 갤러리 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw index e67bfdc..0093a06 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw @@ -793,4 +793,7 @@ 将标题栏扩展至整个页面使得在任意非交互区域均可拖拽窗口 + + 音乐库 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw index 716bfa7..9aa559b 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-TW/Resources.resw @@ -793,4 +793,7 @@ 將標題列擴展至整個頁面使得在任意非互動區域均可拖曳窗口 + + 音樂庫 + \ No newline at end of file diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs index 7302b6d..25c1055 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsPageViewModel.cs @@ -9,16 +9,19 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; +using System; using System.Numerics; using System.Threading.Tasks; namespace BetterLyrics.WinUI3.ViewModels { - public partial class LyricsPageViewModel : BaseViewModel, - IRecipient>, + public partial class LyricsPageViewModel : BaseViewModel, + IRecipient>, IRecipient>, - IRecipient> + IRecipient>, + IRecipient> { private readonly IPlaybackService _playbackService; @@ -46,10 +49,10 @@ namespace BetterLyrics.WinUI3.ViewModels IsSongPlaying = _playbackService.IsPlaying; } - //private void SystemVolumeHelper_VolumeChanged(int volume) - //{ - // Volume = volume; - //} + private void SystemVolumeHelper_VolumeChanged(int volume) + { + Volume = volume; + } private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e) { @@ -59,14 +62,21 @@ namespace BetterLyrics.WinUI3.ViewModels private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e) { SongInfo = e.SongInfo; + SongDurationSeconds = SongInfo?.Duration ?? 0; if (ResetPositionOffsetOnSongChanged) { PositionOffset = 0; } } - //[ObservableProperty] - //public partial int Volume { get; set; } + [ObservableProperty] + public partial double TimelinePositionSeconds { get; set; } + + [ObservableProperty] + public partial int SongDurationSeconds { get; set; } + + [ObservableProperty] + public partial int Volume { get; set; } [ObservableProperty] public partial string LyricsFontFamily { get; set; } @@ -74,12 +84,6 @@ namespace BetterLyrics.WinUI3.ViewModels [ObservableProperty] public partial int LyricsFontSize { get; set; } - [ObservableProperty] - public partial Vector3 BottomRightCommandGridTranslation { get; set; } = new Vector3(0, 0, 0); - - [ObservableProperty] - public partial Vector3 BottomCenterCommandGridTranslation { get; set; } = new Vector3(0, 0, 0); - [ObservableProperty] public partial bool IsImmersiveMode { get; set; } @@ -87,7 +91,7 @@ namespace BetterLyrics.WinUI3.ViewModels public partial float BottomCommandGridOpacity { get; set; } [ObservableProperty] - public partial Thickness BottomCommandGridMargin { get; set; } = new Thickness(12); + public partial float BottomCommandFlyoutTriggerOpacity { get; set; } [ObservableProperty] [NotifyPropertyChangedRecipients] @@ -157,7 +161,7 @@ namespace BetterLyrics.WinUI3.ViewModels [RelayCommand] private static void OpenSettingsWindow() { - WindowHelper.OpenOrShowWindow(); + WindowHelper.OpenWindow(); } [RelayCommand] @@ -205,10 +209,12 @@ namespace BetterLyrics.WinUI3.ViewModels if (value) { BottomCommandGridOpacity = 0f; + BottomCommandFlyoutTriggerOpacity = 0f; } else { BottomCommandGridOpacity = 1f; + BottomCommandFlyoutTriggerOpacity = 1f; } } @@ -239,9 +245,23 @@ namespace BetterLyrics.WinUI3.ViewModels } } - //partial void OnVolumeChanged(int value) - //{ - // SystemVolumeHelper.SetMasterVolume(value); - //} + partial void OnVolumeChanged(int value) + { + SystemVolumeHelper.SetMasterVolume(value); + } + + public void Receive(PropertyChangedMessage message) + { + if (message.Sender is LyricsRendererViewModel) + { + if (message.PropertyName == nameof(LyricsRendererViewModel.TotalTime)) + { + _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () => + { + TimelinePositionSeconds = message.NewValue.TotalSeconds; + }); + } + } + } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs index ce3a2a3..fb6d9da 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Ctor.cs @@ -2,6 +2,7 @@ using BetterLyrics.WinUI3.Services; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.Logging; +using System; namespace BetterLyrics.WinUI3.ViewModels { @@ -66,6 +67,8 @@ namespace BetterLyrics.WinUI3.ViewModels _playbackService.AlbumArtChangedChanged += PlaybackService_AlbumArtChangedChanged; _playbackService.PositionChanged += PlaybackService_PositionChanged; + _isPlaying = _playbackService.IsPlaying; + UpdateColorConfig(); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs index 0f67c60..718fc20 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Draw.cs @@ -78,7 +78,7 @@ namespace BetterLyrics.WinUI3.ViewModels $"[DEBUG]\n" + $"Cur playing {_playingLineIndex}, char start idx {charStartIndex}, length {charLength}, prog {charProgress}\n" + $"Visible lines [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" + - $"Cur time {_totalTime + _positionOffset}\n" + + $"Cur time {TotalTime + _positionOffset}\n" + $"Lang size {_lyricsDataArr.Count}\n" + $"Song duration {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}", new Vector2(10, 10), diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs index c5dabbb..33d96ce 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Messages.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml; using System; using System.Collections.ObjectModel; +using System.Threading.Tasks; using Windows.UI; namespace BetterLyrics.WinUI3.ViewModels @@ -24,13 +25,13 @@ namespace BetterLyrics.WinUI3.ViewModels IRecipient>, IRecipient>, IRecipient>>, - IRecipient>> + IRecipient>> { - public void Receive(PropertyChangedMessage> message) + public void Receive(PropertyChangedMessage> message) { if (message.Sender is SettingsPageViewModel) { - if (message.PropertyName == nameof(SettingsPageViewModel.LocalLyricsFolders)) + if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders)) { // Music lib changed, re-fetch lyrics _logger.LogInformation("Local lyrics folders changed, refreshing lyrics."); @@ -333,5 +334,6 @@ namespace BetterLyrics.WinUI3.ViewModels } } } + } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs index 684aac0..7e904e0 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.Update.cs @@ -7,6 +7,8 @@ using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.UI; using Microsoft.UI.Xaml; using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Numerics; using Windows.UI; @@ -29,7 +31,7 @@ namespace BetterLyrics.WinUI3.ViewModels if (_isPlaying) { - _totalTime += _elapsedTime; + TotalTime += _elapsedTime; } var playingLineIndex = GetCurrentPlayingLineIndex(); @@ -194,48 +196,13 @@ namespace BetterLyrics.WinUI3.ViewModels _canvasYScrollTransition.Update(_elapsedTime); - int startVisibleLineIndex = -1; - int endVisibleLineIndex = -1; - // Update visible line indices - for (int i = startLineIndex; i <= endLineIndex; i++) - { - var line = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.ElementAtOrDefault(i); + var lines = _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines; + if (lines == null || lines.Count == 0) return; - if (line == null || line.CanvasTextLayout == null) - { - continue; - } - - var textLayout = line.CanvasTextLayout; - - if ( - _canvasYScrollTransition.Value - + _canvasHeight / 2 - + line.Position.Y - + textLayout.LayoutBounds.Height - >= 0 - ) - { - if (startVisibleLineIndex == -1) - { - startVisibleLineIndex = i; - } - } - if ( - _canvasYScrollTransition.Value - + _canvasHeight / 2 - + line.Position.Y - + textLayout.LayoutBounds.Height - >= control.Size.Height - ) - { - if (endVisibleLineIndex == -1) - { - endVisibleLineIndex = i; - } - } - } + float offset = _canvasYScrollTransition.Value + _canvasHeight / 2; + int startVisibleLineIndex = FindFirstVisibleLine(lines, offset); + int endVisibleLineIndex = FindLastVisibleLine(lines, offset, _canvasHeight); if (startVisibleLineIndex != -1 && endVisibleLineIndex == -1) { @@ -248,6 +215,52 @@ namespace BetterLyrics.WinUI3.ViewModels _endVisibleLineIndex = endVisibleLineIndex; } + private int FindFirstVisibleLine(IList lines, float offset) + { + int left = 0, right = lines.Count - 1, result = -1; + while (left <= right) + { + int mid = (left + right) / 2; + var line = lines[mid]; + var layout = line.CanvasTextLayout; + if (layout == null) break; + float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height; + if (value >= 0) + { + result = mid; + right = mid - 1; + } + else + { + left = mid + 1; + } + } + return result; + } + + private int FindLastVisibleLine(IList lines, float offset, float canvasHeight) + { + int left = 0, right = lines.Count - 1, result = -1; + while (left <= right) + { + int mid = (left + right) / 2; + var line = lines[mid]; + var layout = line.CanvasTextLayout; + if (layout == null) break; + float value = offset + line.Position.Y + (float)layout.LayoutBounds.Height; + if (value >= canvasHeight) + { + result = mid; + right = mid - 1; + } + else + { + left = mid + 1; + } + } + return result; + } + private void UpdateColorConfig() { if (_isDesktopMode || _isDockMode) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs index a5edd99..1dce977 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/LyricsRendererViewModel.cs @@ -31,7 +31,11 @@ namespace BetterLyrics.WinUI3.ViewModels public partial class LyricsRendererViewModel : BaseViewModel { private TimeSpan _elapsedTime = TimeSpan.Zero; - private TimeSpan _totalTime = TimeSpan.Zero; + + [ObservableProperty] + [NotifyPropertyChangedRecipients] + public partial TimeSpan TotalTime { get; set; } = TimeSpan.Zero; + private TimeSpan _positionOffset = TimeSpan.Zero; private int _songDurationMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds; @@ -193,7 +197,7 @@ namespace BetterLyrics.WinUI3.ViewModels private int GetCurrentPlayingLineIndex() { - var totalMs = _totalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds; + var totalMs = TotalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds; if (totalMs < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.FirstOrDefault()?.StartMs) return 0; for (int i = 0; i < _lyricsDataArr.ElementAtOrDefault(_langIndex)?.LyricsLines.Count; i++) @@ -229,7 +233,7 @@ namespace BetterLyrics.WinUI3.ViewModels else if (nextLine != null) lineEndMs = nextLine.StartMs; else lineEndMs = _songDurationMs; - float now = (float)_totalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds; + float now = (float)TotalTime.TotalMilliseconds + (float)_positionOffset.TotalMilliseconds; // 1. 还没到本句 if (now < line.StartMs) @@ -333,9 +337,9 @@ namespace BetterLyrics.WinUI3.ViewModels private void PlaybackService_PositionChanged(object? sender, PositionChangedEventArgs e) { - if (Math.Abs(_totalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold) + if (Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds) >= _timelineSyncThreshold) { - _totalTime = e.Position; + TotalTime = e.Position; } } @@ -362,7 +366,7 @@ namespace BetterLyrics.WinUI3.ViewModels { await RefreshLyricsAsync(token); }); - _totalTime = TimeSpan.Zero; + TotalTime = TimeSpan.Zero; } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs new file mode 100644 index 0000000..e30ea43 --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/MusicGalleryViewModel.cs @@ -0,0 +1,148 @@ +using ATL; +using BetterLyrics.WinUI3.Helper; +using BetterLyrics.WinUI3.Models; +using BetterLyrics.WinUI3.Services; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Microsoft.UI.Xaml.Controls; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel; +using Windows.Media; +using Windows.Media.Core; +using Windows.Media.Playback; + +namespace BetterLyrics.WinUI3.ViewModels +{ + public partial class MusicGalleryViewModel : BaseViewModel, + IRecipient>> + { + private readonly ILibWatcherService _libWatcherService; + private readonly MediaPlayer _mediaPlayer = new(); + private readonly SystemMediaTransportControls _smtc; + + [ObservableProperty] + public partial ObservableCollection Tracks { get; set; } = []; + + [ObservableProperty] + public partial bool IsDataLoading { get; set; } = false; + + public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) : base(settingsService) + { + _smtc = _mediaPlayer.SystemMediaTransportControls; + _mediaPlayer.CommandManager.IsEnabled = false; + _smtc.IsEnabled = true; + _smtc.IsPlayEnabled = true; + _smtc.IsPauseEnabled = true; + _smtc.IsNextEnabled = true; + _smtc.IsPreviousEnabled = true; + _smtc.ButtonPressed += Smtc_ButtonPressed; + _smtc.PlaybackPositionChangeRequested += Smtc_PlaybackPositionChangeRequested; + + _libWatcherService = libWatcherService; + _libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged; + } + + private void Smtc_PlaybackPositionChangeRequested(SystemMediaTransportControls sender, PlaybackPositionChangeRequestedEventArgs args) + { + _mediaPlayer.TimelineController.Position = args.RequestedPlaybackPosition; + } + + private void Smtc_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) + { + switch (args.Button) + { + case SystemMediaTransportControlsButton.Play: + _smtc.PlaybackStatus = MediaPlaybackStatus.Playing; + _mediaPlayer.Play(); + break; + case SystemMediaTransportControlsButton.Pause: + _smtc.PlaybackStatus = MediaPlaybackStatus.Paused; + _mediaPlayer.Pause(); + break; + case SystemMediaTransportControlsButton.Next: + //Next + break; + case SystemMediaTransportControlsButton.Previous: + //Previous + break; + } + } + + private void LibWatcherService_MusicLibraryFilesChanged(object? sender, Events.LibChangedEventArgs e) + { + RefreshSongs(); + } + + public void RefreshSongs() + { + IsDataLoading = true; + Tracks.Clear(); + + Task.Run(() => + { + foreach (var folder in _settingsService.LocalMediaFolders) + { + if (Directory.Exists(folder.Path) && folder.IsEnabled) + { + foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories)) + { + Track track = new(file); + _dispatcherQueue.TryEnqueue(() => + { + Tracks.Add(track); + }); + } + } + } + + _dispatcherQueue.TryEnqueue(() => + { + IsDataLoading = false; + }); + }); + } + + public void PlaySongAt(int? index) + { + if (index.HasValue) + { + var track = Tracks.ElementAtOrDefault(index.Value); + if (track != null) + { + _mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path)); + var updater = _smtc.DisplayUpdater; + updater.AppMediaId = Package.Current.Id.FullName; + updater.Type = MediaPlaybackType.Music; + updater.MusicProperties.Title = track.Title; + updater.MusicProperties.Artist = track.Artist; + updater.MusicProperties.AlbumTitle = track.Album; + if (track.EmbeddedPictures.FirstOrDefault()?.PictureData is byte[] pictureData) + { + updater.Thumbnail = ImageHelper.ByteArrayToRandomAccessStreamReference(pictureData); + } + updater.Update(); + _mediaPlayer.Play(); + _smtc.PlaybackStatus = MediaPlaybackStatus.Playing; + } + } + } + + public void Receive(PropertyChangedMessage> message) + { + if (message.Sender is SettingsPageViewModel) + { + if (message.PropertyName == nameof(SettingsPageViewModel.LocalMediaFolders)) + { + RefreshSongs(); + } + } + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs index 8a99b40..bbb6fd9 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SettingsPageViewModel.cs @@ -7,25 +7,17 @@ using BetterLyrics.WinUI3.Services; using BetterLyrics.WinUI3.Views; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using CommunityToolkit.Mvvm.Messaging; -using Microsoft.Graphics.Canvas.Text; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using ShadowViewer.Controls; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Security.Cryptography; using System.Threading.Tasks; using Windows.ApplicationModel; using Windows.Globalization; -using Windows.Media.Playback; using Windows.System; using Windows.UI; -using Windows.UI.Popups; using WinRT.Interop; using MetadataHelper = BetterLyrics.WinUI3.Helper.MetadataHelper; @@ -48,7 +40,7 @@ namespace BetterLyrics.WinUI3.ViewModels LibreTranslateServer = _settingsService.LibreTranslateServer; SelectedTargetLanguageIndex = _settingsService.SelectedTargetLanguageIndex; - LocalLyricsFolders = [.. _settingsService.LocalLyricsFolders]; + LocalMediaFolders = [.. _settingsService.LocalMediaFolders]; LyricsSearchProvidersInfo = [.. _settingsService.LyricsSearchProvidersInfo]; AlbumArtSearchProvidersInfo = [.. _settingsService.AlbumArtSearchProvidersInfo]; @@ -180,7 +172,7 @@ namespace BetterLyrics.WinUI3.ViewModels public partial Enums.Language Language { get; set; } [ObservableProperty] - public partial ObservableCollection LocalLyricsFolders { get; set; } + public partial ObservableCollection LocalMediaFolders { get; set; } [ObservableProperty] [NotifyPropertyChangedRecipients] @@ -345,18 +337,18 @@ namespace BetterLyrics.WinUI3.ViewModels ); } - public void RemoveFolderAsync(LocalLyricsFolder folder) + public void RemoveFolderAsync(LocalMediaFolder folder) { - LocalLyricsFolders.Remove(folder); - _settingsService.LocalLyricsFolders = [.. LocalLyricsFolders]; - _libWatcherService.UpdateWatchers([.. LocalLyricsFolders]); - Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders)); + LocalMediaFolders.Remove(folder); + _settingsService.LocalMediaFolders = [.. LocalMediaFolders]; + _libWatcherService.UpdateWatchers([.. LocalMediaFolders]); + Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders)); } - public void ToggleLocalLyricsFolder(LocalLyricsFolder folder) + public void ToggleLocalLyricsFolder(LocalMediaFolder folder) { - _settingsService.LocalLyricsFolders = [.. LocalLyricsFolders]; - Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders)); + _settingsService.LocalMediaFolders = [.. LocalMediaFolders]; + Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders)); } public void ToggleLyricsSearchProvider(LyricsSearchProviderInfo providerInfo) @@ -392,16 +384,16 @@ namespace BetterLyrics.WinUI3.ViewModels { var normalizedPath = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - if (LocalLyricsFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase))) + if (LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase))) { App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathExistedInfo")); } - else if (LocalLyricsFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))) + else if (LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))) { // 添加的文件夹是现有文件夹的子文件夹 App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPagePathBeIncludedInfo")); } - else if (LocalLyricsFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)) + else if (LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase)) ) { // 添加的文件夹是现有文件夹的父文件夹 @@ -409,10 +401,10 @@ namespace BetterLyrics.WinUI3.ViewModels } else { - LocalLyricsFolders.Add(new LocalLyricsFolder(path, true)); - _settingsService.LocalLyricsFolders = [.. LocalLyricsFolders]; - _libWatcherService.UpdateWatchers([.. LocalLyricsFolders]); - Broadcast(LocalLyricsFolders, LocalLyricsFolders, nameof(LocalLyricsFolders)); + LocalMediaFolders.Add(new LocalMediaFolder(path, true)); + _settingsService.LocalMediaFolders = [.. LocalMediaFolders]; + _libWatcherService.UpdateWatchers([.. LocalMediaFolders]); + Broadcast(LocalMediaFolders, LocalMediaFolders, nameof(LocalMediaFolders)); } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs index ecf1758..86a3c6d 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/ViewModels/SystemTrayViewModel.cs @@ -40,7 +40,7 @@ namespace BetterLyrics.WinUI3.ViewModels [RelayCommand] private static void OpenSettings() { - WindowHelper.OpenOrShowWindow(); + WindowHelper.OpenWindow(); } [RelayCommand] diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml index acd4b6b..82839c0 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml @@ -46,26 +46,15 @@ Value="{x:Null}"> - - - - - - @@ -73,291 +62,330 @@ - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + x:Name="BottomCommandFlyoutTriggerHint" + Width="150" + Margin="4" + Background="{ThemeResource TextFillColorPrimaryBrush}" + CornerRadius="2" + Translation="0,0,0"> - - - - - - - - - - - - - - - - + + + + + + + + - + - + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs index 2239869..ad61c07 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsPage.xaml.cs @@ -6,6 +6,8 @@ using BetterLyrics.WinUI3.ViewModels; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using System.Diagnostics; +using System.Numerics; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -14,16 +16,17 @@ namespace BetterLyrics.WinUI3.Views public sealed partial class LyricsPage : Page { private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService(); + private readonly IPlaybackService _playbackService = Ioc.Default.GetRequiredService(); + + public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext; public LyricsPage() { this.InitializeComponent(); - DataContext = Ioc.Default.GetService(); + DataContext = Ioc.Default.GetRequiredService(); } - public LyricsPageViewModel ViewModel => (LyricsPageViewModel)DataContext; - private void WelcomeTeachingTip_Closed(TeachingTip sender, TeachingTipClosedEventArgs args) { ViewModel.IsFirstRun = false; @@ -54,18 +57,20 @@ namespace BetterLyrics.WinUI3.Views private void BottomCommandGrid_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - if (ViewModel.IsImmersiveMode) + if (ViewModel.IsImmersiveMode && BottomCommandGrid.Children.Count != 0) { ViewModel.BottomCommandGridOpacity = 1f; } + e.Handled = true; } private void BottomCommandGrid_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) { - if (ViewModel.IsImmersiveMode) + if (ViewModel.IsImmersiveMode && BottomCommandGrid.Children.Count != 0) { ViewModel.BottomCommandGridOpacity = 0f; } + e.Handled = true; } private void DisplayTypeSwitchButton_Click(object sender, RoutedEventArgs e) @@ -75,7 +80,7 @@ namespace BetterLyrics.WinUI3.Views private void TimelineOffsetButton_Click(object sender, RoutedEventArgs e) { - TimelineOffsetFlyout.ShowAt(BottomRightCommandStackPanel); + TimelineOffsetFlyout.ShowAt(BottomLeftCommandStackPanel); } private void TranslationButton_Click(object sender, RoutedEventArgs e) @@ -85,33 +90,59 @@ namespace BetterLyrics.WinUI3.Views private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) { - if (e.NewSize.Width < 500) + if (e.NewSize.Width < 500 || e.NewSize.Height < 100) { - ViewModel.BottomCenterCommandGridTranslation = new System.Numerics.Vector3(0, -48, 0); + if (BottomCommandGrid.Children.Count != 0) + { + BottomCommandGrid.Children.Remove(BottomCommandContent); + BottomCommandFlyoutContainer.Children.Add(BottomCommandContent); + } + BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 0, 0); } else { - ViewModel.BottomCenterCommandGridTranslation = new System.Numerics.Vector3(0, 0, 0); + if (BottomCommandFlyoutContainer.Children.Count != 0) + { + BottomCommandFlyout.Hide(); + BottomCommandFlyoutContainer.Children.Remove(BottomCommandContent); + BottomCommandGrid.Children.Add(BottomCommandContent); + } + BottomCommandFlyoutTriggerHint.Translation = new Vector3(0, 12, 0); } + } - if (e.NewSize.Height < 80) - { - ViewModel.BottomRightCommandGridTranslation = new System.Numerics.Vector3(-200, 0, 0); - } - else - { - ViewModel.BottomRightCommandGridTranslation = new System.Numerics.Vector3(0, 0, 0); - } + private async void TimelineSliderOverlay_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e) + { + await _playbackService.ChangePosition(e.NewValue); + } - if (e.NewSize.Height < 100) - { - ViewModel.BottomCommandGridMargin = new Thickness(0); - } - else - { - ViewModel.BottomCommandGridMargin = new Thickness(12); - } + private void VolumeButton_Click(object sender, RoutedEventArgs e) + { + VolumeFlyout.ShowAt(BottomRightCommandStackPanel); + } + private void BottomCommandFlyoutTrigger_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (ViewModel.IsImmersiveMode && BottomCommandFlyoutContainer.Children.Count != 0) + { + ViewModel.BottomCommandFlyoutTriggerOpacity = 1f; + } + } + + private void BottomCommandFlyoutTrigger_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e) + { + if (ViewModel.IsImmersiveMode && BottomCommandFlyoutContainer.Children.Count != 0) + { + ViewModel.BottomCommandFlyoutTriggerOpacity = 0f; + } + } + + private void BottomCommandFlyoutTrigger_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e) + { + if (BottomCommandFlyoutContainer.Children.Count != 0) + { + BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger); + } } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml index 4901035..a0c7cdc 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/LyricsWindow.xaml @@ -25,7 +25,6 @@ - + + + + + - + = 0 && rect.Y >= 0 && size.Width > 0 && size.Height > 0) { + if (ViewModel.IsDesktopMode) + { + _settingsService.DesktopWindowLeft = rect.X; + _settingsService.DesktopWindowTop = rect.Y; + _settingsService.DesktopWindowWidth = size.Width; + _settingsService.DesktopWindowHeight = size.Height; + } + else if (ViewModel.IsDockMode) + { + } + else + { + _settingsService.StandardWindowLeft = rect.X; + _settingsService.StandardWindowTop = rect.Y; + _settingsService.StandardWindowWidth = size.Width; + _settingsService.StandardWindowHeight = size.Height; + } } - else - { - _settingsService.StandardWindowLeft = rect.X; - _settingsService.StandardWindowTop = rect.Y; - _settingsService.StandardWindowWidth = size.Width; - _settingsService.StandardWindowHeight = size.Height; - } - } } @@ -164,7 +166,7 @@ namespace BetterLyrics.WinUI3.Views private void SettingsMenuFlyoutItem_Click(object sender, RoutedEventArgs e) { - WindowHelper.OpenOrShowWindow(); + WindowHelper.OpenWindow(); } private void UpdateTitleBarWindowButtonsVisibility() @@ -259,7 +261,8 @@ namespace BetterLyrics.WinUI3.Views private void CloseButton_Click(object sender, RoutedEventArgs e) { - WindowHelper.ExitAllWindows(); + DockModeHelper.Disable(this); + App.Current.Exit(); } private void MaximiseButton_Click(object sender, RoutedEventArgs e) @@ -310,23 +313,22 @@ namespace BetterLyrics.WinUI3.Views private void RootGrid_PointerEntered(object sender, PointerRoutedEventArgs e) { ViewModel.IsMouseWithinWindow = true; + e.Handled = true; } private void RootGrid_PointerExited(object sender, PointerRoutedEventArgs e) { ViewModel.IsMouseWithinWindow = false; + e.Handled = true; } private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) { - if (e.NewSize.Height < 100) - { - TopCommandGrid.Margin = new Thickness(0); - } - else - { - TopCommandGrid.Margin = new Thickness(12); - } + } + + private void MusicGalleryButton_Click(object sender, RoutedEventArgs e) + { + WindowHelper.OpenWindow(); } } } diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml new file mode 100644 index 0000000..3446cfe --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml.cs new file mode 100644 index 0000000..186d25a --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryPage.xaml.cs @@ -0,0 +1,45 @@ +using BetterLyrics.WinUI3.ViewModels; +using CommunityToolkit.Mvvm.DependencyInjection; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace BetterLyrics.WinUI3.Views +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MusicGalleryPage : Page + { + public MusicGalleryViewModel ViewModel => (MusicGalleryViewModel)DataContext; + public MusicGalleryPage() + { + InitializeComponent(); + DataContext = Ioc.Default.GetRequiredService(); + } + + private void SongListView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + ViewModel.PlaySongAt((sender as ListView)?.SelectedIndex); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + ViewModel.RefreshSongs(); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml new file mode 100644 index 0000000..3e9580e --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml.cs new file mode 100644 index 0000000..e7cd49f --- /dev/null +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/MusicGalleryWindow.xaml.cs @@ -0,0 +1,36 @@ +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Data; +using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Navigation; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; +using Windows.Foundation.Collections; +using WinUIEx; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace BetterLyrics.WinUI3.Views +{ + /// + /// An empty window that can be used on its own or navigated to within a Frame. + /// + public sealed partial class MusicGalleryWindow : Window + { + public MusicGalleryWindow() + { + InitializeComponent(); + Title = App.ResourceLoader!.GetString("MusicGalleryPageTitle"); + AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode; + this.SetIcon(@"Assets/Logo.ico"); + } + } +} diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml index 04c4e15..f0cb9df 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml @@ -391,7 +391,7 @@ HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}" IsExpanded="True" - ItemsSource="{x:Bind ViewModel.LocalLyricsFolders, Mode=OneWay}"> + ItemsSource="{x:Bind ViewModel.LocalMediaFolders, Mode=OneWay}"> @@ -426,13 +426,13 @@ diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml.cs index 53439f4..6fe5753 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsPage.xaml.cs @@ -26,7 +26,7 @@ namespace BetterLyrics.WinUI3.Views { if (sender is ToggleSwitch toggleSwitch) { - if (toggleSwitch.DataContext is LocalLyricsFolder localLyricsFolder) + if (toggleSwitch.DataContext is LocalMediaFolder localLyricsFolder) { ViewModel.ToggleLocalLyricsFolder(localLyricsFolder); } @@ -65,7 +65,7 @@ namespace BetterLyrics.WinUI3.Views Microsoft.UI.Xaml.RoutedEventArgs e ) { - ViewModel.RemoveFolderAsync((LocalLyricsFolder)(sender as HyperlinkButton)!.Tag); + ViewModel.RemoveFolderAsync((LocalMediaFolder)(sender as HyperlinkButton)!.Tag); } private void MediaSourceProviderToggleSwitch_Toggled(object sender, RoutedEventArgs e) diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml index 3e54e3a..c1a9851 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml @@ -12,6 +12,8 @@ - + + + diff --git a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml.cs b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml.cs index 452c957..daa7488 100644 --- a/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml.cs +++ b/BetterLyrics.WinUI3/BetterLyrics.WinUI3/Views/SettingsWindow.xaml.cs @@ -1,6 +1,6 @@ using BetterLyrics.WinUI3.ViewModels; using CommunityToolkit.Mvvm.DependencyInjection; -using H.NotifyIcon; +using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using WinUIEx; @@ -8,26 +8,14 @@ namespace BetterLyrics.WinUI3.Views { public sealed partial class SettingsWindow : Window { + public SettingsWindowViewModel ViewModel { get; set; } = Ioc.Default.GetRequiredService(); + public SettingsWindow() { InitializeComponent(); - Title = App.ResourceLoader!.GetString("SettingsPageTitle"); - ExtendsContentIntoTitleBar = true; - - AppWindow.Closing += AppWindow_Closing; - } - - public SettingsWindowViewModel ViewModel { get; set; } = - Ioc.Default.GetRequiredService(); - - private void AppWindow_Closing( - Microsoft.UI.Windowing.AppWindow sender, - Microsoft.UI.Windowing.AppWindowClosingEventArgs args - ) - { - args.Cancel = true; // Prevent the window from closing - this.Hide(true); + AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode; + this.SetIcon(@"Assets/Logo.ico"); } } }