diff --git a/src/UniGetUI.Avalonia/App.axaml.cs b/src/UniGetUI.Avalonia/App.axaml.cs index 21fc57b544..1142b6c6ac 100644 --- a/src/UniGetUI.Avalonia/App.axaml.cs +++ b/src/UniGetUI.Avalonia/App.axaml.cs @@ -149,19 +149,6 @@ private static void StartMainWindow(IClassicDesktopStyleApplicationLifetime desk }; } - if (CoreData.WasDaemon) - { - // Start silently: hide the window on first open only. - // Opened fires on every Show() in Avalonia, so we must unsubscribe - // immediately or every ShowFromTray() call would hide the window again. - void HideOnce(object? s, EventArgs e) - { - mainWindow.Opened -= HideOnce; - mainWindow.Hide(); - } - mainWindow.Opened += HideOnce; - } - if (splash is not null) { var splashRef = splash; @@ -173,9 +160,10 @@ void CloseSplashOnce(object? s, EventArgs e) mainWindow.Opened += CloseSplashOnce; } - // Framework auto-show already passed (we deferred via Dispatcher.Post), - // so we have to open the window ourselves. - mainWindow.Show(); + // Framework auto-show already passed (we deferred via Dispatcher.Post), so we have to + // open the window ourselves. Daemon mode never shows it at all. + if (!CoreData.WasDaemon) + mainWindow.Show(); _ = StartupAsync(mainWindow); } @@ -194,12 +182,7 @@ private static async Task StartupAsync(MainWindow mainWindow) // ShowDialog tries to attach to it as owner. await Task.Yield(); - // ShowDialog requires a visible owner. In daemon mode the main window - // is hidden, so temporarily show it and re-hide after the dialog closes. - bool reshide = CoreData.WasDaemon; - if (reshide) mainWindow.Show(); - await new CrashReportWindow(report).ShowDialog(mainWindow); - if (reshide) mainWindow.Hide(); + await mainWindow.ShowDialogAndRestoreVisibilityAsync(new CrashReportWindow(report)); } catch { /* must not prevent normal startup */ } } diff --git a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaBootstrapper.cs b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaBootstrapper.cs index 2aad7ac25b..c37fa9ce84 100644 --- a/src/UniGetUI.Avalonia/Infrastructure/AvaloniaBootstrapper.cs +++ b/src/UniGetUI.Avalonia/Infrastructure/AvaloniaBootstrapper.cs @@ -91,14 +91,14 @@ await Dispatcher.UIThread.InvokeAsync( private static async Task ShowIntegrityViolationDialogAsync() { if (MainWindow.Instance is not { } owner) return; - await new IntegrityViolationDialog().ShowDialog(owner); + await owner.ShowDialogAndRestoreVisibilityAsync(new IntegrityViolationDialog()); } private static async Task ShowMissingDependencyDialogAsync( ManagerDependency dep, int current, int total) { if (MainWindow.Instance is not { } owner) return; - await new MissingDependencyDialog(dep, current, total).ShowDialog(owner); + await owner.ShowDialogAndRestoreVisibilityAsync(new MissingDependencyDialog(dep, current, total)); } private static Task InitializeSharedServicesAsync() diff --git a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs index 05b2b466b6..282632837a 100644 --- a/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs +++ b/src/UniGetUI.Avalonia/Views/MainWindow.axaml.cs @@ -829,6 +829,11 @@ private void SaveGeometryNow() { try { + // Width/Height stay NaN until the window has been laid out, which never happens on a + // daemon launch the user has not opened yet. Saving then would persist a 0x0 size. + if (double.IsNaN(Width) || double.IsNaN(Height)) + return; + int state = WindowState == WindowState.Maximized ? 1 : 0; string geometry = $"v2,{Position.X},{Position.Y},{(int)Width},{(int)Height},{state}"; Settings.SetValue(Settings.K.WindowGeometry, geometry); @@ -1556,6 +1561,25 @@ public async Task ShowManageIgnoredUpdatesAsync() await ShowImmersiveDialogAsync(dialog); } + // Immersive dialogs are hosted inside this window, so they are unreachable — and never complete — + // while it is off screen (daemon launch). Bring it up for the dialog and put it back after. + public async Task ShowDialogAndRestoreVisibilityAsync(ImmersiveDialog dialog) + { + bool reshide = !IsVisible; + if (reshide) + Show(); + + try + { + await dialog.ShowDialog(this); + } + finally + { + if (reshide) + Hide(); + } + } + public async Task ShowImmersiveDialogAsync(ImmersiveDialog dialog) { var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);