From 097e9b88e67120ad4408166f651700e036fc57f6 Mon Sep 17 00:00:00 2001 From: Difegue <8237712+Difegue@users.noreply.github.com> Date: Sun, 18 Sep 2022 01:29:51 +0200 Subject: [PATCH 01/56] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e1cebe..79a5d07 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Stylophone [**Music Player Daemon**](https://www.musicpd.org/) Client for UWP and iOS/iPadOS. Based on [MpcNET](https://github.com/Difegue/MpcNET), my own fork of the original .NET Client Library for MPD. (now on NuGet!) -English badge +[](https://www.microsoft.com/store/apps/9NCB693428T8?cid=storebadge&ocid=badge) [](https://apps.apple.com/us/app/stylophone/id1644672889?itsct=apps_box_link&itscg=30200) [Buy a sticker if you want!](https://ko-fi.com/s/9fcf421b6e) From 971a5174299cb684c6f9419f774f8584c9703795 Mon Sep 17 00:00:00 2001 From: Difegue Date: Sun, 25 Sep 2022 21:38:56 +0200 Subject: [PATCH 02/56] (#58) Fix theme preference not saving + misc. AppCenter bugfixes --- .../ViewModels/PlaylistViewModel.cs | 7 ++++++- .../ViewModels/QueueViewModel.cs | 17 ++++++++--------- .../ViewModels/SettingsViewModel.cs | 6 +----- Sources/Stylophone/Package.appxmanifest | 2 +- Sources/Stylophone/Package.tt | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Sources/Stylophone.Common/ViewModels/PlaylistViewModel.cs b/Sources/Stylophone.Common/ViewModels/PlaylistViewModel.cs index 917e1a1..0b60563 100644 --- a/Sources/Stylophone.Common/ViewModels/PlaylistViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/PlaylistViewModel.cs @@ -248,7 +248,12 @@ await Task.Run(async () => DominantColor = (art?.DominantColor?.Color).GetValueOrDefault(); if (DominantColor == default(SKColor)) - DominantColor = _interop.GetAccentColor(); + { + await _dispatcherService.ExecuteOnUIThreadAsync(() => + { + DominantColor = _interop.GetAccentColor(); + }); + } IsLight = (!art?.DominantColor?.IsDark).GetValueOrDefault(); } diff --git a/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs b/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs index 53ca64a..4a9b223 100644 --- a/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs @@ -206,18 +206,17 @@ private async void MPDConnectionService_QueueChanged(object sender, EventArgs e) } else { - // PlChanges gives the full list of files starting from the change, so we delete all existing tracks from the source after that change, and swap the new ones in. + // PlChanges gives the full list of files starting from the change, + // so we delete all existing tracks from the source after that change, and swap the new ones in. // If the response is empty, that means the last file in the source was removed. var initialPosition = response.Count() == 0 ? Source.Count - 1 : response.First().Position; - while (Source.Count != initialPosition) - { - await _dispatcherService.ExecuteOnUIThreadAsync(() => { - // Make sure - if (Source.Count != initialPosition) - Source.RemoveAt(initialPosition); - }); - } + await _dispatcherService.ExecuteOnUIThreadAsync(() => { + while (Source.Count > initialPosition) + { + Source.RemoveAt(initialPosition); + } + }); var toAdd = new List(); foreach (var item in response) diff --git a/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs b/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs index 34d07aa..8f4e2f6 100644 --- a/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs @@ -78,11 +78,7 @@ public SettingsViewModel(MPDConnectionService mpdService, IApplicationStorageSer partial void OnElementThemeChanged(Theme value) { Task.Run (async () => await _interop.SetThemeAsync(value)); - - if (value != _elementTheme) - { - _applicationStorageService.SetValue(nameof(ElementTheme), value.ToString()); - } + _applicationStorageService.SetValue(nameof(ElementTheme), value.ToString()); } partial void OnServerHostChanged(string value) diff --git a/Sources/Stylophone/Package.appxmanifest b/Sources/Stylophone/Package.appxmanifest index 04666c7..402c962 100644 --- a/Sources/Stylophone/Package.appxmanifest +++ b/Sources/Stylophone/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="2.5.5.0" /> diff --git a/Sources/Stylophone/Package.tt b/Sources/Stylophone/Package.tt index f796b29..65a9650 100644 --- a/Sources/Stylophone/Package.tt +++ b/Sources/Stylophone/Package.tt @@ -2,7 +2,7 @@ <#@ output extension=".appxmanifest" #> <#@ parameter type="System.String" name="BuildConfiguration" #> <# - string version = "2.5.2.0"; + string version = "2.5.5.0"; // Get configuration name at Build time string configName = Host.ResolveParameterValue("-", "-", "BuildConfiguration"); From 7afdf574a2ff95b32b4587a9967f43c57a088bb1 Mon Sep 17 00:00:00 2001 From: Difegue <8237712+Difegue@users.noreply.github.com> Date: Sat, 22 Oct 2022 22:02:32 +0200 Subject: [PATCH 03/56] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79a5d07..c04b8de 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Stylophone [**Music Player Daemon**](https://www.musicpd.org/) Client for UWP and iOS/iPadOS. Based on [MpcNET](https://github.com/Difegue/MpcNET), my own fork of the original .NET Client Library for MPD. (now on NuGet!) -[](https://www.microsoft.com/store/apps/9NCB693428T8?cid=storebadge&ocid=badge) [](https://apps.apple.com/us/app/stylophone/id1644672889?itsct=apps_box_link&itscg=30200) +[](https://www.microsoft.com/store/apps/9NCB693428T8?cid=storebadge&ocid=badge) [](https://apps.apple.com/us/app/stylophone/id1644672889?itsct=apps_box_link&itscg=30200) [Buy a sticker if you want!](https://ko-fi.com/s/9fcf421b6e) From 0f5a5b84a7df6214da506c2ca8e945614e25b70e Mon Sep 17 00:00:00 2001 From: Difegue Date: Sat, 26 Nov 2022 02:21:08 +0100 Subject: [PATCH 04/56] Improve suspend/resume on Windows not sure if that actually fixes it fully since debugging app lifecycle doesn't work at all --- .../Services/AlbumArtService.cs | 6 ++- .../Services/MPDConnectionService.cs | 48 ++++++++++++------- .../ViewModels/Bases/PlaybackViewModelBase.cs | 5 +- Sources/Stylophone/App.xaml.cs | 15 ++++++ 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/Sources/Stylophone.Common/Services/AlbumArtService.cs b/Sources/Stylophone.Common/Services/AlbumArtService.cs index 25e9df9..7e88da0 100644 --- a/Sources/Stylophone.Common/Services/AlbumArtService.cs +++ b/Sources/Stylophone.Common/Services/AlbumArtService.cs @@ -40,7 +40,6 @@ public void Initialize() { _queueCanceller?.Cancel(); _queueCanceller = new CancellationTokenSource(); - var token = _queueCanceller.Token; _albumArtQueue = new Stack(); @@ -81,6 +80,11 @@ public void Initialize() }).ConfigureAwait(false); } + public void Stop() + { + _queueCanceller?.Cancel(); + } + /// /// Check if this file's album art is already stored in the internal Album Art cache. /// diff --git a/Sources/Stylophone.Common/Services/MPDConnectionService.cs b/Sources/Stylophone.Common/Services/MPDConnectionService.cs index 4d0acc2..8666aa2 100644 --- a/Sources/Stylophone.Common/Services/MPDConnectionService.cs +++ b/Sources/Stylophone.Common/Services/MPDConnectionService.cs @@ -77,13 +77,7 @@ public async Task InitializeAsync(bool withRetry = false) IsConnecting = true; CurrentStatus = BOGUS_STATUS; // Reset status - if (IsConnected) - { - IsConnected = false; - ConnectionChanged?.Invoke(this, new EventArgs()); - } - - ClearResources(); + Disconnect(); var cancelToken = _cancelConnect.Token; @@ -106,23 +100,31 @@ public async Task InitializeAsync(bool withRetry = false) _connectionRetryAttempter.Start(); } } - + IsConnecting = false; } - private void ClearResources() + public void Disconnect() { - _idleConnection?.SendAsync(new NoIdleCommand()); - _idleConnection?.DisconnectAsync(); - _statusConnection?.DisconnectAsync(); + + if (IsConnected) + { + System.Diagnostics.Debug.WriteLine($"Terminating MPD connections"); + IsConnected = false; + ConnectionChanged?.Invoke(this, new EventArgs()); + } + + // Stop the idle connection first + _cancelIdle?.Cancel(); _connectionRetryAttempter?.Stop(); _connectionRetryAttempter?.Dispose(); + // Stop the status timer before killing the matching connection _statusUpdater?.Stop(); _statusUpdater?.Dispose(); + _statusConnection?.DisconnectAsync(); - _cancelIdle?.Cancel(); _cancelIdle = new CancellationTokenSource(); _cancelConnect?.Cancel(); @@ -270,15 +272,25 @@ private void InitializeStatusUpdater(CancellationToken token = default) try { + // Run the idleConnection in a wrapper task since MpcNET isn't fully async and will block here + var idleChangesTask = Task.Run(async () => await _idleConnection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options update"))); + + // Wait for the idle command to finish or for the token to be cancelled + await Task.WhenAny(idleChangesTask, Task.Delay(-1, token)); + if (token.IsCancellationRequested || _idleConnection == null || !_idleConnection.IsConnected) + { + //_idleConnection?.SendAsync(new NoIdleCommand()); + _idleConnection?.DisconnectAsync(); break; + } + + var message = idleChangesTask.Result; - var idleChanges = await _idleConnection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options update")); - - if (idleChanges.IsResponseValid) - await HandleIdleResponseAsync(idleChanges.Response.Content); + if (message.IsResponseValid) + await HandleIdleResponseAsync(message.Response.Content); else - throw new Exception(idleChanges.Response?.Content); + throw new Exception(message.Response?.Content); } catch (Exception e) { diff --git a/Sources/Stylophone.Common/ViewModels/Bases/PlaybackViewModelBase.cs b/Sources/Stylophone.Common/ViewModels/Bases/PlaybackViewModelBase.cs index d717b56..77160da 100644 --- a/Sources/Stylophone.Common/ViewModels/Bases/PlaybackViewModelBase.cs +++ b/Sources/Stylophone.Common/ViewModels/Bases/PlaybackViewModelBase.cs @@ -61,10 +61,9 @@ public PlaybackViewModelBase(INavigationService navigationService, INotification _internalVolume = _mpdService.CurrentStatus.Volume; - // Bind timer methods and start it + // Bind timer methods _updateInformationTimer = new System.Timers.Timer(500); _updateInformationTimer.Elapsed += UpdateInformation; - _updateInformationTimer.Start(); // Update info to current track _mpdService.ConnectionChanged += OnConnectionChanged; @@ -82,6 +81,7 @@ private void OnConnectionChanged(object sender, EventArgs e) else { IsTrackInfoAvailable = false; + _updateInformationTimer?.Stop(); } } @@ -90,6 +90,7 @@ private void Initialize() OnTrackChange(this, new SongChangedEventArgs { NewSongId = -1 }); CurrentTimeValue = _mpdService.CurrentStatus.Elapsed.TotalSeconds; + _updateInformationTimer.Start(); OnStateChange(this, null); } diff --git a/Sources/Stylophone/App.xaml.cs b/Sources/Stylophone/App.xaml.cs index 04fafed..4e675b1 100644 --- a/Sources/Stylophone/App.xaml.cs +++ b/Sources/Stylophone/App.xaml.cs @@ -16,6 +16,7 @@ using Microsoft.Toolkit.Uwp.Helpers; using Windows.Foundation; using Microsoft.Services.Store.Engagement; +using Windows.ApplicationModel; #if DEBUG #else using System.Collections.Generic; @@ -40,6 +41,8 @@ public App() InitializeComponent(); UnhandledException += OnAppUnhandledException; + Suspending += OnAppSuspending; + Resuming += OnAppResuming; // Deferred execution until used. Check https://msdn.microsoft.com/library/dd642331(v=vs.110).aspx for further info on Lazy class. _activationService = new Lazy(CreateActivationService); @@ -119,6 +122,18 @@ private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExc e.Handled = true; } + private async void OnAppResuming(object sender, object e) + { + await Ioc.Default.GetRequiredService().InitializeAsync(true); + Ioc.Default.GetRequiredService().Initialize(); + } + + private void OnAppSuspending(object sender, SuspendingEventArgs e) + { + Ioc.Default.GetRequiredService().Disconnect(); + Ioc.Default.GetRequiredService().Stop(); + } + private ActivationService CreateActivationService() { return new ActivationService(this, typeof(QueueViewModel), new Lazy(CreateShell)); From cc25b3af852ec60c69ba9836f8484ed9c451ec23 Mon Sep 17 00:00:00 2001 From: Difegue Date: Sat, 26 Nov 2022 02:52:03 +0100 Subject: [PATCH 05/56] Rework queue again + fix small bugs --- .../ViewModels/QueueViewModel.cs | 7 +++--- .../ViewModels/SettingsViewModel.cs | 2 +- .../Stylophone/Views/ServerQueuePage.xaml.cs | 22 +++++++++---------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs b/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs index 4a9b223..6aad672 100644 --- a/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/QueueViewModel.cs @@ -211,11 +211,10 @@ private async void MPDConnectionService_QueueChanged(object sender, EventArgs e) // If the response is empty, that means the last file in the source was removed. var initialPosition = response.Count() == 0 ? Source.Count - 1 : response.First().Position; + // Get all the tracks between initialPosition and the end of the Source + var tracksToRemove = Source.Skip(initialPosition).ToList(); await _dispatcherService.ExecuteOnUIThreadAsync(() => { - while (Source.Count > initialPosition) - { - Source.RemoveAt(initialPosition); - } + Source.RemoveRange(tracksToRemove); }); var toAdd = new List(); diff --git a/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs b/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs index 8f4e2f6..85f2852 100644 --- a/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs +++ b/Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs @@ -238,7 +238,7 @@ private async Task UpdateServerVersionAsync() var songs = response.ContainsKey("songs") ? response["songs"] : "??"; var albums = response.ContainsKey("albums") ? response["albums"] : "??"; - if (outputs != null) + if (outputs != null && outputs.Count() > 0) { var outputString = outputs.Select(o => o.Plugin).Aggregate((s, s2) => $"{s}, {s2}"); diff --git a/Sources/Stylophone/Views/ServerQueuePage.xaml.cs b/Sources/Stylophone/Views/ServerQueuePage.xaml.cs index 3004bb0..79d6da7 100644 --- a/Sources/Stylophone/Views/ServerQueuePage.xaml.cs +++ b/Sources/Stylophone/Views/ServerQueuePage.xaml.cs @@ -7,6 +7,7 @@ using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; +using System.Collections.Specialized; namespace Stylophone.Views { @@ -31,7 +32,7 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); - ViewModel.PropertyChanged += ViewModel_PropertyChanged; + ViewModel.Source.CollectionChanged += ScrollToPlayingSong; _mpdService.SongChanged += MPDConnectionService_SongChanged; @@ -58,19 +59,16 @@ private void MPDConnectionService_SongChanged(object sender, SongChangedEventArg }); } - private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + private void ScrollToPlayingSong(object sender, NotifyCollectionChangedEventArgs e) { - if (e.PropertyName == nameof(ViewModel.Source)) - { - if (QueueList.Items.Count == 0) - return; + if (QueueList.Items.Count == 0) + return; - var playing = ViewModel.Source.Where(t => t.IsPlaying && t.File.Id != manualSongId).FirstOrDefault(); - if (playing != null) - { - playing.UpdatePlayingStatus(); - QueueList.ScrollIntoView(playing, ScrollIntoViewAlignment.Leading); - } + var playing = ViewModel.Source.Where(t => t.IsPlaying && t.File.Id != manualSongId).FirstOrDefault(); + if (playing != null) + { + playing.UpdatePlayingStatus(); + QueueList.ScrollIntoView(playing, ScrollIntoViewAlignment.Leading); } } From 4121f2241480cd03530dbcb07f27d53ed0e9d207 Mon Sep 17 00:00:00 2001 From: Difegue Date: Tue, 29 Nov 2022 21:29:55 +0100 Subject: [PATCH 06/56] Better MPD error tracking --- .../Stylophone.Common/Services/MPDConnectionService.cs | 8 ++++++-- Sources/Stylophone/Package.tt | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Sources/Stylophone.Common/Services/MPDConnectionService.cs b/Sources/Stylophone.Common/Services/MPDConnectionService.cs index 8666aa2..c038388 100644 --- a/Sources/Stylophone.Common/Services/MPDConnectionService.cs +++ b/Sources/Stylophone.Common/Services/MPDConnectionService.cs @@ -225,8 +225,12 @@ public async Task SafelySendCommandAsync(IMpcCommand command, bool show { var dict = new Dictionary(); dict.Add("command", command.Serialize()); - dict.Add("exception", e.ToString()); - Analytics.TrackEvent("MPDError", dict); + dict.Add("exception", e.InnerException?.ToString()); + dict.Add("source", e.Source); + dict.Add("message", e.Message); + dict.Add("stacktrace", e.StackTrace); + + Analytics.TrackEvent("MPDError", dict); } #endif } diff --git a/Sources/Stylophone/Package.tt b/Sources/Stylophone/Package.tt index 65a9650..8060ae6 100644 --- a/Sources/Stylophone/Package.tt +++ b/Sources/Stylophone/Package.tt @@ -2,7 +2,7 @@ <#@ output extension=".appxmanifest" #> <#@ parameter type="System.String" name="BuildConfiguration" #> <# - string version = "2.5.5.0"; + string version = "2.5.6.0"; // Get configuration name at Build time string configName = Host.ResolveParameterValue("-", "-", "BuildConfiguration"); From 105d18432360d04a051a8c710eb2be19772038b0 Mon Sep 17 00:00:00 2001 From: Difegue Date: Tue, 14 Mar 2023 21:10:36 +0100 Subject: [PATCH 07/56] cleaner LocalizedLabel implementation --- Sources/Stylophone.iOS/Helpers/LocalizedLabel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Stylophone.iOS/Helpers/LocalizedLabel.cs b/Sources/Stylophone.iOS/Helpers/LocalizedLabel.cs index 34151f9..9825ae3 100644 --- a/Sources/Stylophone.iOS/Helpers/LocalizedLabel.cs +++ b/Sources/Stylophone.iOS/Helpers/LocalizedLabel.cs @@ -26,10 +26,10 @@ public override void AwakeFromNib() // Use the text set in IB to find the matching property. // Set the identifier in "User Defined Runtime Attributes". - var prop = typeof(Resources).GetProperty(stringIdentifier ?? "AppDisplayName"); + var identifier = stringIdentifier ?? "AppDisplayName"; // Get the property value to have the localized string. - Text = prop.GetValue(null, null).ToString(); + Text = Resources.ResourceManager.GetString(identifier); } } } From 596f8c45880ed9007d8766a990076d0e160ab135 Mon Sep 17 00:00:00 2001 From: Difegue Date: Mon, 17 Apr 2023 20:56:41 +0200 Subject: [PATCH 08/56] Try/catch potential NRE in doubletap to play (from appcenter) --- Sources/Stylophone/Package.appxmanifest | 2 +- .../Stylophone/Views/ServerQueuePage.xaml.cs | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Sources/Stylophone/Package.appxmanifest b/Sources/Stylophone/Package.appxmanifest index 402c962..a5a1ff2 100644 --- a/Sources/Stylophone/Package.appxmanifest +++ b/Sources/Stylophone/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="2.5.6.0" /> diff --git a/Sources/Stylophone/Views/ServerQueuePage.xaml.cs b/Sources/Stylophone/Views/ServerQueuePage.xaml.cs index 79d6da7..4b27f81 100644 --- a/Sources/Stylophone/Views/ServerQueuePage.xaml.cs +++ b/Sources/Stylophone/Views/ServerQueuePage.xaml.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Stylophone.Helpers; using CommunityToolkit.Mvvm.DependencyInjection; using Stylophone.Common.Interfaces; @@ -74,13 +75,20 @@ private void ScrollToPlayingSong(object sender, NotifyCollectionChangedEventArgs private void Play_Track(object sender, RoutedEventArgs e) { - var listView = sender as ListView; - var trackVm = listView.SelectedItem as TrackViewModel; - // Set this ID as manually played by the user to prevent unnecessary autoscrolling. - // Kind of a duct tape fix for now - // TODO: Apply to context menu as well, maybe main playbar buttons if the queue is showing? - manualSongId = trackVm.File.Id; - trackVm.PlayTrackCommand.Execute(trackVm.File); + try + { + var listView = sender as ListView; + var trackVm = listView.SelectedItem as TrackViewModel; + // Set this ID as manually played by the user to prevent unnecessary autoscrolling. + // Kind of a duct tape fix for now + // TODO: Apply to context menu as well, maybe main playbar buttons if the queue is showing? + manualSongId = trackVm.File.Id; + trackVm.PlayTrackCommand.Execute(trackVm.File); + } + catch (Exception ex) + { + Ioc.Default.GetRequiredService().ShowErrorNotification(ex); + } } private void Select_Item(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e) => UWPHelpers.SelectItemOnFlyoutRightClick(QueueList, e); From 99045fd9d5a6a7fff6d5cc259a2286cdaa99c9ae Mon Sep 17 00:00:00 2001 From: Difegue Date: Fri, 16 Jun 2023 01:33:30 +0200 Subject: [PATCH 09/56] (#75) Fix potential failure in GetColor crashing the album display --- Sources/Stylophone.Common/Helpers/ColorThief.Skia.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/Stylophone.Common/Helpers/ColorThief.Skia.cs b/Sources/Stylophone.Common/Helpers/ColorThief.Skia.cs index c1ca7f7..72e3e67 100644 --- a/Sources/Stylophone.Common/Helpers/ColorThief.Skia.cs +++ b/Sources/Stylophone.Common/Helpers/ColorThief.Skia.cs @@ -93,6 +93,12 @@ public QuantizedColor GetColor(SKBitmap sourceImage, int quality = DefaultQualit { var palette = GetPalette(sourceImage, 3, quality, ignoreWhite); + // Handle case where GetPalette returns an empty list (because GetColorMap failed?) + if (palette.Count == 0) + { + return new QuantizedColor(SKColors.Black, 1); + } + var avgR = Convert.ToByte(palette.Average(a => a.Color.Red)); var avgG = Convert.ToByte(palette.Average(a => a.Color.Green)); var avgB = Convert.ToByte(palette.Average(a => a.Color.Blue)); From c957a84bdd87f803b4a9d4ea090fb99c933edc4b Mon Sep 17 00:00:00 2001 From: Difegue Date: Mon, 11 Sep 2023 23:40:10 +0200 Subject: [PATCH 10/56] Migrate to Toolkit v8 --- .../Services/MPDConnectionService.cs | 5 - .../Stylophone.Common.csproj | 8 +- Sources/Stylophone.iOS/Stylophone.iOS.csproj | 6 +- .../Behaviors/StackedNotificationsBehavior.cs | 322 ------------------ .../Controls/SettingsBlockControl.xaml | 2 +- .../Controls/SettingsDisplayControl.xaml | 2 +- Sources/Stylophone/Services/DialogService.cs | 2 +- .../Stylophone/Services/DispatcherService.cs | 3 +- .../Stylophone/Services/NavigationService.cs | 2 +- .../Services/NotificationService.cs | 5 +- .../Services/SystemMediaControlsService.cs | 3 +- Sources/Stylophone/Styles/StyloResources.xaml | 5 +- Sources/Stylophone/Stylophone.csproj | 44 +-- .../ViewModels/PlaybackViewModel.cs | 2 +- .../Stylophone/ViewModels/ShellViewModel.cs | 2 +- Sources/Stylophone/Views/FoldersPage.xaml | 4 +- .../Stylophone/Views/LibraryDetailPage.xaml | 15 +- .../Views/LibraryDetailPage.xaml.cs | 2 +- Sources/Stylophone/Views/LibraryPage.xaml | 17 +- .../Views/Playback/NowPlayingBar.xaml | 15 +- .../Views/Playback/OverlayView.xaml | 2 +- .../Views/Playback/OverlayView.xaml.cs | 2 +- .../Views/Playback/PlaybackView.xaml | 15 +- Sources/Stylophone/Views/PlaylistPage.xaml | 45 +-- .../Stylophone/Views/SearchResultsPage.xaml | 2 +- Sources/Stylophone/Views/ServerQueuePage.xaml | 4 +- Sources/Stylophone/Views/SettingsPage.xaml | 2 +- Sources/Stylophone/Views/ShellPage.xaml | 6 +- 28 files changed, 85 insertions(+), 459 deletions(-) delete mode 100644 Sources/Stylophone/Behaviors/StackedNotificationsBehavior.cs diff --git a/Sources/Stylophone.Common/Services/MPDConnectionService.cs b/Sources/Stylophone.Common/Services/MPDConnectionService.cs index c038388..475f67b 100644 --- a/Sources/Stylophone.Common/Services/MPDConnectionService.cs +++ b/Sources/Stylophone.Common/Services/MPDConnectionService.cs @@ -11,11 +11,6 @@ using Stylophone.Common.Interfaces; using MpcNET.Commands.Reflection; using Stylophone.Localization.Strings; -using CommunityToolkit.Mvvm.DependencyInjection; -using Stylophone.Common.ViewModels; -using Microsoft.AppCenter.Analytics; -using Microsoft.AppCenter; -using System.Drawing; namespace Stylophone.Common.Services { diff --git a/Sources/Stylophone.Common/Stylophone.Common.csproj b/Sources/Stylophone.Common/Stylophone.Common.csproj index 84d56ce..acff074 100644 --- a/Sources/Stylophone.Common/Stylophone.Common.csproj +++ b/Sources/Stylophone.Common/Stylophone.Common.csproj @@ -7,14 +7,14 @@ - - + + - + - + diff --git a/Sources/Stylophone.iOS/Stylophone.iOS.csproj b/Sources/Stylophone.iOS/Stylophone.iOS.csproj index 87fcb18..d959dd3 100644 --- a/Sources/Stylophone.iOS/Stylophone.iOS.csproj +++ b/Sources/Stylophone.iOS/Stylophone.iOS.csproj @@ -216,13 +216,13 @@ 2.88.1 - 6.0.0 + 7.0.0 - 4.5.3 + 5.0.2 - 4.5.3 + 5.0.2 0.0.1 diff --git a/Sources/Stylophone/Behaviors/StackedNotificationsBehavior.cs b/Sources/Stylophone/Behaviors/StackedNotificationsBehavior.cs deleted file mode 100644 index af7efe3..0000000 --- a/Sources/Stylophone/Behaviors/StackedNotificationsBehavior.cs +++ /dev/null @@ -1,322 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// TODO: Remove this when StackedNotificationsBehavior lands in Toolkit - -using Microsoft.Toolkit.Uwp.UI.Behaviors; -using Microsoft.UI.Xaml.Controls; -using System; -using System.Collections.Generic; -using Windows.System; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Input; -using DQ = Windows.System.DispatcherQueue; - -namespace CommunityToolkit.Labs.WinUI -{ - /// - /// The content of a notification to display in . - /// The , , and values will - /// always be applied to the targeted . - /// The , , and values - /// will be applied only if set. - /// - public class Notification - { - private NotificationOverrides _overrides; - private bool _isIconVisible; - private object? _content; - private DataTemplate? _contentTemplate; - private ButtonBase? _actionButton; - - /// - /// Gets or sets the notification title. - /// - public string? Title { get; set; } - - /// - /// Gets or sets the notification message. - /// - public string? Message { get; set; } - - /// - /// Gets or sets the duration of the notification. - /// Set to null for persistent notification. - /// - public TimeSpan? Duration { get; set; } - - /// - /// Gets or sets the type of the to apply consistent status color, icon, - /// and assistive technology settings dependent on the criticality of the notification. - /// - public InfoBarSeverity Severity { get; set; } - - /// - /// Gets or sets a value indicating whether the icon is visible or not. - /// True if the icon is visible; otherwise, false. The default is true. - /// - public bool IsIconVisible - { - get => _isIconVisible; - set - { - _isIconVisible = value; - _overrides |= NotificationOverrides.Icon; - } - } - - /// - /// Gets or sets the XAML Content that is displayed below the title and message in - /// the InfoBar. - /// - public object? Content - { - get => _content; - set - { - _content = value; - _overrides |= NotificationOverrides.Content; - } - } - - /// - /// Gets or sets the data template for the . - /// - public DataTemplate? ContentTemplate - { - get => _contentTemplate; - set - { - _contentTemplate = value; - _overrides |= NotificationOverrides.ContentTemplate; - } - } - - /// - /// Gets or sets the action button of the InfoBar. - /// - public ButtonBase? ActionButton - { - get => _actionButton; - set - { - _actionButton = value; - _overrides |= NotificationOverrides.ActionButton; - } - } - - internal NotificationOverrides Overrides => _overrides; - } - - /// - /// The overrides which should be set on the notification. - /// - [Flags] - internal enum NotificationOverrides - { - None, - Icon, - Content, - ContentTemplate, - ActionButton, - } - - /// - /// A behavior to add the stacked notification support to . - /// - public class StackedNotificationsBehavior : BehaviorBase - { - private readonly LinkedList _stackedNotifications; - private readonly DispatcherQueueTimer _dismissTimer; - private Notification? _currentNotification; - private bool _initialIconVisible; - private object? _initialContent; - private DataTemplate? _initialContentTemplate; - private ButtonBase? _initialActionButton; - - /// - /// Initializes a new instance of the class. - /// - public StackedNotificationsBehavior() - { - _stackedNotifications = new LinkedList(); - - // TODO: On WinUI 3 we can use the local DispatcherQueue, so we need to abstract better for UWP - var dispatcherQueue = DQ.GetForCurrentThread(); - _dismissTimer = dispatcherQueue.CreateTimer(); - _dismissTimer.Tick += OnTimerTick; - } - - /// - /// Show . - /// - /// The notification to display. - public void Show(Notification notification) - { - if (notification is null) - { - throw new ArgumentNullException(nameof(notification)); - } - - _stackedNotifications.AddLast(notification); - ShowNext(); - } - - /// - /// Remove the . - /// If the notification is displayed, it will be closed. - /// If the notification is still in the queue, it will be removed. - /// - /// The notification to remove. - public void Remove(Notification notification) - { - if (notification is null) - { - throw new ArgumentNullException(nameof(notification)); - } - - if (notification == _currentNotification) - { - // We close the notification. This will trigger the display of the next one. - // See OnInfoBarClosed. - AssociatedObject.IsOpen = false; - return; - } - - _stackedNotifications.Remove(notification); - } - - /// - protected override bool Initialize() - { - AssociatedObject.Closed += OnInfoBarClosed; - AssociatedObject.PointerEntered += OnPointerEntered; - AssociatedObject.PointerExited += OnPointedExited; - return true; - } - - /// - protected override bool Uninitialize() - { - AssociatedObject.Closed -= OnInfoBarClosed; - AssociatedObject.PointerEntered -= OnPointerEntered; - AssociatedObject.PointerExited -= OnPointedExited; - return true; - } - - private void OnInfoBarClosed(InfoBar sender, InfoBarClosedEventArgs args) - { - // We display the next notification. - ShowNext(); - } - - private void ShowNext() - { - if (AssociatedObject.IsOpen) - { - // One notification is already displayed. We wait for it to close - return; - } - - StopTimer(); - AssociatedObject.IsOpen = false; - RestoreOverridenProperties(); - - if (_stackedNotifications.Count == 0) - { - _currentNotification = null; - return; - } - - var notificationToDisplay = _stackedNotifications!.First!.Value; - _stackedNotifications.RemoveFirst(); - - _currentNotification = notificationToDisplay; - SetNotification(notificationToDisplay); - AssociatedObject.IsOpen = true; - - StartTimer(notificationToDisplay.Duration); - } - - private void SetNotification(Notification notification) - { - AssociatedObject.Title = notification.Title ?? string.Empty; - AssociatedObject.Message = notification.Message ?? string.Empty; - AssociatedObject.Severity = notification.Severity; - - if (notification.Overrides.HasFlag(NotificationOverrides.Icon)) - { - _initialIconVisible = AssociatedObject.IsIconVisible; - AssociatedObject.IsIconVisible = notification.IsIconVisible; - } - - if (notification.Overrides.HasFlag(NotificationOverrides.Content)) - { - _initialContent = AssociatedObject.Content; - AssociatedObject.Content = notification.Content!; - } - - if (notification.Overrides.HasFlag(NotificationOverrides.ContentTemplate)) - { - _initialContentTemplate = AssociatedObject.ContentTemplate; - AssociatedObject.ContentTemplate = notification.ContentTemplate!; - } - - if (notification.Overrides.HasFlag(NotificationOverrides.ActionButton)) - { - _initialActionButton = AssociatedObject.ActionButton; - AssociatedObject.ActionButton = notification.ActionButton!; - } - } - - private void RestoreOverridenProperties() - { - if (_currentNotification is null) - { - return; - } - - if (_currentNotification.Overrides.HasFlag(NotificationOverrides.Icon)) - { - AssociatedObject.IsIconVisible = _initialIconVisible; - } - - if (_currentNotification.Overrides.HasFlag(NotificationOverrides.Content)) - { - AssociatedObject.Content = _initialContent!; - } - - if (_currentNotification.Overrides.HasFlag(NotificationOverrides.ContentTemplate)) - { - AssociatedObject.ContentTemplate = _initialContentTemplate!; - } - - if (_currentNotification.Overrides.HasFlag(NotificationOverrides.ActionButton)) - { - AssociatedObject.ActionButton = _initialActionButton!; - } - } - - private void StartTimer(TimeSpan? duration) - { - if (duration is null) - { - return; - } - - _dismissTimer.Interval = duration.Value; - _dismissTimer.Start(); - } - - private void StopTimer() => _dismissTimer.Stop(); - - private void OnTimerTick(DispatcherQueueTimer sender, object args) => AssociatedObject.IsOpen = false; - - private void OnPointedExited(object sender, PointerRoutedEventArgs e) => StartTimer(_currentNotification?.Duration); - - private void OnPointerEntered(object sender, PointerRoutedEventArgs e) => StopTimer(); - } - -} - diff --git a/Sources/Stylophone/Controls/SettingsBlockControl.xaml b/Sources/Stylophone/Controls/SettingsBlockControl.xaml index 1f58b7e..d59f9e0 100644 --- a/Sources/Stylophone/Controls/SettingsBlockControl.xaml +++ b/Sources/Stylophone/Controls/SettingsBlockControl.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Stylophone.Controls" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:muxc="using:Microsoft.UI.Xaml.Controls" diff --git a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml b/Sources/Stylophone/Controls/SettingsDisplayControl.xaml index b8d4a26..8929217 100644 --- a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml +++ b/Sources/Stylophone/Controls/SettingsDisplayControl.xaml @@ -2,7 +2,7 @@ x:Class="Stylophone.Controls.SettingsDisplayControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="300" diff --git a/Sources/Stylophone/Services/DialogService.cs b/Sources/Stylophone/Services/DialogService.cs index fd78844..ffbf5a1 100644 --- a/Sources/Stylophone/Services/DialogService.cs +++ b/Sources/Stylophone/Services/DialogService.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Stylophone.Views; -using Microsoft.Toolkit.Uwp.Helpers; using Stylophone.Common.Interfaces; using Stylophone.Common.Services; using Stylophone.Common.ViewModels; @@ -12,6 +11,7 @@ using Windows.Services.Store; using Windows.UI.Xaml.Media; using System.Linq; +using Microsoft.Toolkit.Uwp.Helpers; namespace Stylophone.Services { diff --git a/Sources/Stylophone/Services/DispatcherService.cs b/Sources/Stylophone/Services/DispatcherService.cs index fa5c076..054b100 100644 --- a/Sources/Stylophone/Services/DispatcherService.cs +++ b/Sources/Stylophone/Services/DispatcherService.cs @@ -1,4 +1,5 @@ -using Microsoft.Toolkit.Uwp; +using CommunityToolkit; +using CommunityToolkit.WinUI; using Stylophone.Common.Interfaces; using System; using System.Threading.Tasks; diff --git a/Sources/Stylophone/Services/NavigationService.cs b/Sources/Stylophone/Services/NavigationService.cs index 43e4976..5bf1737 100644 --- a/Sources/Stylophone/Services/NavigationService.cs +++ b/Sources/Stylophone/Services/NavigationService.cs @@ -1,7 +1,7 @@ using Stylophone.ViewModels; using Stylophone.Views; using CommunityToolkit.Mvvm.ComponentModel; -using Microsoft.Toolkit.Uwp.UI.Animations; +using CommunityToolkit.WinUI.Animations; using Stylophone.Common.Interfaces; using Stylophone.Common.ViewModels; using System; diff --git a/Sources/Stylophone/Services/NotificationService.cs b/Sources/Stylophone/Services/NotificationService.cs index ddec931..311e652 100644 --- a/Sources/Stylophone/Services/NotificationService.cs +++ b/Sources/Stylophone/Services/NotificationService.cs @@ -1,5 +1,4 @@ using System; -using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.Toolkit.Uwp.Notifications; using Windows.UI.Notifications; using Stylophone.Common.Interfaces; @@ -21,7 +20,7 @@ public override void ShowBasicToastNotification(string title, string description // Create the toast content var content = new ToastContent() { - // More about the Launch property at https://docs.microsoft.com/dotnet/api/microsoft.toolkit.uwp.notifications.toastcontent + // More about the Launch property at https://docs.microsoft.com/dotnet/api/CommunityToolkit.notifications.toastcontent Launch = "ToastContentActivationParams", Visual = new ToastVisual() @@ -47,7 +46,7 @@ public override void ShowBasicToastNotification(string title, string description { Buttons = { - // More about Toast Buttons at https://docs.microsoft.com/dotnet/api/microsoft.toolkit.uwp.notifications.toastbutton + // More about Toast Buttons at https://docs.microsoft.com/dotnet/api/CommunityToolkit.notifications.toastbutton new ToastButton("OK", "ToastButtonActivationArguments") { ActivationType = ToastActivationType.Foreground diff --git a/Sources/Stylophone/Services/SystemMediaControlsService.cs b/Sources/Stylophone/Services/SystemMediaControlsService.cs index 2d501e1..a4ad2d5 100644 --- a/Sources/Stylophone/Services/SystemMediaControlsService.cs +++ b/Sources/Stylophone/Services/SystemMediaControlsService.cs @@ -1,7 +1,5 @@ using System; using System.Threading.Tasks; -using Stylophone.Helpers; -using Microsoft.Toolkit.Uwp.Helpers; using MpcNET; using MpcNET.Commands.Playback; using Stylophone.Common.Helpers; @@ -10,6 +8,7 @@ using Windows.Media; using Windows.Storage; using Windows.Storage.Streams; +using Microsoft.Toolkit.Uwp.Helpers; namespace Stylophone.Services { diff --git a/Sources/Stylophone/Styles/StyloResources.xaml b/Sources/Stylophone/Styles/StyloResources.xaml index aa34e0c..ed16f68 100644 --- a/Sources/Stylophone/Styles/StyloResources.xaml +++ b/Sources/Stylophone/Styles/StyloResources.xaml @@ -3,10 +3,11 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Stylophone="using:Stylophone.Helpers" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="using:Stylophone.Common.ViewModels" + xmlns:media="using:CommunityToolkit.WinUI.Media" mc:Ignorable="d"> @@ -30,7 +31,7 @@ - + diff --git a/Sources/Stylophone/Stylophone.csproj b/Sources/Stylophone/Stylophone.csproj index 89186d5..93f2d3a 100644 --- a/Sources/Stylophone/Stylophone.csproj +++ b/Sources/Stylophone/Stylophone.csproj @@ -139,36 +139,27 @@ PackageReference - - 4.5.3 - - - 4.5.3 - - - 6.0.0 - + + + 6.2.14 10.1901.28001 - - 7.1.2 - - - 7.1.2 - - - 7.1.2 - - - 7.1.2 - - - 7.1.2 - + + + + + + + + + + + + 2.7.3 @@ -181,9 +172,7 @@ 2.88.1 - - 3.3.2 - + 1.1.0 @@ -194,7 +183,6 @@ - SettingsBlockControl.xaml diff --git a/Sources/Stylophone/ViewModels/PlaybackViewModel.cs b/Sources/Stylophone/ViewModels/PlaybackViewModel.cs index c6eecac..2d7b62c 100644 --- a/Sources/Stylophone/ViewModels/PlaybackViewModel.cs +++ b/Sources/Stylophone/ViewModels/PlaybackViewModel.cs @@ -1,6 +1,6 @@ using Stylophone.Services; using Stylophone.Views; -using Microsoft.Toolkit.Uwp; +using CommunityToolkit; using Stylophone.Common.Interfaces; using Stylophone.Common.Services; using Stylophone.Common.ViewModels; diff --git a/Sources/Stylophone/ViewModels/ShellViewModel.cs b/Sources/Stylophone/ViewModels/ShellViewModel.cs index 3f7ec30..15d0e32 100644 --- a/Sources/Stylophone/ViewModels/ShellViewModel.cs +++ b/Sources/Stylophone/ViewModels/ShellViewModel.cs @@ -14,9 +14,9 @@ using Stylophone.Services; using Windows.Foundation; using MpcNET.Commands.Playback; -using CommunityToolkit.Labs.WinUI; using CommunityToolkit.Mvvm.Messaging; using Microsoft.UI.Xaml.Controls; +using CommunityToolkit.WinUI.Behaviors; namespace Stylophone.ViewModels { diff --git a/Sources/Stylophone/Views/FoldersPage.xaml b/Sources/Stylophone/Views/FoldersPage.xaml index 01cc0c8..804a7f6 100644 --- a/Sources/Stylophone/Views/FoldersPage.xaml +++ b/Sources/Stylophone/Views/FoldersPage.xaml @@ -3,12 +3,12 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:behaviors="using:Stylophone.Behaviors" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:vm="using:Stylophone.Common.ViewModels" xmlns:winui="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> diff --git a/Sources/Stylophone/Views/LibraryDetailPage.xaml b/Sources/Stylophone/Views/LibraryDetailPage.xaml index 4506db2..61dab23 100644 --- a/Sources/Stylophone/Views/LibraryDetailPage.xaml +++ b/Sources/Stylophone/Views/LibraryDetailPage.xaml @@ -2,12 +2,12 @@ x:Class="Stylophone.Views.LibraryDetailPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" + xmlns:controls="using:CommunityToolkit.WinUI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" xmlns:stylophone="using:Stylophone.Helpers" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:winui="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> @@ -57,18 +57,15 @@ - + ui:Effects.Shadow="{StaticResource CommonShadow}" + > - + - + ui:Effects.Shadow="{StaticResource CommonShadow}"> @@ -96,7 +93,7 @@ - + diff --git a/Sources/Stylophone/Views/Playback/NowPlayingBar.xaml b/Sources/Stylophone/Views/Playback/NowPlayingBar.xaml index 0265fe1..00ee6c7 100644 --- a/Sources/Stylophone/Views/Playback/NowPlayingBar.xaml +++ b/Sources/Stylophone/Views/Playback/NowPlayingBar.xaml @@ -3,11 +3,11 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WindowsStateTriggers="using:WindowsStateTriggers" - xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:controls="using:CommunityToolkit.WinUI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:strings="using:Stylophone.Localization.Strings" d:DesignHeight="96" mc:Ignorable="d"> @@ -115,16 +115,13 @@ - + ui:Effects.Shadow="{StaticResource CommonShadow}"> - + - + ui:Effects.Shadow="{StaticResource CommonShadow}"> - + @@ -57,22 +57,16 @@ - + ui:Effects.Shadow="{StaticResource CommonShadow}"> - + - + ui:Effects.Shadow="{StaticResource CommonShadow}"> - + - + ui:Effects.Shadow="{StaticResource CommonShadow}"> - + - - - - - diff --git a/Sources/Stylophone/Views/SearchResultsPage.xaml b/Sources/Stylophone/Views/SearchResultsPage.xaml index 08eb1c4..636c958 100644 --- a/Sources/Stylophone/Views/SearchResultsPage.xaml +++ b/Sources/Stylophone/Views/SearchResultsPage.xaml @@ -6,7 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:winui="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> diff --git a/Sources/Stylophone/Views/ServerQueuePage.xaml b/Sources/Stylophone/Views/ServerQueuePage.xaml index c17b827..dcdd0ff 100644 --- a/Sources/Stylophone/Views/ServerQueuePage.xaml +++ b/Sources/Stylophone/Views/ServerQueuePage.xaml @@ -3,11 +3,11 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Stylophone="using:Stylophone.Helpers" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" - xmlns:ui="using:Microsoft.Toolkit.Uwp.UI" + xmlns:ui="using:CommunityToolkit.WinUI" xmlns:vm="using:Stylophone.Common.ViewModels" NavigationCacheMode="Required" mc:Ignorable="d"> diff --git a/Sources/Stylophone/Views/SettingsPage.xaml b/Sources/Stylophone/Views/SettingsPage.xaml index a6e1384..be6ecb8 100644 --- a/Sources/Stylophone/Views/SettingsPage.xaml +++ b/Sources/Stylophone/Views/SettingsPage.xaml @@ -3,7 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="using:Microsoft.UI.Xaml.Controls" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:Stylophone.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" diff --git a/Sources/Stylophone/Views/ShellPage.xaml b/Sources/Stylophone/Views/ShellPage.xaml index 5ab3dd5..cc3de41 100644 --- a/Sources/Stylophone/Views/ShellPage.xaml +++ b/Sources/Stylophone/Views/ShellPage.xaml @@ -2,8 +2,8 @@ x:Class="Stylophone.Views.ShellPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" - xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters" + xmlns:controls="using:CommunityToolkit.WinUI.Converters" + xmlns:converters="using:CommunityToolkit.WinUI.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="using:Stylophone.Helpers" xmlns:i="using:Microsoft.Xaml.Interactivity" @@ -14,7 +14,7 @@ xmlns:strings="using:Stylophone.Localization.Strings" xmlns:stylophone="using:Stylophone.Common.ViewModels" xmlns:viewmodels="using:Stylophone.ViewModels" - xmlns:views="using:Stylophone.Views" xmlns:behaviors="using:CommunityToolkit.Labs.WinUI" + xmlns:views="using:Stylophone.Views" xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors" muxc:BackdropMaterial.ApplyToRootOrPageBackground="True" PreviewKeyDown="GlobalPlayPauseShortcut" mc:Ignorable="d"> From ae077a07c308b438a8f10cce5feb3aeaef61ad9f Mon Sep 17 00:00:00 2001 From: Difegue Date: Mon, 11 Sep 2023 23:57:54 +0200 Subject: [PATCH 11/56] Add unported MiddleClick extension + take advantage of the namespace mess to add hotfixed AlternateRows --- .../ListViewExtensions.AlternateRows.cs | 258 +++++++++++ ...llViewerExtensions.MiddleClickScrolling.cs | 400 ++++++++++++++++++ Sources/Stylophone/Stylophone.csproj | 14 +- .../Stylophone/Views/LibraryDetailPage.xaml | 5 +- Sources/Stylophone/Views/LibraryPage.xaml | 3 +- Sources/Stylophone/Views/PlaylistPage.xaml | 5 +- .../Stylophone/Views/SearchResultsPage.xaml | 5 +- Sources/Stylophone/Views/ServerQueuePage.xaml | 5 +- 8 files changed, 677 insertions(+), 18 deletions(-) create mode 100644 Sources/Stylophone/Helpers/ListViewExtensions.AlternateRows.cs create mode 100644 Sources/Stylophone/Helpers/ScrollViewerExtensions.MiddleClickScrolling.cs diff --git a/Sources/Stylophone/Helpers/ListViewExtensions.AlternateRows.cs b/Sources/Stylophone/Helpers/ListViewExtensions.AlternateRows.cs new file mode 100644 index 0000000..f10e557 --- /dev/null +++ b/Sources/Stylophone/Helpers/ListViewExtensions.AlternateRows.cs @@ -0,0 +1,258 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.WinUI; +using System.Collections.Generic; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI +{ + /// + /// Provides attached dependency properties for the + /// + public static partial class ListViewExtensions + { + /// + /// Attached for setting the container content stretch direction on the + /// + public static readonly DependencyProperty ItemContainerStretchDirectionProperty = DependencyProperty.RegisterAttached("ItemContainerStretchDirection", typeof(ItemContainerStretchDirection), typeof(ListViewExtensions), new PropertyMetadata(null, OnItemContainerStretchDirectionPropertyChanged)); + + /// + /// Gets the stretch associated with the specified + /// + /// The to get the associated from + /// The associated with the + public static ItemContainerStretchDirection GetItemContainerStretchDirection(Windows.UI.Xaml.Controls.ListViewBase obj) + { + return (ItemContainerStretchDirection)obj.GetValue(ItemContainerStretchDirectionProperty); + } + + /// + /// Sets the stretch associated with the specified + /// + /// The to associate the with + /// The for binding to the + public static void SetItemContainerStretchDirection(Windows.UI.Xaml.Controls.ListViewBase obj, ItemContainerStretchDirection value) + { + obj.SetValue(ItemContainerStretchDirectionProperty, value); + } + + private static Dictionary, Windows.UI.Xaml.Controls.ListViewBase> _itemsForList = new Dictionary, Windows.UI.Xaml.Controls.ListViewBase>(); + + /// + /// Attached for binding a as an alternate background color to a + /// + public static readonly DependencyProperty AlternateColorProperty = DependencyProperty.RegisterAttached("AlternateColor", typeof(Brush), typeof(ListViewExtensions), new PropertyMetadata(null, OnAlternateColorPropertyChanged)); + + /// + /// Attached for binding a as an alternate template to a + /// + public static readonly DependencyProperty AlternateItemTemplateProperty = DependencyProperty.RegisterAttached("AlternateItemTemplate", typeof(DataTemplate), typeof(ListViewExtensions), new PropertyMetadata(null, OnAlternateItemTemplatePropertyChanged)); + + /// + /// Gets the alternate associated with the specified + /// + /// The to get the associated from + /// The associated with the + public static Brush GetAlternateColor(Windows.UI.Xaml.Controls.ListViewBase obj) + { + return (Brush)obj.GetValue(AlternateColorProperty); + } + + /// + /// Sets the alternate associated with the specified + /// + /// The to associate the with + /// The for binding to the + public static void SetAlternateColor(Windows.UI.Xaml.Controls.ListViewBase obj, Brush value) + { + obj.SetValue(AlternateColorProperty, value); + } + + /// + /// Gets the associated with the specified + /// + /// The to get the associated from + /// The associated with the + public static DataTemplate GetAlternateItemTemplate(Windows.UI.Xaml.Controls.ListViewBase obj) + { + return (DataTemplate)obj.GetValue(AlternateItemTemplateProperty); + } + + /// + /// Sets the associated with the specified + /// + /// The to associate the with + /// The for binding to the + public static void SetAlternateItemTemplate(Windows.UI.Xaml.Controls.ListViewBase obj, DataTemplate value) + { + obj.SetValue(AlternateItemTemplateProperty, value); + } + + private static void OnAlternateColorPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; + + if (listViewBase == null) + { + return; + } + + listViewBase.ContainerContentChanging -= ColorContainerContentChanging; + listViewBase.Items.VectorChanged -= ColorItemsVectorChanged; + listViewBase.Unloaded -= OnListViewBaseUnloaded; + + _itemsForList[listViewBase.Items] = listViewBase; + if (AlternateColorProperty != null) + { + listViewBase.ContainerContentChanging += ColorContainerContentChanging; + listViewBase.Items.VectorChanged += ColorItemsVectorChanged; + listViewBase.Unloaded += OnListViewBaseUnloaded; + } + } + + private static void ColorContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) + { + var itemContainer = args.ItemContainer as Control; + SetItemContainerBackground(sender, itemContainer, args.ItemIndex); + } + + private static void OnAlternateItemTemplatePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; + + if (listViewBase == null) + { + return; + } + + listViewBase.ContainerContentChanging -= ItemTemplateContainerContentChanging; + listViewBase.Unloaded -= OnListViewBaseUnloaded; + + if (AlternateItemTemplateProperty != null) + { + listViewBase.ContainerContentChanging += ItemTemplateContainerContentChanging; + listViewBase.Unloaded += OnListViewBaseUnloaded; + } + } + + private static void ItemTemplateContainerContentChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) + { + var itemContainer = args.ItemContainer as SelectorItem; + + if (args.ItemIndex % 2 == 0) + { + itemContainer.ContentTemplate = GetAlternateItemTemplate(sender); + } + else + { + itemContainer.ContentTemplate = sender.ItemTemplate; + } + } + + private static void OnItemContainerStretchDirectionPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args) + { + Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; + + if (listViewBase == null) + { + return; + } + + listViewBase.ContainerContentChanging -= ItemContainerStretchDirectionChanging; + listViewBase.Unloaded -= OnListViewBaseUnloaded; + + if (ItemContainerStretchDirectionProperty != null) + { + listViewBase.ContainerContentChanging += ItemContainerStretchDirectionChanging; + listViewBase.Unloaded += OnListViewBaseUnloaded; + } + } + + private static void ItemContainerStretchDirectionChanging(Windows.UI.Xaml.Controls.ListViewBase sender, ContainerContentChangingEventArgs args) + { + var itemContainer = args.ItemContainer as SelectorItem; + var stretchDirection = GetItemContainerStretchDirection(sender); + + if (stretchDirection == ItemContainerStretchDirection.Vertical || stretchDirection == ItemContainerStretchDirection.Both) + { + itemContainer.VerticalContentAlignment = VerticalAlignment.Stretch; + } + + if (stretchDirection == ItemContainerStretchDirection.Horizontal || stretchDirection == ItemContainerStretchDirection.Both) + { + itemContainer.HorizontalContentAlignment = HorizontalAlignment.Stretch; + } + } + + private static void OnListViewBaseUnloaded(object sender, RoutedEventArgs e) + { + Windows.UI.Xaml.Controls.ListViewBase listViewBase = sender as Windows.UI.Xaml.Controls.ListViewBase; + _itemsForList.Remove(listViewBase.Items); + + listViewBase.ContainerContentChanging -= ItemContainerStretchDirectionChanging; + listViewBase.ContainerContentChanging -= ItemTemplateContainerContentChanging; + listViewBase.ContainerContentChanging -= ColorContainerContentChanging; + listViewBase.Items.VectorChanged -= ColorItemsVectorChanged; + listViewBase.Unloaded -= OnListViewBaseUnloaded; + } + + private static void ColorItemsVectorChanged(IObservableVector sender, IVectorChangedEventArgs args) + { + // If the index is at the end we can ignore + if (args.Index == (sender.Count - 1)) + { + return; + } + + // Only need to handle Inserted and Removed because we'll handle everything else in the + // ColorContainerContentChanging method + if ((args.CollectionChange == CollectionChange.ItemInserted) || (args.CollectionChange == CollectionChange.ItemRemoved)) + { + _itemsForList.TryGetValue(sender, out Windows.UI.Xaml.Controls.ListViewBase listViewBase); + if (listViewBase == null) + { + return; + } + + int index = (int)args.Index; + for (int i = index; i < sender.Count; i++) + { + var itemContainer = listViewBase.ContainerFromIndex(i) as Control; + if (itemContainer != null) + { + SetItemContainerBackground(listViewBase, itemContainer, i); + } + } + } + } + + private static void SetItemContainerBackground(Windows.UI.Xaml.Controls.ListViewBase sender, Control itemContainer, int itemIndex) + { + if (itemIndex % 2 == 0) + { + itemContainer.Background = GetAlternateColor(sender); + var rootBorder = itemContainer.FindDescendant(); + if (rootBorder != null) + { + rootBorder.Background = GetAlternateColor(sender); + } + } + else + { + itemContainer.Background = null; + + var rootBorder = itemContainer.FindDescendant(); + if (rootBorder != null) + { + rootBorder.Background = null; + } + } + } + } +} diff --git a/Sources/Stylophone/Helpers/ScrollViewerExtensions.MiddleClickScrolling.cs b/Sources/Stylophone/Helpers/ScrollViewerExtensions.MiddleClickScrolling.cs new file mode 100644 index 0000000..84e272a --- /dev/null +++ b/Sources/Stylophone/Helpers/ScrollViewerExtensions.MiddleClickScrolling.cs @@ -0,0 +1,400 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.WinUI; +using System; +using System.Threading; +using Windows.Devices.Input; +using Windows.Foundation; +using Windows.System; +using Windows.UI.Core; +using Windows.UI.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Input; + +namespace Microsoft.Toolkit.Uwp.UI +{ + + /// + /// Provides attached dependency properties and methods for the control. + /// + public partial class ScrollViewerExtensions + { +#pragma warning disable CS0419 // Ambiguous reference in cref attribute + + /// + /// Attached for enabling middle click scrolling + /// + public static readonly DependencyProperty EnableMiddleClickScrollingProperty = + DependencyProperty.RegisterAttached("EnableMiddleClickScrolling", typeof(bool), typeof(ScrollViewerExtensions), new PropertyMetadata(false, OnEnableMiddleClickScrollingChanged)); + + /// + /// Get . Returns `true` if middle click scrolling is enabled else return `false` + /// + /// The to get the associated `bool` + /// The `bool` associated with the + public static bool GetEnableMiddleClickScrolling(DependencyObject obj) + { + return (bool)obj.GetValue(EnableMiddleClickScrollingProperty); + } + + /// + /// Set . `true` to enable middle click scrolling + /// + /// The to associate the `bool` with + /// The `bool` for binding to the + public static void SetEnableMiddleClickScrolling(DependencyObject obj, bool value) + { + obj.SetValue(EnableMiddleClickScrollingProperty, value); + } +#pragma warning restore CS0419 // Ambiguous reference in cref attribute + } + + /// + /// Provides attached dependency properties and methods for the control. + /// + public static partial class ScrollViewerExtensions + { + private static double _threshold = 50; + private static bool _isPressed = false; + private static bool _isMoved = false; + private static Point _startPosition; + private static bool _isDeferredMovingStarted = false; + private static double _factor = 50; + private static Point _currentPosition; + private static Timer _timer; + private static ScrollViewer _scrollViewer; + private static uint _oldCursorID = 100; + private static uint _maxSpeed = 200; + private static bool _isCursorAvailable = false; + + /// + /// Function will be called when is updated + /// + /// Holds the dependency object + /// Holds the dependency object args + private static void OnEnableMiddleClickScrollingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is ScrollViewer scrollViewer) + { + _scrollViewer = scrollViewer; + } + else + { + _scrollViewer = (d as FrameworkElement).FindDescendant(); + + if (_scrollViewer == null) + { + (d as FrameworkElement).Loaded += (sender, arg) => + { + _scrollViewer = (sender as FrameworkElement).FindDescendant(); + + if (_scrollViewer != null) + { + UpdateChange((bool)e.NewValue); + } + }; + } + } + + if (_scrollViewer == null) + { + return; + } + + UpdateChange((bool)e.NewValue); + } + + /// + /// Function to update changes in + /// + /// New value from the + private static void UpdateChange(bool newValue) + { + if (newValue) + { + _scrollViewer.PointerPressed -= ScrollViewer_PointerPressed; + _scrollViewer.PointerPressed += ScrollViewer_PointerPressed; + } + else + { + _scrollViewer.PointerPressed -= ScrollViewer_PointerPressed; + UnsubscribeMiddleClickScrolling(); + } + } + + /// + /// Function to set default value and subscribe to events + /// + private static void SubscribeMiddleClickScrolling(DispatcherQueue dispatcherQueue) + { + _isPressed = true; + _isMoved = false; + _startPosition = default(Point); + _currentPosition = default(Point); + _isDeferredMovingStarted = false; + _oldCursorID = 100; + _isCursorAvailable = IsCursorResourceAvailable(); + + _timer?.Dispose(); + _timer = new Timer(Scroll, dispatcherQueue, 5, 5); + + Window.Current.CoreWindow.PointerMoved -= CoreWindow_PointerMoved; + Window.Current.CoreWindow.PointerReleased -= CoreWindow_PointerReleased; + + Window.Current.CoreWindow.PointerMoved += CoreWindow_PointerMoved; + Window.Current.CoreWindow.PointerReleased += CoreWindow_PointerReleased; + } + + /// + /// Function to set default value and unsubscribe to events + /// + private static void UnsubscribeMiddleClickScrolling() + { + _isPressed = false; + _isMoved = false; + _startPosition = default(Point); + _currentPosition = default(Point); + _isDeferredMovingStarted = false; + _oldCursorID = 100; + _timer?.Dispose(); + + Window.Current.CoreWindow.PointerMoved -= CoreWindow_PointerMoved; + Window.Current.CoreWindow.PointerReleased -= CoreWindow_PointerReleased; + + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0); + } + + /// + /// This function will be called for every small interval by + /// + /// Default param for . In this function it will be `null` + private static void Scroll(object state) + { + var dispatcherQueue = state as DispatcherQueue; + if (dispatcherQueue == null) + { + return; + } + + var offsetX = _currentPosition.X - _startPosition.X; + var offsetY = _currentPosition.Y - _startPosition.Y; + + SetCursorType(dispatcherQueue, offsetX, offsetY); + + if (Math.Abs(offsetX) > _threshold || Math.Abs(offsetY) > _threshold) + { + offsetX = Math.Abs(offsetX) < _threshold ? 0 : offsetX; + offsetY = Math.Abs(offsetY) < _threshold ? 0 : offsetY; + + offsetX /= _factor; + offsetY /= _factor; + + offsetX = offsetX > 0 ? Math.Pow(offsetX, 2) : -Math.Pow(offsetX, 2); + offsetY = offsetY > 0 ? Math.Pow(offsetY, 2) : -Math.Pow(offsetY, 2); + + offsetX = offsetX > _maxSpeed ? _maxSpeed : offsetX; + offsetY = offsetY > _maxSpeed ? _maxSpeed : offsetY; + + dispatcherQueue.EnqueueAsync(() => _scrollViewer?.ChangeView(_scrollViewer.HorizontalOffset + offsetX, _scrollViewer.VerticalOffset + offsetY, null, true)); + } + } + + /// + /// Function to check the status of scrolling + /// + /// Return true if the scrolling is started + private static bool CanScroll() + { + return _isDeferredMovingStarted || (_isPressed && !_isDeferredMovingStarted); + } + + private static void ScrollViewer_PointerPressed(object sender, PointerRoutedEventArgs e) + { + // Unsubscribe if deferred moving is started + if (_isDeferredMovingStarted) + { + UnsubscribeMiddleClickScrolling(); + return; + } + + Pointer pointer = e.Pointer; + + if (pointer.PointerDeviceType == PointerDeviceType.Mouse) + { + _scrollViewer = sender as ScrollViewer; + + PointerPoint pointerPoint = e.GetCurrentPoint(_scrollViewer); + + // SubscribeMiddle if middle button is pressed + if (pointerPoint.Properties.IsMiddleButtonPressed) + { + SubscribeMiddleClickScrolling(DispatcherQueue.GetForCurrentThread()); + + _startPosition = Window.Current.CoreWindow.PointerPosition; + _currentPosition = Window.Current.CoreWindow.PointerPosition; + } + } + } + + private static void CoreWindow_PointerMoved(CoreWindow sender, PointerEventArgs args) + { + // If condition that occurs before scrolling begins + if (_isPressed && !_isMoved) + { + PointerPoint pointerPoint = args.CurrentPoint; + + if (pointerPoint.Properties.IsMiddleButtonPressed) + { + _currentPosition = Window.Current.CoreWindow.PointerPosition; + + var offsetX = _currentPosition.X - _startPosition.X; + var offsetY = _currentPosition.Y - _startPosition.Y; + + // Setting _isMoved if pointer goes out of threshold value + if (Math.Abs(offsetX) > _threshold || Math.Abs(offsetY) > _threshold) + { + _isMoved = true; + } + } + } + + // Update current position of the pointer if scrolling started + if (CanScroll()) + { + _currentPosition = Window.Current.CoreWindow.PointerPosition; + } + } + + private static void CoreWindow_PointerReleased(CoreWindow sender, PointerEventArgs args) + { + // Start deferred moving if the pointer is pressed and not moved + if (_isPressed && !_isMoved) + { + _isDeferredMovingStarted = true; + + // Event to stop deferred scrolling if pointer exited + Window.Current.CoreWindow.PointerExited -= CoreWindow_PointerExited; + Window.Current.CoreWindow.PointerExited += CoreWindow_PointerExited; + + // Event to stop deferred scrolling if pointer pressed + Window.Current.CoreWindow.PointerPressed -= CoreWindow_PointerPressed; + Window.Current.CoreWindow.PointerPressed += CoreWindow_PointerPressed; + + SetCursorType(DispatcherQueue.GetForCurrentThread(), 0, 0); + } + else + { + _isDeferredMovingStarted = false; + } + + // Unsubscribe if the pointer is pressed and not DeferredMoving + if (_isPressed && !_isDeferredMovingStarted) + { + UnsubscribeMiddleClickScrolling(); + } + } + + private static void CoreWindow_PointerPressed(CoreWindow sender, PointerEventArgs args) + { + Window.Current.CoreWindow.PointerPressed -= CoreWindow_PointerPressed; + Window.Current.CoreWindow.PointerExited -= CoreWindow_PointerExited; + UnsubscribeMiddleClickScrolling(); + } + + private static void CoreWindow_PointerExited(CoreWindow sender, PointerEventArgs args) + { + Window.Current.CoreWindow.PointerPressed -= CoreWindow_PointerPressed; + Window.Current.CoreWindow.PointerExited -= CoreWindow_PointerExited; + UnsubscribeMiddleClickScrolling(); + } + + private static void SetCursorType(DispatcherQueue dispatcherQueue, double offsetX, double offsetY) + { + if (!_isCursorAvailable) + { + return; + } + + uint cursorID = 101; + + if (Math.Abs(offsetX) < _threshold && Math.Abs(offsetY) < _threshold) + { + cursorID = 101; + } + else if (Math.Abs(offsetX) < _threshold && offsetY < -_threshold) + { + cursorID = 102; + } + else if (offsetX > _threshold && offsetY < -_threshold) + { + cursorID = 103; + } + else if (offsetX > _threshold && Math.Abs(offsetY) < _threshold) + { + cursorID = 104; + } + else if (offsetX > _threshold && offsetY > _threshold) + { + cursorID = 105; + } + else if (Math.Abs(offsetX) < _threshold && offsetY > _threshold) + { + cursorID = 106; + } + else if (offsetX < -_threshold && offsetY > _threshold) + { + cursorID = 107; + } + else if (offsetX < -_threshold && Math.Abs(offsetY) < _threshold) + { + cursorID = 108; + } + else if (offsetX < -_threshold && offsetY < -_threshold) + { + cursorID = 109; + } + + if (_oldCursorID != cursorID) + { + dispatcherQueue.EnqueueAsync(() => Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, cursorID)); + + _oldCursorID = cursorID; + } + } + + /// + /// Function to check the availability of cursor resource + /// + /// Returns `true` if the cursor resource is available + private static bool IsCursorResourceAvailable() + { + var isCursorAvailable = true; + + try + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 101); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 102); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 103); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 104); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 105); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 106); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 107); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 108); + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Custom, 109); + } + catch (Exception) + { + isCursorAvailable = false; + } + finally + { + Window.Current.CoreWindow.PointerCursor = new CoreCursor(CoreCursorType.Arrow, 0); + } + + return isCursorAvailable; + } + } +} diff --git a/Sources/Stylophone/Stylophone.csproj b/Sources/Stylophone/Stylophone.csproj index 93f2d3a..0ed85f6 100644 --- a/Sources/Stylophone/Stylophone.csproj +++ b/Sources/Stylophone/Stylophone.csproj @@ -160,18 +160,12 @@ - - 2.7.3 - - - 2.0.1 - + + 1.4.0 - - 2.88.1 - + 1.1.0 @@ -194,7 +188,9 @@ + + diff --git a/Sources/Stylophone/Views/LibraryDetailPage.xaml b/Sources/Stylophone/Views/LibraryDetailPage.xaml index 61dab23..6f7ce29 100644 --- a/Sources/Stylophone/Views/LibraryDetailPage.xaml +++ b/Sources/Stylophone/Views/LibraryDetailPage.xaml @@ -8,6 +8,7 @@ xmlns:strings="using:Stylophone.Localization.Strings" xmlns:stylophone="using:Stylophone.Helpers" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:ui7="using:Microsoft.Toolkit.Uwp.UI" xmlns:winui="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> @@ -191,12 +192,12 @@ diff --git a/Sources/Stylophone/Views/LibraryPage.xaml b/Sources/Stylophone/Views/LibraryPage.xaml index 7972861..55d69b1 100644 --- a/Sources/Stylophone/Views/LibraryPage.xaml +++ b/Sources/Stylophone/Views/LibraryPage.xaml @@ -11,6 +11,7 @@ xmlns:strings="using:Stylophone.Localization.Strings" xmlns:stylophone="using:Stylophone.Helpers" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:ui7="using:Microsoft.Toolkit.Uwp.UI" xmlns:vm="using:Stylophone.Common.ViewModels" xmlns:winui="using:Microsoft.UI.Xaml.Controls" NavigationCacheMode="Required" @@ -33,7 +34,7 @@ animations:Connected.ListItemKey="animationKeyLibrary" behaviors:ListViewBehavior.FillBeforeWrap="True" behaviors:ListViewBehavior.MinItemWidth="204" - ui:ScrollViewerExtensions.EnableMiddleClickScrolling="True" + ui7:ScrollViewerExtensions.EnableMiddleClickScrolling="True" IsItemClickEnabled="True" ItemClick="AlbumClicked" ItemsSource="{x:Bind ViewModel.FilteredSource, Mode=OneWay}" diff --git a/Sources/Stylophone/Views/PlaylistPage.xaml b/Sources/Stylophone/Views/PlaylistPage.xaml index cbc9ff2..7d06447 100644 --- a/Sources/Stylophone/Views/PlaylistPage.xaml +++ b/Sources/Stylophone/Views/PlaylistPage.xaml @@ -9,6 +9,7 @@ xmlns:muxc="using:Microsoft.UI.Xaml.Controls" xmlns:strings="using:Stylophone.Localization.Strings" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:ui7="using:Microsoft.Toolkit.Uwp.UI" mc:Ignorable="d"> @@ -237,14 +238,14 @@ diff --git a/Sources/Stylophone/Views/SearchResultsPage.xaml b/Sources/Stylophone/Views/SearchResultsPage.xaml index 636c958..63de041 100644 --- a/Sources/Stylophone/Views/SearchResultsPage.xaml +++ b/Sources/Stylophone/Views/SearchResultsPage.xaml @@ -7,6 +7,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:ui7="using:Microsoft.Toolkit.Uwp.UI" xmlns:winui="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> @@ -46,12 +47,12 @@ diff --git a/Sources/Stylophone/Views/ServerQueuePage.xaml b/Sources/Stylophone/Views/ServerQueuePage.xaml index dcdd0ff..e14dd54 100644 --- a/Sources/Stylophone/Views/ServerQueuePage.xaml +++ b/Sources/Stylophone/Views/ServerQueuePage.xaml @@ -8,6 +8,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" xmlns:ui="using:CommunityToolkit.WinUI" + xmlns:ui7="using:Microsoft.Toolkit.Uwp.UI" xmlns:vm="using:Stylophone.Common.ViewModels" NavigationCacheMode="Required" mc:Ignorable="d"> @@ -23,7 +24,7 @@ Date: Tue, 12 Sep 2023 00:26:49 +0200 Subject: [PATCH 12/56] Migrate to SettingsCard/SettingsExpander --- .../Controls/SettingsBlockControl.xaml | 124 ---------- .../Controls/SettingsBlockControl.xaml.cs | 115 --------- .../Controls/SettingsDisplayControl.xaml | 118 --------- .../Controls/SettingsDisplayControl.xaml.cs | 85 ------- Sources/Stylophone/Stylophone.csproj | 14 -- Sources/Stylophone/Views/SettingsPage.xaml | 229 ++++++++---------- 6 files changed, 100 insertions(+), 585 deletions(-) delete mode 100644 Sources/Stylophone/Controls/SettingsBlockControl.xaml delete mode 100644 Sources/Stylophone/Controls/SettingsBlockControl.xaml.cs delete mode 100644 Sources/Stylophone/Controls/SettingsDisplayControl.xaml delete mode 100644 Sources/Stylophone/Controls/SettingsDisplayControl.xaml.cs diff --git a/Sources/Stylophone/Controls/SettingsBlockControl.xaml b/Sources/Stylophone/Controls/SettingsBlockControl.xaml deleted file mode 100644 index d59f9e0..0000000 --- a/Sources/Stylophone/Controls/SettingsBlockControl.xaml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Sources/Stylophone/Controls/SettingsBlockControl.xaml.cs b/Sources/Stylophone/Controls/SettingsBlockControl.xaml.cs deleted file mode 100644 index ccfb0a9..0000000 --- a/Sources/Stylophone/Controls/SettingsBlockControl.xaml.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Markup; - -namespace Stylophone.Controls -{ - [ContentProperty(Name = nameof(SettingsActionableElement))] - public sealed partial class SettingsBlockControl : UserControl - { - public FrameworkElement SettingsActionableElement { get; set; } - - public static readonly DependencyProperty ExpandableContentProperty = DependencyProperty.Register( - "ExpandableContent", - typeof(FrameworkElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public FrameworkElement ExpandableContent - { - get => (FrameworkElement)GetValue(ExpandableContentProperty); - set => SetValue(ExpandableContentProperty, value); - } - - public static readonly DependencyProperty AdditionalDescriptionContentProperty = DependencyProperty.Register( - "AdditionalDescriptionContent", - typeof(FrameworkElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public FrameworkElement AdditionalDescriptionContent - { - get => (FrameworkElement)GetValue(AdditionalDescriptionContentProperty); - set => SetValue(AdditionalDescriptionContentProperty, value); - } - - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( - "Title", - typeof(string), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public string Title - { - get => (string)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( - "Description", - typeof(string), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public string Description - { - get => (string)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - public static readonly DependencyProperty IconProperty = DependencyProperty.Register( - "Icon", - typeof(IconElement), - typeof(SettingsBlockControl), - new PropertyMetadata(null) - ); - - public IconElement Icon - { - get => (IconElement)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - public static readonly DependencyProperty IsClickableProperty = DependencyProperty.Register( - "IsClickable", - typeof(bool), - typeof(SettingsBlockControl), - new PropertyMetadata(false) - ); - - public bool IsClickable - { - get => (bool)GetValue(IsClickableProperty); - set => SetValue(IsClickableProperty, value); - } - - // - // Summary: - // Occurs when a button control is clicked. - public event RoutedEventHandler Click; - - public SettingsBlockControl() - { - this.InitializeComponent(); - } - - private void ActionableButton_Click(object sender, RoutedEventArgs e) - { - Click?.Invoke(this, e); - } - - private void Expander_Expanding(Microsoft.UI.Xaml.Controls.Expander sender, Microsoft.UI.Xaml.Controls.ExpanderExpandingEventArgs args) - { - Click?.Invoke(this, new RoutedEventArgs()); - } - - private void Expander_Collapsed(Microsoft.UI.Xaml.Controls.Expander sender, Microsoft.UI.Xaml.Controls.ExpanderCollapsedEventArgs args) - { - Click?.Invoke(this, new RoutedEventArgs()); - } - } -} diff --git a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml b/Sources/Stylophone/Controls/SettingsDisplayControl.xaml deleted file mode 100644 index 8929217..0000000 --- a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml.cs b/Sources/Stylophone/Controls/SettingsDisplayControl.xaml.cs deleted file mode 100644 index 549f5bb..0000000 --- a/Sources/Stylophone/Controls/SettingsDisplayControl.xaml.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Markup; - -namespace Stylophone.Controls -{ - [ContentProperty(Name = nameof(SettingsActionableElement))] - public sealed partial class SettingsDisplayControl : UserControl - { - public FrameworkElement SettingsActionableElement { get; set; } - - public static readonly DependencyProperty AdditionalDescriptionContentProperty = DependencyProperty.Register( - "AdditionalDescriptionContent", - typeof(FrameworkElement), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public FrameworkElement AdditionalDescriptionContent - { - get => (FrameworkElement)GetValue(AdditionalDescriptionContentProperty); - set => SetValue(AdditionalDescriptionContentProperty, value); - } - - public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( - "Title", - typeof(string), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public string Title - { - get => (string)GetValue(TitleProperty); - set => SetValue(TitleProperty, value); - } - - public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register( - "Description", - typeof(string), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public string Description - { - get => (string)GetValue(DescriptionProperty); - set => SetValue(DescriptionProperty, value); - } - - public static readonly DependencyProperty IconProperty = DependencyProperty.Register( - "Icon", - typeof(IconElement), - typeof(SettingsDisplayControl), - new PropertyMetadata(null) - ); - - public IconElement Icon - { - get => (IconElement)GetValue(IconProperty); - set => SetValue(IconProperty, value); - } - - public SettingsDisplayControl() - { - this.InitializeComponent(); - VisualStateManager.GoToState(this, "NormalState", false); - } - - private void MainPanel_SizeChanged(object sender, SizeChangedEventArgs e) - { - if (e.NewSize.Width == e.PreviousSize.Width || ActionableElement == null) - return; - - if (ActionableElement.ActualWidth > e.NewSize.Width / 3) - { - VisualStateManager.GoToState(this, "CompactState", false); - } - else - { - VisualStateManager.GoToState(this, "NormalState", false); - } - } - } -} diff --git a/Sources/Stylophone/Stylophone.csproj b/Sources/Stylophone/Stylophone.csproj index 0ed85f6..e7caa22 100644 --- a/Sources/Stylophone/Stylophone.csproj +++ b/Sources/Stylophone/Stylophone.csproj @@ -178,12 +178,6 @@ - - SettingsBlockControl.xaml - - - SettingsDisplayControl.xaml - @@ -256,14 +250,6 @@ - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - Designer MSBuild:Compile diff --git a/Sources/Stylophone/Views/SettingsPage.xaml b/Sources/Stylophone/Views/SettingsPage.xaml index be6ecb8..0e4057c 100644 --- a/Sources/Stylophone/Views/SettingsPage.xaml +++ b/Sources/Stylophone/Views/SettingsPage.xaml @@ -9,6 +9,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:strings="using:Stylophone.Localization.Strings" xmlns:helpers="using:Stylophone.Helpers" + xmlns:toolkit="using:CommunityToolkit.WinUI.Controls" mc:Ignorable="d"> @@ -33,37 +34,25 @@ - + HeaderIcon="{x:Bind ViewModel.IsServerValid, Converter={StaticResource IconConverter}, Mode=OneWay}"> - - - + - - - - - + + - - - - + + + - - - - + + + - + - + - - - - + + - - - + + + + + + - - + - - - + + - - + @@ -123,42 +105,40 @@ Style="{ThemeResource BaseTextBlockStyle}" Text="{x:Bind strings:Resources.SettingsDatabase}" /> - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - + + + - + + - + + - + - - + + - + - - - + + + - - - - - - - + + + + + + + @@ -367,11 +378,11 @@ - + - + @@ -384,19 +395,19 @@ - - + - + - + + + @@ -122,14 +124,14 @@ - - + + + @@ -157,11 +161,10 @@ - - - - + + + @@ -178,19 +181,15 @@ - - + + - - - - @@ -199,7 +198,6 @@ - @@ -219,7 +217,6 @@ - @@ -236,7 +233,7 @@ - + @@ -247,11 +244,11 @@ - + - + @@ -262,17 +259,17 @@ - + - + - + - - + + + + + + + + + + + @@ -359,16 +370,16 @@ - + - + - - + + From 49f2a05abcf0fc666e9b05f0407ea53ea4b50a6e Mon Sep 17 00:00:00 2001 From: Difegue Date: Thu, 14 Sep 2023 01:58:22 +0200 Subject: [PATCH 23/56] Add autoshrink to compactplayback track title --- Sources/Stylophone.iOS/Views/NowPlaying.storyboard | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Stylophone.iOS/Views/NowPlaying.storyboard b/Sources/Stylophone.iOS/Views/NowPlaying.storyboard index 6da640e..c2bda56 100644 --- a/Sources/Stylophone.iOS/Views/NowPlaying.storyboard +++ b/Sources/Stylophone.iOS/Views/NowPlaying.storyboard @@ -4,7 +4,6 @@ - @@ -394,7 +393,7 @@ - - + -