ShutdownWorkerAndCloseApp only closed MainWindow via CloseWindow, relying on ShutdownMode.OnMainWindowClose to bring the process down. That mode backs off if any window cancels its own Closing event, and MissionControlWindow always does (it hides instead of closing) - so the process never actually exited while Mission Control, Cheat Sheets, or the diff viewer were still open. Call IClassicDesktopStyleApplicationLifetime .Shutdown() directly, which force-closes every window regardless of per-window cancellation.
117 lines
3.9 KiB
C#
117 lines
3.9 KiB
C#
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Input;
|
|
using Avalonia.Layout;
|
|
using Avalonia.Media;
|
|
using ClaudeDo.Ui.Localization;
|
|
using ClaudeDo.Ui.Services;
|
|
using ClaudeDo.Ui.ViewModels;
|
|
using ClaudeDo.Ui.ViewModels.Islands;
|
|
using ClaudeDo.Ui.ViewModels.Modals;
|
|
using ClaudeDo.Ui.Views.Modals;
|
|
|
|
namespace ClaudeDo.Ui.Views;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
private bool _forceClose;
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
KeyDown += OnWindowKeyDown;
|
|
DataContextChanged += OnDataContextChanged;
|
|
UpdateMaxIcon();
|
|
}
|
|
|
|
// Closing the main window kills every open Mission Control ConPTY session without
|
|
// warning (App.axaml.cs: ShutdownMode.OnMainWindowClose). Ask first — never silently.
|
|
protected override void OnClosing(WindowClosingEventArgs e)
|
|
{
|
|
if (!_forceClose
|
|
&& DataContext is IslandsShellViewModel { Dialogs: { } dialogs } vm
|
|
&& IslandsShellViewModel.RequiresCloseConfirmation(vm.OpenMissionControlSessionCount))
|
|
{
|
|
e.Cancel = true;
|
|
_ = ConfirmCloseWithOpenSessionsAsync(dialogs, vm.OpenMissionControlSessionCount);
|
|
return;
|
|
}
|
|
base.OnClosing(e);
|
|
}
|
|
|
|
private async Task ConfirmCloseWithOpenSessionsAsync(IDialogService dialogs, int openSessionCount)
|
|
{
|
|
var confirmed = await dialogs.ConfirmAsync(Loc.T("vm.mainWindow.closeConfirm.message", openSessionCount));
|
|
if (!confirmed) return;
|
|
_forceClose = true;
|
|
Close();
|
|
}
|
|
|
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
{
|
|
base.OnPropertyChanged(change);
|
|
if (change.Property == WindowStateProperty)
|
|
{
|
|
UpdateMaxIcon();
|
|
if (DataContext is IslandsShellViewModel vm && vm.Tasks is not null)
|
|
vm.Tasks.IsWindowVisible = WindowState != WindowState.Minimized;
|
|
}
|
|
if (change.Property == OffScreenMarginProperty)
|
|
RootGrid.Margin = OffScreenMargin;
|
|
}
|
|
|
|
private void UpdateMaxIcon()
|
|
{
|
|
var key = WindowState == WindowState.Maximized ? "Icon.WinRestore" : "Icon.WinMax";
|
|
if (this.TryFindResource(key, out var geometry) && geometry is Geometry g)
|
|
MaxIcon.Data = g;
|
|
}
|
|
|
|
private void OnDataContextChanged(object? sender, EventArgs e)
|
|
{
|
|
if (DataContext is IslandsShellViewModel vm)
|
|
{
|
|
vm.Dialogs = new WindowDialogService(this);
|
|
vm.BringToFront = () =>
|
|
{
|
|
if (WindowState == WindowState.Minimized)
|
|
WindowState = WindowState.Normal;
|
|
Activate();
|
|
};
|
|
if (vm.Tasks is not null)
|
|
vm.Tasks.IsWindowVisible = WindowState != WindowState.Minimized;
|
|
}
|
|
}
|
|
|
|
private void OnWindowKeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Space
|
|
&& FocusManager?.GetFocusedElement() is not TextBox
|
|
&& DataContext is IslandsShellViewModel vm)
|
|
{
|
|
e.Handled = true;
|
|
_ = vm.ToggleSelectedDoneAsync();
|
|
}
|
|
}
|
|
|
|
private void OnTitleBarPressed(object? sender, PointerPressedEventArgs e)
|
|
{
|
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
|
BeginMoveDrag(e);
|
|
}
|
|
private void OnMinimize(object? s, Avalonia.Interactivity.RoutedEventArgs e) =>
|
|
WindowState = WindowState.Minimized;
|
|
private void OnToggleMax(object? s, Avalonia.Interactivity.RoutedEventArgs e) =>
|
|
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
|
private void OnClose(object? s, Avalonia.Interactivity.RoutedEventArgs e) => Close();
|
|
|
|
protected override void OnSizeChanged(SizeChangedEventArgs e)
|
|
{
|
|
base.OnSizeChanged(e);
|
|
if (DataContext is IslandsShellViewModel vm) vm.WindowWidth = Bounds.Width;
|
|
}
|
|
|
|
}
|