Browse Source

Win32 window behavior and style fixes

Ruben 1 week ago
parent
commit
c580c8d4cf

+ 2 - 246
src/PicView.Avalonia.Win32/App.axaml.cs

@@ -1,35 +1,13 @@
 using System.Runtime;
 using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
-using Avalonia.Media.Imaging;
-using Clowd.Clipboard;
-using PicView.Avalonia.ColorManagement;
-using PicView.Avalonia.Interfaces;
-using PicView.Avalonia.StartUp;
-using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.Win32.Views;
-using PicView.Avalonia.Win32.WindowImpl;
-using PicView.Core.FileAssociations;
-using PicView.Core.FileSorting;
-using PicView.Core.Localization;
-using PicView.Core.ProcessHandling;
-using PicView.Core.WindowsNT;
-using PicView.Core.WindowsNT.FileAssociation;
-using PicView.Core.WindowsNT.FileHandling;
-using PicView.Core.WindowsNT.Taskbar;
-using PicView.Core.WindowsNT.Wallpaper;
-using Win32Clipboard = PicView.Core.WindowsNT.Copy.Win32Clipboard;
 
 namespace PicView.Avalonia.Win32;
 
-public class App : Application, IPlatformSpecificService, IPlatformWindowService
+public class App : Application
 {
     private static WinMainWindow2? _mainWindow;
-    private static WindowInitializer? _windowInitializer;
-    private TaskbarProgress? _taskbarProgress;
-    private MainViewModel? _vm;
-     
     public override void Initialize()
     {
 #if DEBUG
@@ -46,229 +24,7 @@ public class App : Application, IPlatformSpecificService, IPlatformWindowService
     {
         base.OnFrameworkInitializationCompleted();
 
-        if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
-        {
-            return;
-        }
-
-        var settingsExists = LoadSettings();
-
-        TranslationManager.Init();
-        _vm = new MainViewModel(this, this)
-        {
-            MainWindow =
-            {
-                TopTitlebarViewModel = new TopTitlebarViewModel()
-            }
-        };
-
-        ThemeManager.DetermineTheme(Current, settingsExists);
-
-        _mainWindow = new WinMainWindow2();
-        desktop.MainWindow = _mainWindow;
-        _mainWindow.DataContext = _vm;
-        StartUpHelper2.StartWithArguments(_vm, settingsExists, desktop, _mainWindow);
-        _windowInitializer = new WindowInitializer();
-    }
-
-    public int CombinedTitleButtonsWidth
-    {
-        get => (int)(Settings.WindowProperties.Maximized && !Settings.WindowProperties.Fullscreen
-            ? _mainWindow?.OffScreenMargin.Left + _mainWindow?.OffScreenMargin.Right + field ?? field
-            : field);
-        set;
-    } = 185;
-
-    #region Interface Implementations
-    
-    public Task<bool> DeleteFile(string path, bool recycle) =>
-        Task.Run(() => WinFileHelper.DeleteFile(path, recycle));
-
-    public void SetTaskbarProgress(ulong progress, ulong maximum)
-    {
-        if (_taskbarProgress is null)
-        {
-            var handle = _mainWindow?.TryGetPlatformHandle()?.Handle;
-
-            // Ensure the handle is valid before proceeding
-            if (handle == IntPtr.Zero || handle is null)
-            {
-                return;
-            }
-
-            _taskbarProgress = new TaskbarProgress(handle.Value);
-        }
-
-        _taskbarProgress.SetProgress(progress, maximum);
-    }
-
-    public void StopTaskbarProgress()
-    {
-        var handle = _mainWindow?.TryGetPlatformHandle()?.Handle;
-
-        // Ensure the handle is valid before proceeding
-        if (handle == IntPtr.Zero || handle is null)
-        {
-            return;
-        }
-
-        _taskbarProgress?.StopProgress();
-
-        _taskbarProgress = null;
-    }
-
-    public void SetCursorPos(int x, int y)
-    {
-        NativeMethods.SetCursorPos(x, y);
-    }
-
-    public List<FileInfo> GetFiles(FileInfo fileInfo)
-    {
-        return FileListRetriever.RetrieveFiles(fileInfo, CompareStrings);
-    }
-
-    public int CompareStrings(string str1, string str2)
-    {
-        return NativeMethods.StrCmpLogicalW(str1, str2);
-    }
-
-    public void OpenWith(string path)
-    {
-        ProcessHelper.OpenWith(path);
-    }
-
-    public void LocateOnDisk(string path)
-    {
-        var folder = Path.GetDirectoryName(path);
-        FileExplorer.OpenFolderAndSelectFile(folder, path);
-    }
-
-    public void ShowFileProperties(string path)
-    {
-        FileExplorer.ShowFileProperties(path);
-    }
-
-    public void Print(string path)
-    {
-        if (Settings.UIProperties.ShowPrintPreview)
-        {
-            _windowInitializer?.ShowPrintPreviewWindow(_vm, path);
-        }
-        else
-        {
-            ProcessHelper.Print(path);
-        }
-    }
-
-    public async Task SetAsWallpaper(string path, int wallpaperStyle)
-    {
-        await Task.Run(() =>
-        {
-            var style = (WallpaperHelper.WallpaperStyle)wallpaperStyle;
-            WallpaperHelper.SetDesktopWallpaper(path, style);
-        });
+        _mainWindow = new WinMainWindow2(false);
     }
 
-    public bool SetAsLockScreen(string path)
-    {
-        return false;
-        // return LockscreenHelper.SetLockScreenImage(path);
-    }
-
-    public bool CopyFile(string path)
-    {
-        return Win32Clipboard.CopyFileToClipboard(false, path);
-    }
-
-    public bool CutFile(string path)
-    {
-        return Win32Clipboard.CopyFileToClipboard(true, path);
-    }
-
-    public async Task CopyImageToClipboard(Bitmap bitmap)
-    {
-        await ClipboardAvalonia.SetImageAsync(bitmap).ConfigureAwait(false);
-    }
-
-    public async Task<Bitmap?> GetImageFromClipboard()
-    {
-        return await ClipboardAvalonia.GetImageAsync().ConfigureAwait(false);
-    }
-
-    public async Task<bool> ExtractWithLocalSoftwareAsync(string path, string tempDirectory)
-    {
-        return await ArchiveExtractionHelper.ExtractWithLocalSoftwareAsync(path, tempDirectory);
-    }
-
-    public string DefaultJsonKeyMap()
-    {
-        return WindowsKeybindings.DefaultKeybindings;
-    }
-
-    public void InitiateFileAssociationService()
-    {
-        var iIFileAssociationService = new WindowsFileAssociationService();
-        FileAssociationManager.Initialize(iIFileAssociationService);
-    }
-
-    public void DisableScreensaver()
-    {
-        NativeMethods.DisableScreensaver();
-    }
-
-    public void EnableScreensaver()
-    {
-        NativeMethods.EnableScreensaver();
-    }
-
-    #endregion
-
-    #region Window interface implementations
-    
-    public void ShowAboutWindow() =>
-        _windowInitializer?.ShowAboutWindow(_vm);
-
-    public async Task ShowImageInfoWindow() =>
-        await _windowInitializer?.ShowImageInfoWindow(_vm);
-
-    public async Task ShowKeybindingsWindow() =>
-        await _windowInitializer?.ShowKeybindingsWindow(_vm);
-
-    public async Task ShowSettingsWindow() =>
-        await _windowInitializer?.ShowSettingsWindow(_vm);
-
-    public void ShowSingleImageResizeWindow() =>
-        _windowInitializer?.ShowSingleImageResizeWindow(_vm);
-
-    public async Task ShowBatchResizeWindow() =>
-       await _windowInitializer?.ShowBatchResizeWindow(_vm);
-
-    public void ShowEffectsWindow() =>
-        _windowInitializer?.ShowEffectsWindow(_vm);
-
-    public void ShowConvertWindow() =>
-        _windowInitializer?.ShowConvertWindow(_vm);
-
-    /// <inheritdoc />
-    public async Task Maximize(bool saveSetting = true) =>
-        await Win32Window.Maximize(_mainWindow, _vm, saveSetting);
-    
-    /// <inheritdoc />
-    public async Task MaximizeRestore(bool saveSetting = true) =>
-        await Win32Window.ToggleMaximize(_mainWindow, _vm, saveSetting);
-
-    /// <inheritdoc />
-    public async Task Fullscreen(bool saveSetting = true) =>
-        await Win32Window.Fullscreen(_mainWindow, _vm, saveSetting);
-    
-    /// <inheritdoc />
-    public async Task ToggleFullscreen(bool saveSetting = true) =>
-        await Win32Window.ToggleFullscreen(_mainWindow, _vm, saveSetting);
-    
-    /// <inheritdoc />
-    public async Task Restore() =>
-        await Win32Window.Restore(_mainWindow, _vm);
-
-    #endregion
-
 }

+ 1 - 1
src/PicView.Avalonia.Win32/Program.cs

@@ -11,7 +11,7 @@ internal class Program
     // yet and stuff might break.
     [STAThread]
     public static void Main(string[] args) => BuildAvaloniaApp()
-        .StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose);
+        .StartWithClassicDesktopLifetime(args, ShutdownMode.OnLastWindowClose);
 
     // Avalonia configuration, don't remove; also used by visual designer.
     public static AppBuilder BuildAvaloniaApp()

