diff --git a/src/SharpEmu.GUI/App.axaml.cs b/src/SharpEmu.GUI/App.axaml.cs index 111ef489..b88789cf 100644 --- a/src/SharpEmu.GUI/App.axaml.cs +++ b/src/SharpEmu.GUI/App.axaml.cs @@ -18,6 +18,7 @@ public partial class App : Application { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { + desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnMainWindowClose; desktop.MainWindow = new MainWindow(); } diff --git a/src/SharpEmu.GUI/MainWindow.axaml b/src/SharpEmu.GUI/MainWindow.axaml index 5c2a27e3..cd7fe0b5 100644 --- a/src/SharpEmu.GUI/MainWindow.axaml +++ b/src/SharpEmu.GUI/MainWindow.axaml @@ -73,19 +73,23 @@ SPDX-License-Identifier: GPL-2.0-or-later Classes="windowChrome" ToolTip.Tip="Minimize" AutomationProperties.Name="Minimize window"> - + diff --git a/src/SharpEmu.GUI/MainWindow.axaml.cs b/src/SharpEmu.GUI/MainWindow.axaml.cs index 11607290..ac085d11 100644 --- a/src/SharpEmu.GUI/MainWindow.axaml.cs +++ b/src/SharpEmu.GUI/MainWindow.axaml.cs @@ -165,8 +165,6 @@ public partial class MainWindow : Window ConsoleList.ItemsSource = _consoleLines; _consoleMirror = GuiConsoleMirror.Install((line, isError) => _pendingLines.Enqueue((line, isError))); - Closed += (_, _) => _emulator?.Stop(); - _consoleFlushTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(80), @@ -297,7 +295,8 @@ public partial class MainWindow : Window CtxRemove.Click += (_, _) => RemoveSelectedFromLibrary(); Opened += async (_, _) => await OnOpenedAsync(); - Closing += (_, _) => OnWindowClosing(); + Closing += (_, _) => BeginWindowClosing(); + Closed += (_, _) => CompleteWindowClosing(); SdlLauncherGamepad.EnsureStarted(); _gamepadTimer = new DispatcherTimer @@ -816,22 +815,44 @@ public partial class MainWindow : Window // still needs a preview hook for its own shortcuts. } - private void OnWindowClosing() + private void BeginWindowClosing() { + if (_isClosing) + { + return; + } + _isClosing = true; Interlocked.Increment(ref _libraryScanGeneration); Interlocked.Increment(ref _detailLoadGeneration); - _libraryWatcher.Dispose(); - _settings.Save(); _consoleFlushTimer.Stop(); _gamepadTimer.Stop(); - SdlLauncherGamepad.Shutdown(); - _sndPreview.Stop(); - _discord?.Dispose(); - _consoleWindow?.Close(); - _emulator?.Dispose(); - _consoleMirror?.Dispose(); - DropFileLog(); + } + + private void CompleteWindowClosing() + { + RunShutdownStep("library watcher", _libraryWatcher.Dispose); + RunShutdownStep("settings", _settings.Save); + RunShutdownStep("SDL gamepad", SdlLauncherGamepad.Shutdown); + RunShutdownStep("title music", _sndPreview.Stop); + RunShutdownStep("Discord Rich Presence", () => _discord?.Dispose()); + RunShutdownStep("console window", () => _consoleWindow?.Close()); + RunShutdownStep("emulator process", () => _emulator?.Dispose()); + RunShutdownStep("console mirror", () => _consoleMirror?.Dispose()); + RunShutdownStep("file log", DropFileLog); + } + + private static void RunShutdownStep(string component, Action action) + { + try + { + action(); + } + catch (Exception exception) + { + Console.Error.WriteLine( + $"[GUI][WARN] Failed to clean up {component}: {exception}"); + } } private void OnTitleBarPointerPressed(object? sender, PointerPressedEventArgs e) @@ -868,14 +889,17 @@ public partial class MainWindow : Window return; } - var isMaximized = WindowState == WindowState.Maximized; - glyph.Text = isMaximized ? "❐" : "□"; - ToolTip.SetTip(button, isMaximized ? "Restore" : "Maximize"); - AutomationProperties.SetName( - button, - isMaximized ? "Restore window" : "Maximize window"); + var state = GetMaximizeButtonState(WindowState); + glyph.Text = state.Glyph; + ToolTip.SetTip(button, state.ToolTip); + AutomationProperties.SetName(button, state.AutomationName); } + internal static WindowMaximizeButtonState GetMaximizeButtonState(WindowState windowState) => + windowState == WindowState.Maximized + ? new("filter_none", "Restore", "Restore window") + : new("crop_square", "Maximize", "Maximize window"); + private void UpdateWindowChromeState() { UpdateMaximizeButton(); diff --git a/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml b/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml index 6ac47f3f..8d3959e9 100644 --- a/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml +++ b/src/SharpEmu.GUI/Themes/Styles/Chrome.axaml @@ -19,6 +19,12 @@ Window chrome button sizing and interaction states. + +