From cd9ce511f3af47f3d7c548cec94358e9b21001cf Mon Sep 17 00:00:00 2001 From: Ruben Date: Thu, 17 Oct 2024 12:46:11 +0200 Subject: [PATCH] Fix starting up in fullscreen mode and refactor window logic and window resizing --- .../Views/AboutWindow.axaml | 1 + .../Views/KeybindingsWindow.axaml | 1 + .../Views/SettingsWindow.axaml | 1 + .../Views/WinMainWindow.axaml.cs | 31 +- .../Views/WinTitleBar.axaml.cs | 3 +- .../CustomControls/GalleryAnimationControl.cs | 7 +- .../Navigation/NavigationHelper.cs | 3 +- src/PicView.Avalonia/Navigation/QuickLoad.cs | 5 +- src/PicView.Avalonia/Navigation/Slideshow.cs | 7 +- .../Navigation/UpdateImage.cs | 7 +- src/PicView.Avalonia/UI/FunctionsHelper.cs | 33 +- src/PicView.Avalonia/UI/HideInterfaceLogic.cs | 5 +- src/PicView.Avalonia/UI/StartUpHelper.cs | 16 +- src/PicView.Avalonia/UI/UIHelper.cs | 7 +- .../ViewModels/MainViewModel.cs | 12 +- src/PicView.Avalonia/Views/BottomBar.axaml.cs | 3 +- .../Views/ImageViewer.axaml.cs | 5 +- src/PicView.Avalonia/Views/MainView.axaml.cs | 3 +- .../UC/BottomGalleryItemSizeSlider.axaml.cs | 3 +- .../UC/FullGalleryItemSizeSlider.axaml.cs | 4 +- .../Views/UC/GalleryItemSizeSlider.axaml.cs | 5 +- .../WindowFunctions.cs} | 519 +++++------------- .../WindowBehavior/WindowResizing.cs | 294 ++++++++++ 23 files changed, 522 insertions(+), 453 deletions(-) rename src/PicView.Avalonia/{UI/WindowHelper.cs => WindowBehavior/WindowFunctions.cs} (60%) create mode 100644 src/PicView.Avalonia/WindowBehavior/WindowResizing.cs diff --git a/src/PicView.Avalonia.Win32/Views/AboutWindow.axaml b/src/PicView.Avalonia.Win32/Views/AboutWindow.axaml index b539de51..ee372343 100644 --- a/src/PicView.Avalonia.Win32/Views/AboutWindow.axaml +++ b/src/PicView.Avalonia.Win32/Views/AboutWindow.axaml @@ -76,6 +76,7 @@ Foreground="{DynamicResource MainTextColor}" Height="28" LineHeight="28" + Padding="30,0,0,0" Text="{CompiledBinding About}" TextAlignment="Center" x:Name="TitleText" /> diff --git a/src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml b/src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml index a57e8af5..e4527ba8 100644 --- a/src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml +++ b/src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml @@ -80,6 +80,7 @@ Classes="txt" Foreground="{DynamicResource MainTextColor}" LineHeight="28" + Padding="30,0,0,0" Text="{CompiledBinding ApplicationShortcuts}" TextAlignment="Center" x:Name="TitleText" /> diff --git a/src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml b/src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml index 8d2f2856..d4c5d993 100644 --- a/src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml +++ b/src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml @@ -82,6 +82,7 @@ Classes="txt" Foreground="{DynamicResource MainTextColor}" LineHeight="28" + Padding="30,0,0,0" Text="{CompiledBinding Settings, Mode=OneWay}" TextAlignment="Center" diff --git a/src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs b/src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs index 0858748d..9c681d23 100644 --- a/src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs +++ b/src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs @@ -3,8 +3,8 @@ using Avalonia.Controls.ApplicationLifetimes; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; -using ReactiveUI; namespace PicView.Avalonia.Win32.Views; @@ -23,31 +23,12 @@ public WinMainWindow() // Keep window position when resizing ClientSizeProperty.Changed.Subscribe(size => { - WindowHelper.HandleWindowResize(this, size); - }); - - this.WhenAnyValue(x => x.WindowState).Subscribe(state => - { - switch (state) - { - case WindowState.Normal: - SettingsHelper.Settings.WindowProperties.Maximized = false; - SettingsHelper.Settings.WindowProperties.Fullscreen = false; - break; - case WindowState.Minimized: - break; - case WindowState.Maximized: - WindowHelper.Maximize(); - break; - case WindowState.FullScreen: - //WindowHelper.Fullscreen(DataContext as MainViewModel, Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime); - break; - } + WindowResizing.HandleWindowResize(this, size); }); ScalingChanged += (_, _) => { ScreenHelper.UpdateScreenSize(this); - WindowHelper.SetSize(DataContext as MainViewModel); + WindowResizing.SetSize(DataContext as MainViewModel); }; PointerExited += (_, _) => { @@ -61,14 +42,14 @@ public WinMainWindow() desktop.ShutdownRequested += async (_, e) => { - await WindowHelper.WindowClosingBehavior(this); + await WindowFunctions.WindowClosingBehavior(this); }; } protected override async void OnClosing(WindowClosingEventArgs e) { e.Cancel = true; - await WindowHelper.WindowClosingBehavior(this); + await WindowFunctions.WindowClosingBehavior(this); base.OnClosing(e); } @@ -89,6 +70,6 @@ private void Control_OnSizeChanged(object? sender, SizeChangedEventArgs e) return; } var wm = (MainViewModel)DataContext; - WindowHelper.SetSize(wm); + WindowResizing.SetSize(wm); } } \ No newline at end of file diff --git a/src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs b/src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs index a4339630..eeaab798 100644 --- a/src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs +++ b/src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Media; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; namespace PicView.Avalonia.Win32.Views; @@ -98,6 +99,6 @@ private void MoveWindow(PointerPressedEventArgs e) { return; } - WindowHelper.WindowDragAndDoubleClickBehavior((Window)VisualRoot, e); + WindowFunctions.WindowDragAndDoubleClickBehavior((Window)VisualRoot, e); } } \ No newline at end of file diff --git a/src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs b/src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs index 7bfcf72e..874f8a44 100644 --- a/src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs +++ b/src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs @@ -9,6 +9,7 @@ using PicView.Avalonia.Gallery; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Gallery; using ReactiveUI; @@ -174,7 +175,7 @@ await Dispatcher.UIThread.InvokeAsync(() => { IsVisible = true; Opacity = 1; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); UIHelper.GetGalleryView.BlurMask.BlurEnabled = false; vm.GalleryItemMargin = new Thickness(2,0); }); @@ -208,7 +209,7 @@ private async Task BottomToClosedAnimation() } await Dispatcher.UIThread.InvokeAsync(() => { - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); UIHelper.GetGalleryView.BlurMask.BlurEnabled = false; }); @@ -225,7 +226,7 @@ await Dispatcher.UIThread.InvokeAsync(() => { Height = to; IsVisible = false; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); }); _isAnimating = false; } diff --git a/src/PicView.Avalonia/Navigation/NavigationHelper.cs b/src/PicView.Avalonia/Navigation/NavigationHelper.cs index 50655cc3..4668568d 100644 --- a/src/PicView.Avalonia/Navigation/NavigationHelper.cs +++ b/src/PicView.Avalonia/Navigation/NavigationHelper.cs @@ -8,6 +8,7 @@ using PicView.Avalonia.Keybindings; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.ArchiveHandling; using PicView.Core.Config; using PicView.Core.Gallery; @@ -507,7 +508,7 @@ private static async Task PreviewPicAndLoadGallery(FileInfo fileInfo, MainViewMo vm.ImageType = imageModel.ImageType; await Dispatcher.UIThread.InvokeAsync(() => { - WindowHelper.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, 0,0, imageModel.Rotation, vm); + WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, 0,0, imageModel.Rotation, vm); }); if (files is null) diff --git a/src/PicView.Avalonia/Navigation/QuickLoad.cs b/src/PicView.Avalonia/Navigation/QuickLoad.cs index 42e02c0c..aa0040aa 100644 --- a/src/PicView.Avalonia/Navigation/QuickLoad.cs +++ b/src/PicView.Avalonia/Navigation/QuickLoad.cs @@ -3,6 +3,7 @@ using PicView.Avalonia.ImageHandling; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.FileHandling; using PicView.Core.Gallery; @@ -46,10 +47,10 @@ public static async Task QuickLoadAsync(MainViewModel vm, string file) await Dispatcher.UIThread.InvokeAsync(() => { vm.ImageViewer.SetTransform(imageModel.EXIFOrientation); - WindowHelper.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, secondaryPreloadValue?.ImageModel?.PixelWidth ?? 0, secondaryPreloadValue?.ImageModel?.PixelHeight ?? 0, imageModel.Rotation, vm); + WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, secondaryPreloadValue?.ImageModel?.PixelWidth ?? 0, secondaryPreloadValue?.ImageModel?.PixelHeight ?? 0, imageModel.Rotation, vm); if (SettingsHelper.Settings.WindowProperties.AutoFit) { - WindowHelper.CenterWindowOnScreen(); + WindowFunctions.CenterWindowOnScreen(); } }, DispatcherPriority.Send); diff --git a/src/PicView.Avalonia/Navigation/Slideshow.cs b/src/PicView.Avalonia/Navigation/Slideshow.cs index 57b35f16..1f2ad092 100644 --- a/src/PicView.Avalonia/Navigation/Slideshow.cs +++ b/src/PicView.Avalonia/Navigation/Slideshow.cs @@ -3,6 +3,7 @@ using PicView.Avalonia.Keybindings; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Navigation; using Timer = System.Timers.Timer; @@ -43,10 +44,10 @@ public static void StopSlideshow(MainViewModel vm) if (!SettingsHelper.Settings.WindowProperties.Fullscreen) { - WindowHelper.Restore(vm, Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime); + WindowFunctions.Restore(vm, Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime); if (SettingsHelper.Settings.WindowProperties.AutoFit) { - WindowHelper.CenterWindowOnScreen(); + WindowFunctions.CenterWindowOnScreen(); } } @@ -100,7 +101,7 @@ private static async Task Start(MainViewModel vm, double seconds) if (!SettingsHelper.Settings.WindowProperties.Fullscreen) { - await WindowHelper.ToggleFullscreen(vm, false); + await WindowFunctions.ToggleFullscreen(vm, false); } await vm.ImageIterator.NextIteration(NavigateTo.Next); diff --git a/src/PicView.Avalonia/Navigation/UpdateImage.cs b/src/PicView.Avalonia/Navigation/UpdateImage.cs index 32f6940a..39bf0822 100644 --- a/src/PicView.Avalonia/Navigation/UpdateImage.cs +++ b/src/PicView.Avalonia/Navigation/UpdateImage.cs @@ -6,6 +6,7 @@ using PicView.Avalonia.ImageHandling; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Gallery; using PicView.Core.Navigation; @@ -56,7 +57,7 @@ await Dispatcher.UIThread.InvokeAsync(() => vm.ImageType = preLoadValue.ImageModel.ImageType; - WindowHelper.SetSize(preLoadValue.ImageModel.PixelWidth, preLoadValue.ImageModel.PixelHeight, + WindowResizing.SetSize(preLoadValue.ImageModel.PixelWidth, preLoadValue.ImageModel.PixelHeight, nextPreloadValue?.ImageModel?.PixelWidth ?? 0, nextPreloadValue?.ImageModel?.PixelHeight ?? 0, preLoadValue.ImageModel.Rotation, vm); @@ -68,7 +69,7 @@ await Dispatcher.UIThread.InvokeAsync(() => if (SettingsHelper.Settings.WindowProperties.KeepCentered) { - await Dispatcher.UIThread.InvokeAsync(() => { WindowHelper.CenterWindowOnScreen(); }); + await Dispatcher.UIThread.InvokeAsync(() => { WindowFunctions.CenterWindowOnScreen(); }); } if (vm.SelectedGalleryItemIndex != index) @@ -139,7 +140,7 @@ public static void SetSingleImage(object source, ImageType imageType, string nam Dispatcher.UIThread.Invoke(() => { - WindowHelper.SetSize(width, height, 0, 0, 0, vm); + WindowResizing.SetSize(width, height, 0, 0, 0, vm); }); if (vm.RotationAngle != 0) diff --git a/src/PicView.Avalonia/UI/FunctionsHelper.cs b/src/PicView.Avalonia/UI/FunctionsHelper.cs index 48e1880d..fdd933f0 100644 --- a/src/PicView.Avalonia/UI/FunctionsHelper.cs +++ b/src/PicView.Avalonia/UI/FunctionsHelper.cs @@ -11,6 +11,7 @@ using PicView.Avalonia.ImageTransformations; using PicView.Avalonia.Navigation; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.FileHandling; using PicView.Core.ImageDecoding; @@ -66,6 +67,7 @@ public static Task> GetFunctionByName(string functionName) // Window functions "Fullscreen" => Fullscreen, + "ToggleFullscreen" => ToggleFullscreen, "SetTopMost" => SetTopMost, "Close" => Close, "ToggleInterface" => ToggleInterface, @@ -426,7 +428,7 @@ public static async Task Close() if (SettingsHelper.Settings.WindowProperties.Fullscreen) { - await WindowHelper.MaximizeRestore(); + await WindowFunctions.MaximizeRestore(); return; } if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) @@ -457,7 +459,7 @@ public static async Task Center() // TODO: scroll to center when the gallery is open await Dispatcher.UIThread.InvokeAsync(() => { - WindowHelper.CenterWindowOnScreen(); + WindowFunctions.CenterWindowOnScreen(); }); } @@ -508,36 +510,47 @@ public static Task SettingsWindow() public static async Task Stretch() { - await WindowHelper.Stretch(Vm); + await WindowFunctions.Stretch(Vm); } public static async Task AutoFitWindow() { - await WindowHelper.ToggleAutoFit(Vm); + await WindowFunctions.ToggleAutoFit(Vm); } public static async Task AutoFitWindowAndStretch() { - await WindowHelper.AutoFitAndStretch(Vm); + await WindowFunctions.AutoFitAndStretch(Vm); } public static async Task NormalWindow() { - await WindowHelper.NormalWindow(Vm); + await WindowFunctions.NormalWindow(Vm); } public static async Task NormalWindowAndStretch() { - await WindowHelper.NormalWindowStretch(Vm); + await WindowFunctions.NormalWindowStretch(Vm); } - public static async Task Fullscreen() + public static async Task ToggleFullscreen() { if (Vm is null) { return; } - await WindowHelper.ToggleFullscreen(Vm); + await WindowFunctions.ToggleFullscreen(Vm); + } + + public static Task Fullscreen() + { + if (Vm is null) + { + return Task.CompletedTask; + } + + WindowFunctions.Fullscreen(Vm, Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime); + return Task.CompletedTask; } public static async Task SetTopMost() @@ -547,7 +560,7 @@ public static async Task SetTopMost() return; } - await WindowHelper.ToggleTopMost(Vm); + await WindowFunctions.ToggleTopMost(Vm); } #endregion diff --git a/src/PicView.Avalonia/UI/HideInterfaceLogic.cs b/src/PicView.Avalonia/UI/HideInterfaceLogic.cs index 74525495..86fb75d9 100644 --- a/src/PicView.Avalonia/UI/HideInterfaceLogic.cs +++ b/src/PicView.Avalonia/UI/HideInterfaceLogic.cs @@ -4,6 +4,7 @@ using PicView.Avalonia.Gallery; using PicView.Avalonia.Navigation; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Calculations; using PicView.Core.Config; using PicView.Core.Gallery; @@ -85,7 +86,7 @@ await Dispatcher.UIThread.InvokeAsync(() => } } - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); UIHelper.CloseMenus(vm); await SettingsHelper.SaveSettingsAsync(); } @@ -111,7 +112,7 @@ public static async Task ToggleBottomToolbar(MainViewModel vm) } await Dispatcher.UIThread.InvokeAsync(() => { - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); }); await SettingsHelper.SaveSettingsAsync(); diff --git a/src/PicView.Avalonia/UI/StartUpHelper.cs b/src/PicView.Avalonia/UI/StartUpHelper.cs index b45b6f19..6c8bf1e5 100644 --- a/src/PicView.Avalonia/UI/StartUpHelper.cs +++ b/src/PicView.Avalonia/UI/StartUpHelper.cs @@ -2,12 +2,14 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Primitives; +using Avalonia.Threading; using PicView.Avalonia.ColorManagement; using PicView.Avalonia.Keybindings; using PicView.Avalonia.Navigation; using PicView.Avalonia.SettingsManagement; using PicView.Avalonia.ViewModels; using PicView.Avalonia.Views; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Gallery; using PicView.Core.ProcessHandling; @@ -38,7 +40,7 @@ private static void InitializeWindowForNoSettings(Window w, MainViewModel vm) { w.Height = w.MinHeight; w.Width = w.MinWidth; - WindowHelper.CenterWindowOnScreen(); + WindowFunctions.CenterWindowOnScreen(); vm.CanResize = true; vm.IsAutoFit = false; } @@ -106,11 +108,17 @@ private static void ApplyWindowSizeAndPosition(MainViewModel vm, IClassicDesktop { if (SettingsHelper.Settings.WindowProperties.Fullscreen) { - WindowHelper.Fullscreen(vm, desktop); + // Need to delay it or it won't render properly + Dispatcher.UIThread.Post(() => + { + w.Topmost = true; + WindowFunctions.CenterWindowOnScreen(); + WindowFunctions.Fullscreen(vm, desktop); + }, DispatcherPriority.ContextIdle); } else if (SettingsHelper.Settings.WindowProperties.Maximized) { - WindowHelper.Maximize(); + WindowFunctions.Maximize(); } else if (SettingsHelper.Settings.WindowProperties.AutoFit) { @@ -123,7 +131,7 @@ private static void ApplyWindowSizeAndPosition(MainViewModel vm, IClassicDesktop { vm.CanResize = true; vm.IsAutoFit = false; - WindowHelper.InitializeWindowSizeAndPosition(w); + WindowFunctions.InitializeWindowSizeAndPosition(w); } } diff --git a/src/PicView.Avalonia/UI/UIHelper.cs b/src/PicView.Avalonia/UI/UIHelper.cs index 1450ebcb..8f0c6218 100644 --- a/src/PicView.Avalonia/UI/UIHelper.cs +++ b/src/PicView.Avalonia/UI/UIHelper.cs @@ -11,6 +11,7 @@ using PicView.Avalonia.Views; using PicView.Avalonia.Views.UC; using PicView.Avalonia.Views.UC.Menus; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Gallery; using PicView.Core.Localization; @@ -149,7 +150,7 @@ public static async Task SideBySide(MainViewModel vm) SettingsHelper.Settings.ImageScaling.ShowImageSideBySide = false; vm.IsShowingSideBySide = false; vm.SecondaryImageSource = null; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); } else { @@ -161,7 +162,7 @@ public static async Task SideBySide(MainViewModel vm) vm.SecondaryImageSource = preloadValue?.ImageModel.Image; Dispatcher.UIThread.InvokeAsync(() => { - WindowHelper.SetSize(vm.ImageWidth, vm.ImageHeight, preloadValue.ImageModel.PixelWidth, + WindowResizing.SetSize(vm.ImageWidth, vm.ImageHeight, preloadValue.ImageModel.PixelWidth, preloadValue.ImageModel.PixelHeight, vm.RotationAngle, vm); }); } @@ -187,7 +188,7 @@ public static void ToggleScroll(MainViewModel vm) SettingsHelper.Settings.Zoom.ScrollEnabled = true; } - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); } public static async Task Flip(MainViewModel vm) diff --git a/src/PicView.Avalonia/ViewModels/MainViewModel.cs b/src/PicView.Avalonia/ViewModels/MainViewModel.cs index 222eed41..34d875e0 100644 --- a/src/PicView.Avalonia/ViewModels/MainViewModel.cs +++ b/src/PicView.Avalonia/ViewModels/MainViewModel.cs @@ -14,6 +14,7 @@ using PicView.Avalonia.Interfaces; using PicView.Avalonia.Navigation; using PicView.Avalonia.UI; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Calculations; using PicView.Core.Config; using PicView.Core.FileHandling; @@ -411,7 +412,6 @@ public bool IsFillSquareMenuChecked public ReactiveCommand? MaximizeCommand { get; } public ReactiveCommand? ToggleFullscreenCommand { get; } - public ReactiveCommand? NextCommand { get; } public ReactiveCommand? NextButtonCommand { get; } public ReactiveCommand? NextArrowButtonCommand { get; } @@ -630,7 +630,7 @@ public bool IsStretched { this.RaiseAndSetIfChanged(ref _isStretched, value); SettingsHelper.Settings.ImageScaling.StretchImage = value; - WindowHelper.SetSize(this); + WindowResizing.SetSize(this); } } @@ -1666,10 +1666,10 @@ public MainViewModel(IPlatformSpecificService? platformSpecificService) #region Window commands - ExitCommand = ReactiveCommand.CreateFromTask(WindowHelper.Close); - MinimizeCommand = ReactiveCommand.CreateFromTask(WindowHelper.Minimize); - MaximizeCommand = ReactiveCommand.CreateFromTask(WindowHelper.MaximizeRestore); - ToggleFullscreenCommand = ReactiveCommand.CreateFromTask(FunctionsHelper.Fullscreen); + ExitCommand = ReactiveCommand.CreateFromTask(WindowFunctions.Close); + MinimizeCommand = ReactiveCommand.CreateFromTask(WindowFunctions.Minimize); + MaximizeCommand = ReactiveCommand.CreateFromTask(WindowFunctions.MaximizeRestore); + ToggleFullscreenCommand = ReactiveCommand.CreateFromTask(FunctionsHelper.ToggleFullscreen); NewWindowCommand = ReactiveCommand.Create(ProcessHelper.StartNewProcess); ShowExifWindowCommand = ReactiveCommand.Create(platformSpecificService.ShowExifWindow); diff --git a/src/PicView.Avalonia/Views/BottomBar.axaml.cs b/src/PicView.Avalonia/Views/BottomBar.axaml.cs index 71639755..ffdca1ca 100644 --- a/src/PicView.Avalonia/Views/BottomBar.axaml.cs +++ b/src/PicView.Avalonia/Views/BottomBar.axaml.cs @@ -3,6 +3,7 @@ using Avalonia.Input; using Avalonia.Media; using PicView.Avalonia.UI; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; namespace PicView.Avalonia.Views; @@ -75,6 +76,6 @@ private void MoveWindow(PointerPressedEventArgs e) { if (VisualRoot is null) { return; } - WindowHelper.WindowDragBehavior((Window)VisualRoot, e); + WindowFunctions.WindowDragBehavior((Window)VisualRoot, e); } } \ No newline at end of file diff --git a/src/PicView.Avalonia/Views/ImageViewer.axaml.cs b/src/PicView.Avalonia/Views/ImageViewer.axaml.cs index bedf46fd..ba2f5e94 100644 --- a/src/PicView.Avalonia/Views/ImageViewer.axaml.cs +++ b/src/PicView.Avalonia/Views/ImageViewer.axaml.cs @@ -10,6 +10,7 @@ using PicView.Avalonia.Navigation; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.ImageDecoding; using PicView.Core.ImageTransformations; @@ -504,7 +505,7 @@ public void Rotate(bool clockWise) }); } - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); MainImage.InvalidateVisual(); } @@ -515,7 +516,7 @@ public void Rotate(double angle) var rotateTransform = new RotateTransform(angle); ImageLayoutTransformControl.LayoutTransform = rotateTransform; - WindowHelper.SetSize(DataContext as MainViewModel); + WindowResizing.SetSize(DataContext as MainViewModel); MainImage.InvalidateVisual(); }); } diff --git a/src/PicView.Avalonia/Views/MainView.axaml.cs b/src/PicView.Avalonia/Views/MainView.axaml.cs index 17fa1bc6..1bbbad5a 100644 --- a/src/PicView.Avalonia/Views/MainView.axaml.cs +++ b/src/PicView.Avalonia/Views/MainView.axaml.cs @@ -8,6 +8,7 @@ using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; using PicView.Avalonia.Views.UC; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; using PicView.Core.Extensions; @@ -48,7 +49,7 @@ private void PointerPressedBehavior(object? sender, PointerPressedEventArgs e) if (MainKeyboardShortcuts.ShiftDown) { var hostWindow = (Window)VisualRoot!; - WindowHelper.WindowDragBehavior(hostWindow, e); + WindowFunctions.WindowDragBehavior(hostWindow, e); } MainKeyboardShortcuts.ClearKeyDownModifiers(); diff --git a/src/PicView.Avalonia/Views/UC/BottomGalleryItemSizeSlider.axaml.cs b/src/PicView.Avalonia/Views/UC/BottomGalleryItemSizeSlider.axaml.cs index 0f30a771..9eff20d6 100644 --- a/src/PicView.Avalonia/Views/UC/BottomGalleryItemSizeSlider.axaml.cs +++ b/src/PicView.Avalonia/Views/UC/BottomGalleryItemSizeSlider.axaml.cs @@ -3,6 +3,7 @@ using PicView.Avalonia.Gallery; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; namespace PicView.Avalonia.Views.UC; @@ -30,7 +31,7 @@ private void BottomGallery_OnValueChanged(object? sender, RangeBaseValueChangedE { vm.GetGalleryItemHeight = e.NewValue; UIHelper.GetGalleryView.Height = vm.GalleryHeight; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); } // Binding to height depends on timing of the update. Maybe find a cleaner mvvm solution one day diff --git a/src/PicView.Avalonia/Views/UC/FullGalleryItemSizeSlider.axaml.cs b/src/PicView.Avalonia/Views/UC/FullGalleryItemSizeSlider.axaml.cs index d205ef43..fdbc8352 100644 --- a/src/PicView.Avalonia/Views/UC/FullGalleryItemSizeSlider.axaml.cs +++ b/src/PicView.Avalonia/Views/UC/FullGalleryItemSizeSlider.axaml.cs @@ -1,8 +1,8 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using PicView.Avalonia.Gallery; -using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; namespace PicView.Avalonia.Views.UC; @@ -29,7 +29,7 @@ private void FullGallery_OnValueChanged(object? sender, RangeBaseValueChangedEve if (GalleryFunctions.IsFullGalleryOpen) { vm.GetGalleryItemHeight = vm.GetFullGalleryItemHeight; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); } // Binding to height depends on timing of the update. Maybe find a cleaner mvvm solution one day diff --git a/src/PicView.Avalonia/Views/UC/GalleryItemSizeSlider.axaml.cs b/src/PicView.Avalonia/Views/UC/GalleryItemSizeSlider.axaml.cs index f99ff19f..626df624 100644 --- a/src/PicView.Avalonia/Views/UC/GalleryItemSizeSlider.axaml.cs +++ b/src/PicView.Avalonia/Views/UC/GalleryItemSizeSlider.axaml.cs @@ -3,6 +3,7 @@ using PicView.Avalonia.Gallery; using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; +using PicView.Avalonia.WindowBehavior; using PicView.Core.Config; namespace PicView.Avalonia.Views.UC; @@ -49,7 +50,7 @@ private void RangeBase_OnValueChanged(object? sender, RangeBaseValueChangedEvent } vm.GetFullGalleryItemHeight = e.NewValue; vm.GetGalleryItemHeight = vm.GetFullGalleryItemHeight; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); // Binding to height depends on timing of the update. Maybe find a cleaner mvvm solution one day // Maybe save this on close or some other way @@ -67,7 +68,7 @@ private void RangeBase_OnValueChanged(object? sender, RangeBaseValueChangedEvent vm.GetGalleryItemHeight = e.NewValue; UIHelper.GetGalleryView.Height = vm.GalleryHeight; - WindowHelper.SetSize(vm); + WindowResizing.SetSize(vm); // Binding to height depends on timing of the update. Maybe find a cleaner mvvm solution one day // Maybe save this on close or some other way diff --git a/src/PicView.Avalonia/UI/WindowHelper.cs b/src/PicView.Avalonia/WindowBehavior/WindowFunctions.cs similarity index 60% rename from src/PicView.Avalonia/UI/WindowHelper.cs rename to src/PicView.Avalonia/WindowBehavior/WindowFunctions.cs index 83255c4f..ab21c693 100644 --- a/src/PicView.Avalonia/UI/WindowHelper.cs +++ b/src/PicView.Avalonia/WindowBehavior/WindowFunctions.cs @@ -3,157 +3,66 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; -using Avalonia.Media.Imaging; using Avalonia.Threading; -using ImageMagick; using PicView.Avalonia.Keybindings; using PicView.Avalonia.Navigation; +using PicView.Avalonia.UI; using PicView.Avalonia.ViewModels; using PicView.Core.ArchiveHandling; using PicView.Core.Calculations; using PicView.Core.Config; using PicView.Core.FileHandling; -using PicView.Core.Navigation; -namespace PicView.Avalonia.UI; +namespace PicView.Avalonia.WindowBehavior; -public static class WindowHelper +public static class WindowFunctions { - #region Window Dragging and size changing - - public static void WindowDragAndDoubleClickBehavior(Window window, PointerPressedEventArgs e) - { - if (e.ClickCount == 2 && e.GetCurrentPoint(window).Properties.IsLeftButtonPressed) - { - _ = MaximizeRestore(); - return; - } - - var currentScreen = ScreenHelper.ScreenSize; - window.BeginMoveDrag(e); - var screen = window.Screens.ScreenFromVisual(window); - if (screen != null) - { - if (screen.WorkingArea.Width != currentScreen.WorkingAreaWidth || - screen.WorkingArea.Height != currentScreen.WorkingAreaHeight || screen.Scaling != currentScreen.Scaling) - { - ScreenHelper.UpdateScreenSize(window); - SetSize(window.DataContext as MainViewModel); - } - } - } - - public static void WindowDragBehavior(Window window, PointerPressedEventArgs e) + public static async Task WindowClosingBehavior(Window window) { - var currentScreen = ScreenHelper.ScreenSize; - window.BeginMoveDrag(e); - var screen = window.Screens.ScreenFromVisual(window); - if (screen != null) + if (!SettingsHelper.Settings.WindowProperties.Maximized || + !SettingsHelper.Settings.WindowProperties.Fullscreen || SettingsHelper.Settings.WindowProperties.AutoFit) { - if (screen.WorkingArea.Width != currentScreen.WorkingAreaWidth || - screen.WorkingArea.Height != currentScreen.WorkingAreaHeight || screen.Scaling != currentScreen.Scaling) - { - ScreenHelper.UpdateScreenSize(window); - SetSize(window.DataContext as MainViewModel); - } + WindowResizing.SaveSize(window); } - } - public static void InitializeWindowSizeAndPosition(Window window) - { if (Dispatcher.UIThread.CheckAccess()) { - window.Position = new PixelPoint((int)SettingsHelper.Settings.WindowProperties.Left, (int)SettingsHelper.Settings.WindowProperties.Top); - window.Width = SettingsHelper.Settings.WindowProperties.Width; - window.Height = SettingsHelper.Settings.WindowProperties.Height; + window.Hide(); } else { - Dispatcher.UIThread.InvokeAsync(() => - { - window.Position = new PixelPoint((int)SettingsHelper.Settings.WindowProperties.Left, (int)SettingsHelper.Settings.WindowProperties.Top); - window.Width = SettingsHelper.Settings.WindowProperties.Width; - window.Height = SettingsHelper.Settings.WindowProperties.Height; - }); - } - } - - public static void HandleWindowResize(Window window, AvaloniaPropertyChangedEventArgs size) - { - if (!SettingsHelper.Settings.WindowProperties.AutoFit) - { - return; - } - - if (!size.OldValue.HasValue || !size.NewValue.HasValue) - { - return; - } - - if (size.OldValue.Value.Width == 0 || size.OldValue.Value.Height == 0 || - size.NewValue.Value.Width == 0 || size.NewValue.Value.Height == 0) - { - return; + await Dispatcher.UIThread.InvokeAsync(window.Hide); } - if (size.Sender != window) + var vm = window.DataContext as MainViewModel; + string lastFile; + if (NavigationHelper.CanNavigate(vm)) { - return; + if (!string.IsNullOrEmpty(ArchiveExtraction.LastOpenedArchive)) + { + lastFile = ArchiveExtraction.LastOpenedArchive; + } + else + { + lastFile = vm?.FileInfo?.FullName ?? FileHistoryNavigation.GetLastFile(); + } } - - var x = (size.OldValue.Value.Width - size.NewValue.Value.Width) / 2; - var y = (size.OldValue.Value.Height - size.NewValue.Value.Height) / 2; - - window.Position = new PixelPoint(window.Position.X + (int)x, window.Position.Y + (int)y); - } - - public static void CenterWindowOnScreen(bool horizontal = true) - { - if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + else { - return; + var url = vm?.Title.GetURL(); + lastFile = !string.IsNullOrWhiteSpace(url) ? url : FileHistoryNavigation.GetLastFile(); } - - Dispatcher.UIThread.Post(() => - { - var window = desktop.MainWindow; - - // Get the screen that the window is currently on - var screens = window.Screens; - var screen = screens.ScreenFromVisual(window); - - if (screen == null) - { - return; // No screen found (edge case) - } - - // Get the scaling factor of the screen (DPI scaling) - var scalingFactor = screen.Scaling; - // Get the current screen's bounds (in physical pixels, not adjusted for scaling) - var screenBounds = screen.WorkingArea; - - // Calculate the actual bounds in logical units (adjusting for scaling) - var screenWidth = screenBounds.Width / scalingFactor; - var screenHeight = screenBounds.Height / scalingFactor; - - // Get the size of the window - var windowSize = window.ClientSize; - - // Calculate the position to center the window on the screen - var centeredX = screenBounds.X + (screenWidth - windowSize.Width) / 2; - var centeredY = screenBounds.Y + (screenHeight - windowSize.Height) / 2; - - // Set the window's new position - window.Position = horizontal ? - new PixelPoint((int)centeredX, (int)centeredY) : - new PixelPoint(window.Position.X, (int)centeredY); - }); + SettingsHelper.Settings.StartUp.LastFile = lastFile; + await SettingsHelper.SaveSettingsAsync(); + await KeybindingsHelper.UpdateKeyBindingsFile(); // Save keybindings + FileDeletionHelper.DeleteTempFiles(); + FileHistoryNavigation.WriteToFile(); + ArchiveExtraction.Cleanup(); + Environment.Exit(0); } - #endregion Window Dragging and size changing - - #region Change window behavior + #region Window State Management public static async Task ToggleTopMost(MainViewModel vm) { @@ -194,7 +103,8 @@ public static async Task ToggleAutoFit(MainViewModel vm) SettingsHelper.Settings.WindowProperties.AutoFit = true; vm.IsAutoFit = true; } - SetSize(vm); + + WindowResizing.SetSize(vm); await Dispatcher.UIThread.InvokeAsync(() => CenterWindowOnScreen(false)); await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } @@ -209,7 +119,6 @@ public static async Task AutoFitAndStretch(MainViewModel vm) SettingsHelper.Settings.ImageScaling.StretchImage = false; vm.IsStretched = false; vm.IsAutoFit = false; - } else { @@ -220,7 +129,8 @@ public static async Task AutoFitAndStretch(MainViewModel vm) vm.IsAutoFit = true; vm.IsStretched = true; } - SetSize(vm); + + WindowResizing.SetSize(vm); await Dispatcher.UIThread.InvokeAsync(() => CenterWindowOnScreen(false)); await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } @@ -230,11 +140,11 @@ public static async Task NormalWindow(MainViewModel vm) vm.SizeToContent = SizeToContent.Manual; vm.CanResize = true; SettingsHelper.Settings.WindowProperties.AutoFit = false; - SetSize(vm); + WindowResizing.SetSize(vm); vm.ImageViewer.MainImage.InvalidateVisual(); await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } - + public static async Task NormalWindowStretch(MainViewModel vm) { vm.SizeToContent = SizeToContent.Manual; @@ -242,16 +152,16 @@ public static async Task NormalWindowStretch(MainViewModel vm) SettingsHelper.Settings.WindowProperties.AutoFit = false; SettingsHelper.Settings.ImageScaling.StretchImage = true; vm.IsStretched = true; - SetSize(vm); + WindowResizing.SetSize(vm); vm.ImageViewer.MainImage.InvalidateVisual(); await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } - + public static async Task Stretch(MainViewModel vm) { SettingsHelper.Settings.ImageScaling.StretchImage = true; vm.IsStretched = true; - SetSize(vm); + WindowResizing.SetSize(vm); vm.ImageViewer.MainImage.InvalidateVisual(); await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } @@ -266,22 +176,22 @@ public static async Task ToggleFullscreen(MainViewModel vm, bool saveSettings = if (SettingsHelper.Settings.WindowProperties.Fullscreen) { vm.IsFullscreen = false; - await Dispatcher.UIThread.InvokeAsync(() => + await Dispatcher.UIThread.InvokeAsync(() => desktop.MainWindow.WindowState = WindowState.Normal); if (saveSettings) { SettingsHelper.Settings.WindowProperties.Fullscreen = false; } - + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - RestoreSize(desktop.MainWindow); + WindowResizing.RestoreSize(desktop.MainWindow); Restore(vm, desktop); } } else { - SaveSize(desktop.MainWindow); + WindowResizing.SaveSize(desktop.MainWindow); Fullscreen(vm, desktop); if (saveSettings) { @@ -291,13 +201,14 @@ await Dispatcher.UIThread.InvokeAsync(() => await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } - + public static async Task MaximizeRestore() { if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) { return; } + var vm = desktop.MainWindow.DataContext as MainViewModel; // Restore if (desktop.MainWindow.WindowState is WindowState.Maximized or WindowState.FullScreen) @@ -309,11 +220,12 @@ public static async Task MaximizeRestore() { if (!SettingsHelper.Settings.WindowProperties.AutoFit) { - SaveSize(desktop.MainWindow); + WindowResizing.SaveSize(desktop.MainWindow); } + Maximize(); } - + await SettingsHelper.SaveSettingsAsync().ConfigureAwait(false); } @@ -330,16 +242,18 @@ public static void Restore(MainViewModel vm, IClassicDesktopStyleApplicationLife vm.IsBottomToolbarShown = true; vm.BottombarHeight = SizeDefaults.BottombarHeight; } + vm.IsUIShown = true; } } - Dispatcher.UIThread.InvokeAsync(() => + + Dispatcher.UIThread.InvokeAsync(() => desktop.MainWindow.WindowState = WindowState.Normal); SettingsHelper.Settings.WindowProperties.Maximized = false; SettingsHelper.Settings.WindowProperties.Fullscreen = false; vm.IsUIShown = SettingsHelper.Settings.UIProperties.ShowInterface; InitializeWindowSizeAndPosition(desktop.MainWindow); - SetSize(vm); + WindowResizing.SetSize(vm); if (SettingsHelper.Settings.WindowProperties.AutoFit) { vm.SizeToContent = SizeToContent.WidthAndHeight; @@ -355,7 +269,7 @@ public static void Restore(MainViewModel vm, IClassicDesktopStyleApplicationLife public static void Maximize() { // TODO: Fix incorrect size for bottom button bar - + if (Dispatcher.UIThread.CheckAccess()) { Set(); @@ -366,12 +280,14 @@ public static void Maximize() } return; + void Set() { if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) { return; } + var vm = desktop.MainWindow.DataContext as MainViewModel; if (SettingsHelper.Settings.WindowProperties.AutoFit) { @@ -380,7 +296,7 @@ void Set() } desktop.MainWindow.WindowState = WindowState.Maximized; - SetSize(desktop.MainWindow.DataContext as MainViewModel); + WindowResizing.SetSize(desktop.MainWindow.DataContext as MainViewModel); SettingsHelper.Settings.WindowProperties.Maximized = true; } } @@ -398,7 +314,7 @@ public static void Fullscreen(MainViewModel vm, IClassicDesktopStyleApplicationL { Dispatcher.UIThread.Invoke(() => desktop.MainWindow.WindowState = WindowState.FullScreen); } - + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { vm.IsTopToolbarShown = false; // Hide interface in fullscreen. Remember to turn back when exiting fullscreen @@ -446,6 +362,7 @@ public static void Fullscreen(MainViewModel vm, IClassicDesktopStyleApplicationL // TODO go to macOS fullscreen mode when auto fit is on } } + vm.GalleryWidth = double.NaN; } @@ -455,292 +372,132 @@ public static async Task Minimize() { return; } - await Dispatcher.UIThread.InvokeAsync(() => + + await Dispatcher.UIThread.InvokeAsync(() => desktop.MainWindow.WindowState = WindowState.Minimized); } - + public static async Task Close() { if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) { return; } - await Dispatcher.UIThread.InvokeAsync(() => + + await Dispatcher.UIThread.InvokeAsync(() => desktop.MainWindow.Close()); } - #endregion Change window behavior + #endregion - #region Set and save Size + #region Window Size and Position - public static void SetSize(MainViewModel vm) + public static void CenterWindowOnScreen(bool horizontal = true) { - double firstWidth, firstHeight; - var preloadValue = vm.ImageIterator?.GetCurrentPreLoadValue(); - if (preloadValue == null) - { - if (vm.FileInfo is null) - { - if (vm.ImageSource is Bitmap bitmap) - { - firstWidth = bitmap.PixelSize.Width; - firstHeight = bitmap.PixelSize.Height; - } - else - return; - } - else - { - var magickImage = new MagickImage(); - magickImage.Ping(vm.FileInfo); - firstWidth = magickImage.Width; - firstHeight = magickImage.Height; - } - } - else + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) { - firstWidth = GetWidth(preloadValue); - firstHeight = GetHeight(preloadValue); + return; } - if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide) - { - var secondaryPreloadValue = vm.ImageIterator?.GetNextPreLoadValue(); - double secondWidth, secondHeight; - if (secondaryPreloadValue != null) - { - secondWidth = GetWidth(secondaryPreloadValue); - secondHeight = GetHeight(secondaryPreloadValue); - } - else if (vm.ImageIterator is not null) - { - var nextIndex = vm.ImageIterator.GetIteration(vm.ImageIterator.CurrentIndex, vm.ImageIterator.IsReversed ? NavigateTo.Previous : NavigateTo.Next); - var magickImage = new MagickImage(); - magickImage.Ping(vm.ImageIterator.ImagePaths[nextIndex]); - secondWidth = magickImage.Width; - secondHeight = magickImage.Height; - } - else - { - secondWidth = 0; - secondHeight = 0; - } - if (Dispatcher.UIThread.CheckAccess()) - { - SetSize(firstWidth, firstHeight, secondWidth, secondHeight, vm.RotationAngle, vm); - } - else - { - Dispatcher.UIThread.InvokeAsync(() => SetSize(firstWidth, firstHeight, secondWidth, secondHeight, vm.RotationAngle, vm)); - } - } - else + Dispatcher.UIThread.Post(() => { - if (Dispatcher.UIThread.CheckAccess()) - { - SetSize(firstWidth, firstHeight, 0, 0, vm.RotationAngle, vm); - } - else + var window = desktop.MainWindow; + + // Get the screen that the window is currently on + var screens = window.Screens; + var screen = screens.ScreenFromVisual(window); + + if (screen == null) { - Dispatcher.UIThread.InvokeAsync(() => SetSize(firstWidth, firstHeight, 0, 0, vm.RotationAngle, vm)); + return; // No screen found (edge case) } - } - return; - double GetWidth(PreLoader.PreLoadValue preloadValue) - { - return preloadValue?.ImageModel?.PixelWidth ?? vm.ImageWidth; - } - - double GetHeight(PreLoader.PreLoadValue preloadValue) - { - return preloadValue?.ImageModel?.PixelHeight ?? vm.ImageHeight; - } - } - - public static void SetSize(double width, double height, double secondWidth, double secondHeight, double rotation, MainViewModel vm) - { - width = width == 0 ? vm.ImageWidth : width; - height = height == 0 ? vm.ImageHeight : height; - if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) - { - return; - } + // Get the scaling factor of the screen (DPI scaling) + var scalingFactor = screen.Scaling; - var mainView = UIHelper.GetMainView; - if (mainView is null) - { - return; - } + // Get the current screen's bounds (in physical pixels, not adjusted for scaling) + var screenBounds = screen.WorkingArea; - const int padding = 45; - var screenSize = ScreenHelper.ScreenSize; - double desktopMinWidth = 0, desktopMinHeight = 0, containerWidth = 0, containerHeight = 0; - desktopMinWidth = desktop.MainWindow.MinWidth; - desktopMinHeight = desktop.MainWindow.MinHeight; - containerWidth = mainView.Bounds.Width; - containerHeight = mainView.Bounds.Height; + // Calculate the actual bounds in logical units (adjusting for scaling) + var screenWidth = screenBounds.Width / scalingFactor; + var screenHeight = screenBounds.Height / scalingFactor; - if (double.IsNaN(containerWidth) || double.IsNaN(containerHeight) || double.IsNaN(width) || double.IsNaN(height)) - { - return; - } - ImageSizeCalculationHelper.ImageSize size; - if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide && secondWidth > 0 && secondHeight > 0) - { - size = ImageSizeCalculationHelper.GetImageSize( - width, - height, - secondWidth, - secondHeight, - screenSize.WorkingAreaWidth, - screenSize.WorkingAreaHeight, - desktopMinWidth, - desktopMinHeight, - ImageSizeCalculationHelper.GetInterfaceSize(), - rotation, - padding, - screenSize.Scaling, - vm.TitlebarHeight, - vm.BottombarHeight, - vm.GalleryHeight, - containerWidth, - containerHeight); - } - else - { - size = ImageSizeCalculationHelper.GetImageSize( - width, - height, - screenSize.WorkingAreaWidth, - screenSize.WorkingAreaHeight, - desktopMinWidth, - desktopMinHeight, - ImageSizeCalculationHelper.GetInterfaceSize(), - rotation, - padding, - screenSize.Scaling, - vm.TitlebarHeight, - vm.BottombarHeight, - vm.GalleryHeight, - containerWidth, - containerHeight); - } - - vm.TitleMaxWidth = size.TitleMaxWidth; - vm.ImageWidth = size.Width; - vm.SecondaryImageWidth = size.SecondaryWidth; - vm.ImageHeight = size.Height; - vm.GalleryMargin = new Thickness(0, 0, 0, size.Margin); - - vm.ScrollViewerWidth = size.ScrollViewerWidth; - vm.ScrollViewerHeight = size.ScrollViewerHeight; + // Get the size of the window + var windowSize = window.ClientSize; - if (SettingsHelper.Settings.WindowProperties.AutoFit) - { - if (SettingsHelper.Settings.WindowProperties.Fullscreen || SettingsHelper.Settings.WindowProperties.Maximized) - { - vm.GalleryWidth = double.NaN; - } - else - { - vm.GalleryWidth = vm.RotationAngle is 90 or 270 ? - Math.Max(size.Height, desktopMinHeight) : - Math.Max(size.Width, desktopMinWidth); - } - } - else - { - vm.GalleryWidth = double.NaN;; - } - } - - public static void SaveSize(Window window) - { - if (Dispatcher.UIThread.CheckAccess()) - { - Set(); - } - else - { - Dispatcher.UIThread.InvokeAsync(Set); - } + // Calculate the position to center the window on the screen + var centeredX = screenBounds.X + (screenWidth - windowSize.Width) / 2; + var centeredY = screenBounds.Y + (screenHeight - windowSize.Height) / 2; - return; - void Set() - { - var top = window.Position.Y; - var left = window.Position.X; - SettingsHelper.Settings.WindowProperties.Top = top; - SettingsHelper.Settings.WindowProperties.Left = left; - SettingsHelper.Settings.WindowProperties.Width = window.Width; - SettingsHelper.Settings.WindowProperties.Height = window.Height; - } + // Set the window's new position + window.Position = horizontal + ? new PixelPoint((int)centeredX, (int)centeredY) + : new PixelPoint(window.Position.X, (int)centeredY); + }); } - - public static void RestoreSize(Window window) + + public static void InitializeWindowSizeAndPosition(Window window) { if (Dispatcher.UIThread.CheckAccess()) { - Set(); + window.Position = new PixelPoint((int)SettingsHelper.Settings.WindowProperties.Left, + (int)SettingsHelper.Settings.WindowProperties.Top); + window.Width = SettingsHelper.Settings.WindowProperties.Width; + window.Height = SettingsHelper.Settings.WindowProperties.Height; } else { - Dispatcher.UIThread.InvokeAsync(Set); - } - - return; - void Set() - { - var x = (int)SettingsHelper.Settings.WindowProperties.Left; - var y = (int)SettingsHelper.Settings.WindowProperties.Top; - window.Position = new PixelPoint(x, y); - window.Width = SettingsHelper.Settings.WindowProperties.Width; - window.Height = SettingsHelper.Settings.WindowProperties.Height; + Dispatcher.UIThread.InvokeAsync(() => + { + window.Position = new PixelPoint((int)SettingsHelper.Settings.WindowProperties.Left, + (int)SettingsHelper.Settings.WindowProperties.Top); + window.Width = SettingsHelper.Settings.WindowProperties.Width; + window.Height = SettingsHelper.Settings.WindowProperties.Height; + }); } } #endregion - public static async Task WindowClosingBehavior(Window window) + #region Window Drag and Behavior + + public static void WindowDragAndDoubleClickBehavior(Window window, PointerPressedEventArgs e) { - if (Dispatcher.UIThread.CheckAccess()) - { - window.Hide(); - } - else + if (e.ClickCount == 2 && e.GetCurrentPoint(window).Properties.IsLeftButtonPressed) { - await Dispatcher.UIThread.InvokeAsync(window.Hide); + _ = MaximizeRestore(); + return; } - if (!SettingsHelper.Settings.WindowProperties.Maximized || !SettingsHelper.Settings.WindowProperties.Fullscreen || SettingsHelper.Settings.WindowProperties.AutoFit) - { - SaveSize(window); - } - var vm = window.DataContext as MainViewModel; - string lastFile; - if (NavigationHelper.CanNavigate(vm)) + var currentScreen = ScreenHelper.ScreenSize; + window.BeginMoveDrag(e); + var screen = window.Screens.ScreenFromVisual(window); + if (screen != null) { - if (!string.IsNullOrEmpty(ArchiveExtraction.LastOpenedArchive)) - { - lastFile = ArchiveExtraction.LastOpenedArchive; - } - else + if (screen.WorkingArea.Width != currentScreen.WorkingAreaWidth || + screen.WorkingArea.Height != currentScreen.WorkingAreaHeight || screen.Scaling != currentScreen.Scaling) { - lastFile = vm?.FileInfo?.FullName ?? FileHistoryNavigation.GetLastFile(); + ScreenHelper.UpdateScreenSize(window); + WindowResizing.SetSize(window.DataContext as MainViewModel); } } - else + } + + public static void WindowDragBehavior(Window window, PointerPressedEventArgs e) + { + var currentScreen = ScreenHelper.ScreenSize; + window.BeginMoveDrag(e); + var screen = window.Screens.ScreenFromVisual(window); + if (screen != null) { - var url = vm?.Title.GetURL(); - lastFile = !string.IsNullOrWhiteSpace(url) ? url : FileHistoryNavigation.GetLastFile(); + if (screen.WorkingArea.Width != currentScreen.WorkingAreaWidth || + screen.WorkingArea.Height != currentScreen.WorkingAreaHeight || screen.Scaling != currentScreen.Scaling) + { + ScreenHelper.UpdateScreenSize(window); + WindowResizing.SetSize(window.DataContext as MainViewModel); + } } - SettingsHelper.Settings.StartUp.LastFile = lastFile; - await SettingsHelper.SaveSettingsAsync(); - await KeybindingsHelper.UpdateKeyBindingsFile(); // Save keybindings - FileDeletionHelper.DeleteTempFiles(); - FileHistoryNavigation.WriteToFile(); - ArchiveExtraction.Cleanup(); - Environment.Exit(0); } + + #endregion } \ No newline at end of file diff --git a/src/PicView.Avalonia/WindowBehavior/WindowResizing.cs b/src/PicView.Avalonia/WindowBehavior/WindowResizing.cs new file mode 100644 index 00000000..6e2f6954 --- /dev/null +++ b/src/PicView.Avalonia/WindowBehavior/WindowResizing.cs @@ -0,0 +1,294 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Media.Imaging; +using Avalonia.Threading; +using ImageMagick; +using PicView.Avalonia.Navigation; +using PicView.Avalonia.UI; +using PicView.Avalonia.ViewModels; +using PicView.Core.Calculations; +using PicView.Core.Config; +using PicView.Core.Navigation; + +namespace PicView.Avalonia.WindowBehavior; + +public static class WindowResizing +{ + #region Window Resize Handling + + public static void HandleWindowResize(Window window, AvaloniaPropertyChangedEventArgs size) + { + if (!SettingsHelper.Settings.WindowProperties.AutoFit) + { + return; + } + + if (!size.OldValue.HasValue || !size.NewValue.HasValue) + { + return; + } + + if (size.OldValue.Value.Width == 0 || size.OldValue.Value.Height == 0 || + size.NewValue.Value.Width == 0 || size.NewValue.Value.Height == 0) + { + return; + } + + if (size.Sender != window) + { + return; + } + + var x = (size.OldValue.Value.Width - size.NewValue.Value.Width) / 2; + var y = (size.OldValue.Value.Height - size.NewValue.Value.Height) / 2; + + window.Position = new PixelPoint(window.Position.X + (int)x, window.Position.Y + (int)y); + } + + #endregion + + #region Set Window Size + + public static void SetSize(MainViewModel vm) + { + double firstWidth, firstHeight; + var preloadValue = vm.ImageIterator?.GetCurrentPreLoadValue(); + if (preloadValue == null) + { + if (vm.FileInfo is null) + { + if (vm.ImageSource is Bitmap bitmap) + { + firstWidth = bitmap.PixelSize.Width; + firstHeight = bitmap.PixelSize.Height; + } + else + { + return; + } + } + else + { + var magickImage = new MagickImage(); + magickImage.Ping(vm.FileInfo); + firstWidth = magickImage.Width; + firstHeight = magickImage.Height; + } + } + else + { + firstWidth = GetWidth(preloadValue); + firstHeight = GetHeight(preloadValue); + } + + if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide) + { + var secondaryPreloadValue = vm.ImageIterator?.GetNextPreLoadValue(); + double secondWidth, secondHeight; + if (secondaryPreloadValue != null) + { + secondWidth = GetWidth(secondaryPreloadValue); + secondHeight = GetHeight(secondaryPreloadValue); + } + else if (vm.ImageIterator is not null) + { + var nextIndex = vm.ImageIterator.GetIteration(vm.ImageIterator.CurrentIndex, + vm.ImageIterator.IsReversed ? NavigateTo.Previous : NavigateTo.Next); + var magickImage = new MagickImage(); + magickImage.Ping(vm.ImageIterator.ImagePaths[nextIndex]); + secondWidth = magickImage.Width; + secondHeight = magickImage.Height; + } + else + { + secondWidth = 0; + secondHeight = 0; + } + + if (Dispatcher.UIThread.CheckAccess()) + { + SetSize(firstWidth, firstHeight, secondWidth, secondHeight, vm.RotationAngle, vm); + } + else + { + Dispatcher.UIThread.InvokeAsync(() => + SetSize(firstWidth, firstHeight, secondWidth, secondHeight, vm.RotationAngle, vm)); + } + } + else + { + if (Dispatcher.UIThread.CheckAccess()) + { + SetSize(firstWidth, firstHeight, 0, 0, vm.RotationAngle, vm); + } + else + { + Dispatcher.UIThread.InvokeAsync(() => SetSize(firstWidth, firstHeight, 0, 0, vm.RotationAngle, vm)); + } + } + + return; + + double GetWidth(PreLoader.PreLoadValue preloadValue) + { + return preloadValue?.ImageModel?.PixelWidth ?? vm.ImageWidth; + } + + double GetHeight(PreLoader.PreLoadValue preloadValue) + { + return preloadValue?.ImageModel?.PixelHeight ?? vm.ImageHeight; + } + } + + public static void SetSize(double width, double height, double secondWidth, double secondHeight, double rotation, + MainViewModel vm) + { + width = width == 0 ? vm.ImageWidth : width; + height = height == 0 ? vm.ImageHeight : height; + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + { + return; + } + + var mainView = UIHelper.GetMainView; + if (mainView is null) + { + return; + } + + const int padding = 45; + var screenSize = ScreenHelper.ScreenSize; + double desktopMinWidth = 0, desktopMinHeight = 0, containerWidth = 0, containerHeight = 0; + desktopMinWidth = desktop.MainWindow.MinWidth; + desktopMinHeight = desktop.MainWindow.MinHeight; + containerWidth = mainView.Bounds.Width; + containerHeight = mainView.Bounds.Height; + + if (double.IsNaN(containerWidth) || double.IsNaN(containerHeight) || double.IsNaN(width) || + double.IsNaN(height)) + { + return; + } + + ImageSizeCalculationHelper.ImageSize size; + if (SettingsHelper.Settings.ImageScaling.ShowImageSideBySide && secondWidth > 0 && secondHeight > 0) + { + size = ImageSizeCalculationHelper.GetImageSize( + width, + height, + secondWidth, + secondHeight, + screenSize.WorkingAreaWidth, + screenSize.WorkingAreaHeight, + desktopMinWidth, + desktopMinHeight, + ImageSizeCalculationHelper.GetInterfaceSize(), + rotation, + padding, + screenSize.Scaling, + vm.TitlebarHeight, + vm.BottombarHeight, + vm.GalleryHeight, + containerWidth, + containerHeight); + } + else + { + size = ImageSizeCalculationHelper.GetImageSize( + width, + height, + screenSize.WorkingAreaWidth, + screenSize.WorkingAreaHeight, + desktopMinWidth, + desktopMinHeight, + ImageSizeCalculationHelper.GetInterfaceSize(), + rotation, + padding, + screenSize.Scaling, + vm.TitlebarHeight, + vm.BottombarHeight, + vm.GalleryHeight, + containerWidth, + containerHeight); + } + + vm.TitleMaxWidth = size.TitleMaxWidth; + vm.ImageWidth = size.Width; + vm.SecondaryImageWidth = size.SecondaryWidth; + vm.ImageHeight = size.Height; + vm.GalleryMargin = new Thickness(0, 0, 0, size.Margin); + + vm.ScrollViewerWidth = size.ScrollViewerWidth; + vm.ScrollViewerHeight = size.ScrollViewerHeight; + + if (SettingsHelper.Settings.WindowProperties.AutoFit) + { + if (SettingsHelper.Settings.WindowProperties.Fullscreen || + SettingsHelper.Settings.WindowProperties.Maximized) + { + vm.GalleryWidth = double.NaN; + } + else + { + vm.GalleryWidth = vm.RotationAngle is 90 or 270 + ? Math.Max(size.Height, desktopMinHeight) + : Math.Max(size.Width, desktopMinWidth); + } + } + else + { + vm.GalleryWidth = double.NaN; + ; + } + } + + public static void SaveSize(Window window) + { + if (Dispatcher.UIThread.CheckAccess()) + { + Set(); + } + else + { + Dispatcher.UIThread.InvokeAsync(Set); + } + + return; + + void Set() + { + var top = window.Position.Y; + var left = window.Position.X; + SettingsHelper.Settings.WindowProperties.Top = top; + SettingsHelper.Settings.WindowProperties.Left = left; + SettingsHelper.Settings.WindowProperties.Width = window.Width; + SettingsHelper.Settings.WindowProperties.Height = window.Height; + } + } + + public static void RestoreSize(Window window) + { + if (Dispatcher.UIThread.CheckAccess()) + { + Set(); + } + else + { + Dispatcher.UIThread.InvokeAsync(Set); + } + + return; + + void Set() + { + var x = (int)SettingsHelper.Settings.WindowProperties.Left; + var y = (int)SettingsHelper.Settings.WindowProperties.Top; + window.Position = new PixelPoint(x, y); + window.Width = SettingsHelper.Settings.WindowProperties.Width; + window.Height = SettingsHelper.Settings.WindowProperties.Height; + } + } + + #endregion +} \ No newline at end of file