+ 1 - 1
src/PicView.Avalonia.Win32/Views/WinMainWindow2.axaml

@@ -1068,7 +1068,7 @@
                                     Padding="9,0" />
                                 <TextBlock
                                     Classes="txt"
-                                    Margin="5,0,0,0"
+                                    Margin="7,0,0,0"
                                     Text="{CompiledBinding TabTitle.Value,
                                                            Mode=OneWay}"
                                     TextWrapping="NoWrap"

+ 277 - 5
src/PicView.Avalonia.Win32/Views/WinMainWindow2.axaml.cs

@@ -2,9 +2,13 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Layout;
+using Avalonia.Media.Imaging;
 using Avalonia.Threading;
+using Clowd.Clipboard;
+using PicView.Avalonia.ColorManagement;
 using PicView.Avalonia.CustomControls;
 using PicView.Avalonia.DragAndDrop;
+using PicView.Avalonia.Interfaces;
 using PicView.Avalonia.StartUp;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
@@ -12,25 +16,94 @@ using PicView.Avalonia.Views.UC;
 using PicView.Avalonia.Views.UC.Menus;
 using PicView.Avalonia.Win32.WindowImpl;
 using PicView.Avalonia.WindowBehavior;
+using PicView.Core.FileAssociations;
+using PicView.Core.FileSorting;
+using PicView.Core.Localization;
+using PicView.Core.ProcessHandling;
 using PicView.Core.ViewModels;
