diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Loading.xaml b/src/Snap.Hutao/Snap.Hutao/Control/Loading.xaml index d7567bf5..f2052287 100644 --- a/src/Snap.Hutao/Snap.Hutao/Control/Loading.xaml +++ b/src/Snap.Hutao/Snap.Hutao/Control/Loading.xaml @@ -24,7 +24,7 @@ x:Name="ContentGrid" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" - x:Load="False"> + x:Load="True"> diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs index 6f924ebb..901341be 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.UI.WindowsAndMessaging; +using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Cryptography; @@ -26,9 +27,10 @@ internal sealed class NotifyIconController : IDisposable { lazyMenu = new(() => new(serviceProvider)); - StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult(); - icon = new(iconFile.Path); - id = Unsafe.As(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconFile.Path)))); + RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService(); + string iconPath = Path.Combine(runtimeOptions.InstalledLocation, "Assets/Logo.ico"); + icon = new(iconPath); + id = Unsafe.As(ref MemoryMarshal.GetArrayDataReference(MD5.HashData(Encoding.UTF8.GetBytes(iconPath)))); xamlHostWindow = new(serviceProvider); xamlHostWindow.MoveAndResize(default); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs index 19262af0..bf5edb3c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs @@ -31,6 +31,12 @@ internal static class WindowExtension return WindowControllers.TryGetValue(window, out _); } + public static void UninitializeController(this TWindow window) + where TWindow : Window + { + WindowControllers.Remove(window); + } + public static DesktopWindowXamlSource? GetDesktopWindowXamlSource(this Window window) { if (window.SystemBackdrop is SystemBackdropDesktopWindowXamlSourceAccess access) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs index 98ab3d69..8be2e106 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs @@ -13,6 +13,7 @@ using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.Setting; using Snap.Hutao.Core.Windowing.Abstraction; using Snap.Hutao.Core.Windowing.NotifyIcon; +using Snap.Hutao.Factory.ContentDialog; using Snap.Hutao.Service; using Snap.Hutao.Win32; using Snap.Hutao.Win32.Foundation; @@ -99,11 +100,10 @@ internal sealed class XamlWindowController private void OnWindowClosed(object sender, WindowEventArgs args) { + serviceProvider.GetRequiredService().PropertyChanged -= OnOptionsPropertyChanged; + if (XamlLifetime.ApplicationLaunchedWithNotifyIcon && !XamlLifetime.ApplicationExiting) { - args.Handled = true; - window.Hide(); - if (!IsNotifyIconVisible()) { new ToastContentBuilder() @@ -119,16 +119,15 @@ internal sealed class XamlWindowController GC.Collect(GC.MaxGeneration); } - else - { - if (window is IXamlWindowRectPersisted rectPersisted) - { - SaveOrSkipWindowSize(rectPersisted); - } - subclass?.Dispose(); - windowNonRudeHWND?.Dispose(); + if (window is IXamlWindowRectPersisted rectPersisted) + { + SaveOrSkipWindowSize(rectPersisted); } + + subclass?.Dispose(); + windowNonRudeHWND?.Dispose(); + window.UninitializeController(); } private bool IsNotifyIconVisible() diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs index 7c06a1b7..4ea5e8b5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/ContentDialogFactory.cs @@ -4,6 +4,7 @@ using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Service; +using System.Collections.Concurrent; namespace Snap.Hutao.Factory.ContentDialog; @@ -18,10 +19,14 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory private readonly ITaskContext taskContext; private readonly AppOptions appOptions; + private readonly ConcurrentQueue> dialogQueue = []; + private bool isDialogShowing; + /// public async ValueTask CreateForConfirmAsync(string title, string content) { await taskContext.SwitchToMainThreadAsync(); + Microsoft.UI.Xaml.Controls.ContentDialog dialog = new() { XamlRoot = currentWindowReference.GetXamlRoot(), @@ -39,6 +44,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory public async ValueTask CreateForConfirmCancelAsync(string title, string content, ContentDialogButton defaultButton = ContentDialogButton.Close) { await taskContext.SwitchToMainThreadAsync(); + Microsoft.UI.Xaml.Controls.ContentDialog dialog = new() { XamlRoot = currentWindowReference.GetXamlRoot(), @@ -57,6 +63,7 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory public async ValueTask CreateForIndeterminateProgressAsync(string title) { await taskContext.SwitchToMainThreadAsync(); + Microsoft.UI.Xaml.Controls.ContentDialog dialog = new() { XamlRoot = currentWindowReference.GetXamlRoot(), @@ -72,9 +79,11 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog { await taskContext.SwitchToMainThreadAsync(); + TContentDialog contentDialog = serviceProvider.CreateInstance(parameters); contentDialog.XamlRoot = currentWindowReference.GetXamlRoot(); contentDialog.RequestedTheme = appOptions.ElementTheme; + return contentDialog; } @@ -84,6 +93,51 @@ internal sealed partial class ContentDialogFactory : IContentDialogFactory TContentDialog contentDialog = serviceProvider.CreateInstance(parameters); contentDialog.XamlRoot = currentWindowReference.GetXamlRoot(); contentDialog.RequestedTheme = appOptions.ElementTheme; + return contentDialog; } + + [SuppressMessage("", "SH003")] + public Task EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog) + { + TaskCompletionSource dialogShowCompletionSource = new(); + + dialogQueue.Enqueue(async () => + { + try + { + ContentDialogResult result = await contentDialog.ShowAsync(); + dialogShowCompletionSource.SetResult(result); + } + catch (Exception ex) + { + dialogShowCompletionSource.SetException(ex); + } + finally + { + await ShowNextDialog().ConfigureAwait(false); + } + }); + + if (!isDialogShowing) + { + ShowNextDialog(); + } + + return dialogShowCompletionSource.Task; + + Task ShowNextDialog() + { + if (dialogQueue.TryDequeue(out Func? showNextDialogAsync)) + { + isDialogShowing = true; + return showNextDialogAsync(); + } + else + { + isDialogShowing = false; + return Task.CompletedTask; + } + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs index 00c434c3..703378df 100644 --- a/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs +++ b/src/Snap.Hutao/Snap.Hutao/Factory/ContentDialog/IContentDialogFactory.cs @@ -40,4 +40,6 @@ internal interface IContentDialogFactory ValueTask CreateInstanceAsync(params object[] parameters) where TContentDialog : Microsoft.UI.Xaml.Controls.ContentDialog; + + Task EnqueueAndShowAsync(Microsoft.UI.Xaml.Controls.ContentDialog contentDialog); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs index 71f93653..02b19c8c 100644 --- a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs @@ -13,7 +13,7 @@ using Windows.Graphics; namespace Snap.Hutao; [HighQuality] -[Injection(InjectAs.Singleton)] +[Injection(InjectAs.Transient)] internal sealed partial class LaunchGameWindow : Window, IDisposable, IXamlWindowExtendContentIntoTitleBar, diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs index 54e0e4c9..2e1a4b51 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao; /// 主窗体 /// [HighQuality] -[Injection(InjectAs.Singleton)] +[Injection(InjectAs.Transient)] internal sealed partial class MainWindow : Window, IXamlWindowExtendContentIntoTitleBar, IXamlWindowRectPersisted, diff --git a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml index 0edacf43..4b31ce7e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/MainView.xaml @@ -17,10 +17,7 @@ mc:Ignorable="d"> - + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs index 5e199f54..a6e876bd 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/MainViewModel.cs @@ -31,6 +31,7 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo public void Initialize(IBackgroundImagePresenterAccessor accessor) { backgroundImagePresenter = accessor.BackgroundImagePresenter; + UpdateBackgroundAsync(true).SafeForget(); } public void Receive(BackgroundImageTypeChangedMessage message) @@ -39,14 +40,14 @@ internal sealed partial class MainViewModel : Abstraction.ViewModel, IMainViewMo } [Command("UpdateBackgroundCommand")] - private async Task UpdateBackgroundAsync() + private async Task UpdateBackgroundAsync(bool forceRefresh = false) { if (backgroundImagePresenter is null) { return; } - (bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(previousBackgroundImage).ConfigureAwait(false); + (bool shouldRefresh, BackgroundImage? backgroundImage) = await backgroundImageService.GetNextBackgroundImageAsync(forceRefresh ? default : previousBackgroundImage).ConfigureAwait(false); if (shouldRefresh) {