+using PicView.Core.WindowsNT;
+using PicView.Core.WindowsNT.Copy;
+using PicView.Core.WindowsNT.FileAssociation;
+using PicView.Core.WindowsNT.FileHandling;
+using PicView.Core.WindowsNT.Taskbar;
+using PicView.Core.WindowsNT.Wallpaper;
 using R3;
 using R3.Avalonia;
 
 namespace PicView.Avalonia.Win32.Views;
 
-public partial class WinMainWindow2 : Window
+public partial class WinMainWindow2 : Window, IPlatformSpecificService, IPlatformWindowService
 {
     private readonly AvaloniaRenderingFrameProvider _frameProvider;
     private readonly CompositeDisposable _disposables = new();
+    
+    private static WindowInitializer? _windowInitializer;
+    private TaskbarProgress? _taskbarProgress;
+    private MainViewModel? _vm;
 
     public WinMainWindow2()
     {
-        InitializeComponent();
-
         // initialize RenderingFrameProvider
         _frameProvider = new AvaloniaRenderingFrameProvider(GetTopLevel(this)!);
         UIHelper.SetFrameProvider(_frameProvider);
+        
+        Initialization();
+    }
+    
+    public WinMainWindow2(bool mainWindowAlreadyExists)
+    {
+        if (mainWindowAlreadyExists)
+        {
+            // initialize RenderingFrameProvider
+            _frameProvider = new AvaloniaRenderingFrameProvider(GetTopLevel(this)!);
+            UIHelper.SetFrameProvider(_frameProvider);
+            
+            Initialization();
+            return;
+        }
+        
+        var settingsExists = LoadSettings();
+        
+        // initialize RenderingFrameProvider
+        _frameProvider = new AvaloniaRenderingFrameProvider(GetTopLevel(this)!);
+        UIHelper.SetFrameProvider(_frameProvider);
+        
+        Initialization();
+        WindowInitialization(settingsExists);
+    }
 
+    private void WindowInitialization(bool settingsExists)
+    {
+        TranslationManager.Init();
+        
+        _vm = new MainViewModel(this, this)
+        {
+            MainWindow =
+            {
+                TopTitlebarViewModel = new TopTitlebarViewModel()
+            }
+        };
+        DataContext = _vm;
+        
+        if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            return;
+        }
+
+        ThemeManager.DetermineTheme(Application.Current, settingsExists);
+        StartUpHelper2.StartWithArguments(_vm, settingsExists, desktop, this);
+        _windowInitializer = new WindowInitializer();
+    }
+
+    private void Initialization()
+    {
+        InitializeComponent();
+
+        LoadedInitialization();
+    }
+
+    private void LoadedInitialization()
+    {
         Loaded += delegate
         {
             if (DataContext is not MainViewModel vm)
@@ -121,7 +194,7 @@ public partial class WinMainWindow2 : Window
         };
     }
     
-        private void MainTabControlOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
+    private void MainTabControlOnSelectionChanged(object? sender, SelectionChangedEventArgs e)
     {
         if (DataContext is not MainViewModel vm)
         {
@@ -237,7 +310,7 @@ public partial class WinMainWindow2 : Window
             Dispatcher.UIThread.Invoke(() =>
             {
                 // Create a new window with the detached tab
-                var newWindow = new WinMainWindow2
+                var newWindow = new WinMainWindow2(true)
                 {
                     Position = new PixelPoint(e.ScreenPosition.X - 100, e.ScreenPosition.Y - 50),
                     Width = Width,
@@ -313,4 +386,203 @@ public partial class WinMainWindow2 : Window
         _disposables.Dispose();
         base.OnClosed(e);
     }
+    
+        #region Interface Implementations
+        
+        public int CombinedTitleButtonsWidth
+        {
+            get => (int)(Settings.WindowProperties.Maximized && !Settings.WindowProperties.Fullscreen
+                ? OffScreenMargin.Left + OffScreenMargin.Right + field : field);
+            set;
+        } = 185;
+    
+    public Task<bool> DeleteFile(string path, bool recycle) =>
+        Task.Run(() => WinFileHelper.DeleteFile(path, recycle));
+
+    public void SetTaskbarProgress(ulong progress, ulong maximum)
+    {
+        if (_taskbarProgress is null)
+        {
+            var handle = TryGetPlatformHandle()?.Handle;
+
+            // Ensure the handle is valid before proceeding
+            if (handle == IntPtr.Zero || handle is null)
+            {
+                return;
+            }
+
+            _taskbarProgress = new TaskbarProgress(handle.Value);
+        }
+
+        _taskbarProgress.SetProgress(progress, maximum);
+    }
+
+    public void StopTaskbarProgress()
+    {
+        var handle = TryGetPlatformHandle()?.Handle;
+
+        // Ensure the handle is valid before proceeding
+        if (handle == IntPtr.Zero || handle is null)
+        {
+            return;
+        }
+
+        _taskbarProgress?.StopProgress();
+
+        _taskbarProgress = null;
+    }
+
+    public void SetCursorPos(int x, int y)
+    {
+        NativeMethods.SetCursorPos(x, y);
+    }
+
+    public List<FileInfo> GetFiles(FileInfo fileInfo)
+    {
+        return FileListRetriever.RetrieveFiles(fileInfo, CompareStrings);
+    }
+
+    public int CompareStrings(string str1, string str2)
+    {
+        return NativeMethods.StrCmpLogicalW(str1, str2);
+    }
+
+    public void OpenWith(string path)
+    {
+        ProcessHelper.OpenWith(path);
+    }
+
+    public void LocateOnDisk(string path)
+    {
+        var folder = Path.GetDirectoryName(path);
+        FileExplorer.OpenFolderAndSelectFile(folder, path);
+    }
+
+    public void ShowFileProperties(string path)
+    {
+        FileExplorer.ShowFileProperties(path);
+    }
+
+    public void Print(string path)
+    {
+        if (Settings.UIProperties.ShowPrintPreview)
+        {
+            _windowInitializer?.ShowPrintPreviewWindow(_vm, path);
+        }
+        else
+        {
+            ProcessHelper.Print(path);
+        }
+    }
+
+    public async Task SetAsWallpaper(string path, int wallpaperStyle)
+    {
+        await Task.Run(() =>
+        {
+            var style = (WallpaperHelper.WallpaperStyle)wallpaperStyle;
+            WallpaperHelper.SetDesktopWallpaper(path, style);
+        });
+    }
+
+    public bool SetAsLockScreen(string path)
+    {
+        return false;
+        // return LockscreenHelper.SetLockScreenImage(path);
+    }
+
+    public bool CopyFile(string path)
+    {
+        return Win32Clipboard.CopyFileToClipboard(false, path);
+    }
+
+    public bool CutFile(string path)
+    {
+        return Win32Clipboard.CopyFileToClipboard(true, path);
+    }
+
+    public async Task CopyImageToClipboard(Bitmap bitmap)
+    {
+        await ClipboardAvalonia.SetImageAsync(bitmap).ConfigureAwait(false);
+    }
+
+    public async Task<Bitmap?> GetImageFromClipboard()
+    {
+        return await ClipboardAvalonia.GetImageAsync().ConfigureAwait(false);
+    }
+
+    public async Task<bool> ExtractWithLocalSoftwareAsync(string path, string tempDirectory)
+    {
+        return await ArchiveExtractionHelper.ExtractWithLocalSoftwareAsync(path, tempDirectory);
+    }
+
+    public string DefaultJsonKeyMap()
+    {
+        return WindowsKeybindings.DefaultKeybindings;
+    }
+
+    public void InitiateFileAssociationService()
+    {
+        var iIFileAssociationService = new WindowsFileAssociationService();
+        FileAssociationManager.Initialize(iIFileAssociationService);
+    }
+
+    public void DisableScreensaver()
+    {
+        NativeMethods.DisableScreensaver();
+    }
+
+    public void EnableScreensaver()
+    {
+        NativeMethods.EnableScreensaver();
+    }
+
+    #endregion
+
+    #region Window interface implementations
+    
+    public void ShowAboutWindow() =>
+        _windowInitializer?.ShowAboutWindow(_vm);
+
+    public async Task ShowImageInfoWindow() =>
+        await _windowInitializer?.ShowImageInfoWindow(_vm);
+
+    public async Task ShowKeybindingsWindow() =>
+        await _windowInitializer?.ShowKeybindingsWindow(_vm);
+
+    public async Task ShowSettingsWindow() =>
+        await _windowInitializer?.ShowSettingsWindow(_vm);
+
+    public void ShowSingleImageResizeWindow() =>
+        _windowInitializer?.ShowSingleImageResizeWindow(_vm);
+
+    public async Task ShowBatchResizeWindow() =>
+       await _windowInitializer?.ShowBatchResizeWindow(_vm);
+
+    public void ShowEffectsWindow() =>
+        _windowInitializer?.ShowEffectsWindow(_vm);
+
+    public void ShowConvertWindow() =>
+        _windowInitializer?.ShowConvertWindow(_vm);
+
+    /// <inheritdoc />
+    public async Task Maximize(bool saveSetting = true) =>
+        await Win32Window.Maximize(this, _vm, saveSetting);
+    
+    /// <inheritdoc />
+    public async Task MaximizeRestore(bool saveSetting = true) =>
+        await Win32Window.ToggleMaximize(this, _vm, saveSetting);
+
+    /// <inheritdoc />
+    public async Task Fullscreen(bool saveSetting = true) =>
+        await Win32Window.Fullscreen(this, _vm, saveSetting);
+    
+    /// <inheritdoc />
+    public async Task ToggleFullscreen(bool saveSetting = true) =>
+        await Win32Window.ToggleFullscreen(this, _vm, saveSetting);
+    
+    /// <inheritdoc />
+    public async Task Restore() =>
+        await Win32Window.Restore(this, _vm);
+
+    #endregion
 }

+ 26 - 20
src/PicView.Avalonia.Win32/Views/WinTitleBar2.axaml

@@ -1116,13 +1116,13 @@
                         IconHeight="12"
                         IconWidth="12"
                         IsRepeatEnabled="False"
+                        Margin="0,0"
                         Width="30"
                         x:Name="MinimizeButton" />
 
                     <customControls:IconButton
                         Background="{DynamicResource WindowButtonBackgroundColor}"
                         BorderBrush="{DynamicResource MainBorderColor}"
-                        BorderThickness="0,0,1,0"
                         Classes="hover"
                         ClickMode="Release"
                         Command="{CompiledBinding MainWindow.ToggleFullscreenCommand}"
@@ -1168,38 +1168,44 @@
 
                 </StackPanel>
 
-                <customControls:IconButton
-                    Background="{DynamicResource WindowButtonBackgroundColor}"
+                <Button
+                    Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
-                    BorderThickness="1,0,0,0"
                     Classes="noBorderHover"
                     Command="{CompiledBinding MainWindow.ToggleTabsCommand}"
-                    Data="{StaticResource FilledArrowDownGeometry}"
+                    CornerRadius="6"
                     DockPanel.Dock="Right"
-                    Foreground="{DynamicResource MainTextColor}"
-                    IconHeight="17"
-                    IconWidth="17"
-                    IsRepeatEnabled="False"
                     IsTabStop="False"
+                    Margin="2,3,7,3"
                     Width="30"
-                    x:Name="DropDownMenuButton" />
-
-                <customControls:IconButton
-                    Background="{DynamicResource WindowButtonBackgroundColor}"
+                    x:Name="DropDownMenuButton">
+                    <Path
+                        Data="{StaticResource FilledArrowDownGeometry}"
+                        Fill="{DynamicResource MainTextColor}"
+                        Height="17"
+                        Stretch="Fill"
+                        Width="17" />
+                </Button>
+
+                <Button
+                    Background="Transparent"
                     BorderBrush="{DynamicResource MainBorderColor}"
-                    BorderThickness="1,0,0,0"
                     Classes="noBorderHover"
                     Command="{CompiledBinding Tabs.CreateTab}"
-                    Data="{StaticResource AddGeometry}"
+                    CornerRadius="6"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColor}"
-                    IconHeight="13"
-                    IconWidth="13"
-                    IsRepeatEnabled="False"
                     IsTabStop="False"
-                    Margin="0"
+                    Margin="0,3,2,3"
                     Width="30"
-                    x:Name="CreateTabButton" />
+                    x:Name="CreateTabButton">
+                    <Path
+                        Data="{StaticResource AddGeometry}"
+                        Fill="{DynamicResource MainTextColor}"
+                        Height="13"
+                        Stretch="Fill"
+                        Width="13" />
+                </Button>
 
                 <uc:EditableTitlebar
                     Background="{DynamicResource WindowSecondaryBackgroundColor}"

+ 1 - 1
src/PicView.Avalonia/StartUp/StartUpHelper.cs

@@ -144,7 +144,7 @@ public static class StartUpHelper
         SetWindowEventHandlers(window);
         HandleThemeUpdates(vm);
 
-        UIHelper.SetControls(desktop);
+        UIHelper.SetControls(window);
         Task.Run(() =>
         {
             _ = FileHistoryManager.InitializeAsync();

+ 2 - 4
src/PicView.Avalonia/StartUp/StartUpHelper2.cs

@@ -10,7 +10,6 @@ using Avalonia.Interactivity;
 using Avalonia.Threading;
 using ImageMagick;
 using PicView.Avalonia.ColorManagement;
-using PicView.Avalonia.Functions;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.Navigation;
 using PicView.Avalonia.SettingsManagement;
@@ -21,7 +20,6 @@ using PicView.Avalonia.WindowBehavior;
 using PicView.Core.FileAssociations;
 using PicView.Core.FileHistory;
 using PicView.Core.ProcessHandling;
-using PicView.Core.ViewModels;
 
 namespace PicView.Avalonia.StartUp;
 
@@ -122,6 +120,7 @@ public static class StartUpHelper2
         HandleWindowScalingMode(vm, window);
 
         StartUpMenuOrLastFile(vm, window);
+        window.Show();
 
         HandlePostWindowUpdates(vm, settingsExists, desktop, window);
     }
@@ -168,10 +167,9 @@ public static class StartUpHelper2
         SetWindowEventHandlers(window);
         HandleThemeUpdates(vm);
 
-        UIHelper.SetControls(desktop);
+        UIHelper.SetControls(window);
         Task.Run(() =>
         {
-            vm.Tabs ??= new TabOverviewViewModel();
             vm.Tabs.SetParentContext(vm);
             _ = FileHistoryManager.InitializeAsync();
             HandleWindowControlSettings(vm, desktop);

+ 4 - 4
src/PicView.Avalonia/UI/UIHelper.cs

@@ -43,13 +43,13 @@ public static class UIHelper
     /// <summary>
     /// Sets up control references from the main desktop application
     /// </summary>
-    public static void SetControls(IClassicDesktopStyleApplicationLifetime desktop)
+    public static void SetControls(Window mainWindow)
     {
-        GetMainView = desktop.MainWindow?.FindControl<MainView2>("MainView");
-        GetTitlebar = desktop.MainWindow?.FindControl<Control>("Titlebar");
+        GetMainView = mainWindow?.FindControl<MainView2>("MainView");
+        GetTitlebar = mainWindow?.FindControl<Control>("Titlebar");
         GetEditableTitlebar = GetTitlebar?.FindControl<EditableTitlebar>("EditableTitlebar");
         GetGalleryView = GetMainView?.MainGrid.GetControl<GalleryAnimationControlView>("GalleryView");
-        GetBottomBar = desktop.MainWindow?.FindControl<BottomBar2>("BottomBar");
+        GetBottomBar = mainWindow?.FindControl<BottomBar2>("BottomBar");
         GetToolTipMessage = GetMainView?.MainGrid.FindControl<ToolTipMessage>("ToolTipMessage");
     }
 

+ 10 - 2
src/PicView.Avalonia/ViewModels/MainWindowViewModel.cs

@@ -88,11 +88,19 @@ public class MainWindowViewModel : IDisposable
 
     public BindableReactiveProperty<IImage?> ChangeCtrlZoomImage { get; } = new();
 
-    public ReactiveCommand ExitCommand { get; } = new(Close);
+    public ReactiveCommand ExitCommand { get; } = new((_, _) =>
+    {
+        WindowFunctions.Close();
+        return ValueTask.CompletedTask;
+    });
 
     public ReactiveCommand MaximizeCommand { get; } = new(async (_, _) => { await FunctionsMapper.Maximize(); });
 
-    public ReactiveCommand MinimizeCommand { get; } = new(async (_, _) => { await WindowFunctions.Minimize(); });
+    public ReactiveCommand MinimizeCommand { get; } = new((_, _) =>
+    {
+        WindowFunctions.Minimize();
+        return ValueTask.CompletedTask;
+    });
 
     public ReactiveCommand RestoreCommand { get; } = new(async (_, _) => { await FunctionsMapper.Restore(); });
 

+ 36 - 6
src/PicView.Avalonia/WindowBehavior/WindowFunctions.cs

@@ -300,26 +300,56 @@ public static class WindowFunctions
         await SaveSettingsAsync().ConfigureAwait(false);
     }
 
-    public static async Task Minimize()
+    public static void Minimize()
     {
         if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
         {
             return;
         }
 
-        await Dispatcher.UIThread.InvokeAsync(() =>
-            desktop.MainWindow.WindowState = WindowState.Minimized);
+        if (desktop.Windows.Count > 1)
+        {
+            foreach (var window in desktop.Windows)
+            {
+                if (!window.IsActive)
+                {
+                    continue;
+                }
+
+                window.WindowState = WindowState.Minimized;
+                return;
+            }
+        }
+        else
+        {
+            desktop.Windows[0].WindowState = WindowState.Minimized;
+        }
     }
 
-    public static async Task Close()
+    public static void Close()
     {
         if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
         {
             return;
         }
 
-        await Dispatcher.UIThread.InvokeAsync(() =>
-            desktop.MainWindow.Close());
+        if (desktop.Windows.Count > 1)
+        {
+            foreach (var window in desktop.Windows)
+            {
+                if (!window.IsActive)
+                {
+                    continue;
+                }
+
+                window.Close();
+                return;
+            }
+        }
+        else
+        {
+            desktop.Windows[0].Close();
+        }
     }
 
     #endregion