소스 검색

Refactor, add xml docs, misc.

Ruben 6 달 전
부모
커밋
35c1ae448e

+ 71 - 38
src/PicView.Avalonia/CustomControls/AnimatedPopUp.cs

@@ -13,21 +13,22 @@ namespace PicView.Avalonia.CustomControls;
 [TemplatePart("PART_Border", typeof(Border))]
 public class AnimatedPopUp : ContentControl
 {
-    public static readonly AvaloniaProperty<bool> ClickingOutSideClosesProperty =
-        AvaloniaProperty.Register<CopyButton, bool>(nameof(ClickingOutSideCloses));
+    public static readonly AvaloniaProperty<bool> ClickingOutsideClosesProperty =
+        AvaloniaProperty.Register<AnimatedPopUp, bool>(nameof(ClickingOutsideCloses));
 
     private Border? _partBorder;
     private Panel? _partOverlay;
 
+    private const double AnimSpeed = 0.3;
     protected AnimatedPopUp()
     {
         Loaded += async delegate { await AnimatedOpening(); };
     }
 
-    public bool ClickingOutSideCloses
+    public bool ClickingOutsideCloses
     {
-        get => (bool)GetValue(ClickingOutSideClosesProperty)!;
-        set => SetValue(ClickingOutSideClosesProperty, value);
+        get => (bool)GetValue(ClickingOutsideClosesProperty)!;
+        set => SetValue(ClickingOutsideClosesProperty, value);
     }
 
     protected override Type StyleKeyOverride => typeof(AnimatedPopUp);
@@ -38,55 +39,87 @@ public class AnimatedPopUp : ContentControl
         _partOverlay = e.NameScope.Find<Panel>("PART_Overlay");
         _partBorder = e.NameScope.Find<Border>("PART_Border");
 
-        _partOverlay.Opacity = 0;
-        _partBorder.Opacity = 0;
+        if (_partOverlay != null)
+        {
+            _partOverlay.Opacity = 0;
+        }
+
+        if (_partBorder != null)
+        {
+            _partBorder.Opacity = 0;
+        }
+
+        ApplyGlassThemeBackground();
+
+        if (_partOverlay != null)
+        {
+            _partOverlay.PointerPressed += async (_, _) => await OnOverlayPointerPressed();
+        }
+    }
 
-        if (Settings.Theme.GlassTheme)
+    private void ApplyGlassThemeBackground()
+    {
+        if (!Settings.Theme.GlassTheme || _partBorder == null)
+        {
+            return;
+        }
+
+        if (Application.Current.TryGetResource("MenuBackgroundColor",
+                Application.Current.RequestedThemeVariant, out var bgColor)
+            && bgColor is Color color)
+        {
+            _partBorder.Background = new SolidColorBrush(color);
+        }
+    }
+
+    private async Task OnOverlayPointerPressed()
+    {
+        if (!ClickingOutsideCloses)
         {
-            if (Application.Current.TryGetResource("MenuBackgroundColor",
-                    Application.Current.RequestedThemeVariant, out var bgColor))
-            {
-                if (bgColor is Color color)
-                {
-                    _partBorder.Background = new SolidColorBrush(color);
-                }
-            }
+            return;
         }
 
-        // Handle click outside to close
-        _partOverlay.PointerPressed += async delegate
+        if (_partBorder is { IsPointerOver: false })
         {
-            if (!ClickingOutSideCloses)
-            {
-                return;
-            }
-
-            if (!_partBorder.IsPointerOver)
-            {
-                await AnimatedClosing();
-            }
-        };
+            await AnimatedClosing();
+        }
     }
 
-    public async Task AnimatedOpening()
+    // ReSharper disable once MemberCanBePrivate.Global
+    protected async Task AnimatedOpening()
     {
+        const int fromX = 50;
+        const int fromY = 100;
+        const int toX = 0;
+        const int toY = 0;
         DialogManager.IsDialogOpen = true;
-        var fadeIn = AnimationsHelper.OpacityAnimation(0, 1, 0.3);
-        var centering = AnimationsHelper.CenteringAnimation(50, 100, 0, 0, 0.3);
-        await Task.WhenAll(fadeIn.RunAsync(_partOverlay), fadeIn.RunAsync(_partBorder),
-            centering.RunAsync(_partBorder));
+        var fadeIn = AnimationsHelper.OpacityAnimation(0, 1, AnimSpeed);
+        var centering = AnimationsHelper.CenteringAnimation(fromX, fromY, toX, toY, AnimSpeed);
+        await Task.WhenAll(
+            fadeIn.RunAsync(_partOverlay),
+            fadeIn.RunAsync(_partBorder),
+            centering.RunAsync(_partBorder)
+        );
     }
 
-    public async Task AnimatedClosing()
+    protected async Task AnimatedClosing()
     {
+        const int fromX = 0;
+        const int fromY = 0;
+        const int toX = 50;
+        const int toY = 100;
         DialogManager.IsDialogOpen = false;
-        var fadeIn = AnimationsHelper.OpacityAnimation(1, 0, 0.3);
-        var centering = AnimationsHelper.CenteringAnimation(0, 0, 50, 100, 0.3);
-        await Task.WhenAll(fadeIn.RunAsync(_partOverlay), fadeIn.RunAsync(_partBorder),
-            centering.RunAsync(_partBorder));
+        var fadeIn = AnimationsHelper.OpacityAnimation(1, 0, AnimSpeed);
+        var centering = AnimationsHelper.CenteringAnimation(fromX, fromY, toX, toY, AnimSpeed);
+        await Task.WhenAll(
+            fadeIn.RunAsync(_partOverlay),
+            fadeIn.RunAsync(_partBorder),
+            centering.RunAsync(_partBorder)
+        );
         UIHelper.GetMainView.MainGrid.Children.Remove(this);
     }
 
+    // ReSharper disable once UnusedParameter.Global
     public void KeyDownHandler(object? sender, KeyEventArgs e)
     {
         RaiseEvent(e);

+ 3 - 6
src/PicView.Avalonia/CustomControls/ThumbImage.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Media;
+using PicView.Core.DebugTools;
 
 namespace PicView.Avalonia.CustomControls;
 
@@ -21,9 +22,7 @@ public class ThumbImage : Image
         }
         catch (Exception e)
         {
-#if DEBUG
-            Console.WriteLine(e);
-#endif
+            DebugHelper.LogDebug(nameof(ThumbImage), nameof(MeasureOverride), e);
         }
 
         return size ?? new Size();
@@ -42,9 +41,7 @@ public class ThumbImage : Image
         }
         catch (Exception e)
         {
-#if DEBUG
-            Console.WriteLine(e);
-#endif
+            DebugHelper.LogDebug(nameof(ThumbImage), nameof(ArrangeOverride), e);
         }
         return new Size();
     }

+ 4 - 11
src/PicView.Avalonia/DragAndDrop/DragAndDropHelper.cs

@@ -1,5 +1,4 @@
-using System.Runtime.InteropServices;
-using System.Text;
+using System.Text;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Platform.Storage;
@@ -32,9 +31,7 @@ public static class DragAndDropHelper
 
         var storageItems = files as IStorageItem[] ?? files.ToArray();
         var firstFile = storageItems.FirstOrDefault();
-        var path = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
-            ? firstFile.Path.AbsolutePath
-            : firstFile.Path.LocalPath;
+        var path = firstFile.Path.AbsolutePath;
         if (e.Data.Contains("text/x-moz-url"))
         {
             await HandleDropFromUrl(e, vm);
@@ -73,9 +70,7 @@ public static class DragAndDropHelper
         {
             foreach (var file in storageItems.Skip(1))
             {
-                var filepath = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
-                    ? file.Path.AbsolutePath
-                    : file.Path.LocalPath;
+                var filepath = file.Path.AbsolutePath;
                 if (filepath.IsSupported())
                 {
                     ProcessHelper.StartNewProcess(filepath);
@@ -160,9 +155,7 @@ public static class DragAndDropHelper
             return;
         }
         var firstFile = fileArray[0];
-        var path = RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
-            ? firstFile.Path.AbsolutePath
-            : firstFile.Path.LocalPath;
+        var path = firstFile.Path.AbsolutePath;
         if (Directory.Exists(path))
         {
             await Dispatcher.UIThread.InvokeAsync(() =>

+ 4 - 5
src/PicView.Avalonia/Navigation/IPC.cs

@@ -4,6 +4,7 @@ using Avalonia;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Threading;
 using PicView.Avalonia.ViewModels;
+using PicView.Core.DebugTools;
 
 namespace PicView.Avalonia.Navigation;
 
@@ -12,13 +13,14 @@ namespace PicView.Avalonia.Navigation;
 /// Allows multiple instances of the application to communicate, 
 /// enabling the transfer of commands or data between them.
 /// </summary>
+// ReSharper disable once InconsistentNaming
 internal static class IPC
 {
     /// <summary>
     /// The default name for the named pipe used by the application.
     /// This pipe is used to facilitate communication between instances of the application.
     /// </summary>
-    internal const string PipeName = "PicViewPipe";
+    private const string PipeName = "PicViewPipe";
 
     /// <summary>
     /// Sends an argument to a running instance of the application via the specified named pipe.
@@ -53,10 +55,7 @@ internal static class IPC
         }
         catch (Exception ex)
         {
-            // Log the exception if in debug mode
-#if DEBUG
-            Trace.WriteLine($"{nameof(SendArgumentToRunningInstance)} exception: \n{ex}");
-#endif
+            DebugHelper.LogDebug(nameof(IPC), nameof(SendArgumentToRunningInstance), ex);
             return false;
         }
         return true;

+ 1 - 1
src/PicView.Avalonia/Preloading/Preloader.cs

@@ -14,7 +14,7 @@ public class PreLoader : IAsyncDisposable
 #if DEBUG
 
     // ReSharper disable once ConvertToConstant.Local
-    private static readonly bool ShowAddRemove = true;
+    private static readonly bool ShowAddRemove = false;
 #endif
 
     /// <summary>

+ 0 - 1
src/PicView.Avalonia/UI/ScreenHelper.cs

@@ -25,7 +25,6 @@ public static class ScreenHelper
     /// TODO: Add support for dragging between multiple monitors.
     /// Dragging to monitor with different scaling (DPI) causes weird incorrect size behavior,
     /// but starting the application works fine for either monitor, until you drag it to the other.
-    /// It works most of the time in debug mode, but not so much for AOT release.
     /// </remarks>
     public static void UpdateScreenSize(Window window)
     {

+ 1 - 1
src/PicView.Avalonia/Views/UC/PopUps/CloseDialog.axaml

@@ -1,5 +1,5 @@
 <customControls:AnimatedPopUp
-    ClickingOutSideCloses="False"
+    ClickingOutsideCloses="False"
     Padding="20"
     d:DesignHeight="450"
     d:DesignWidth="800"

+ 1 - 1
src/PicView.Avalonia/Views/UC/PopUps/DeleteDialog.axaml

@@ -1,5 +1,5 @@
 <customControls:AnimatedPopUp
-    ClickingOutSideCloses="True"
+    ClickingOutsideCloses="True"
     Padding="20"
     d:DesignHeight="450"
     d:DesignWidth="800"

+ 0 - 1
src/PicView.Avalonia/Views/ZoomSettingsView.axaml.cs

@@ -46,7 +46,6 @@ public partial class ZoomSettingsView : UserControl
                         WindowResizing.SetSize(vm);
                         WindowFunctions.CenterWindowOnScreen();
                     }
-                    
                 });
         };
     }

+ 4 - 27
src/PicView.Avalonia/WindowBehavior/WindowResizing.cs

@@ -95,7 +95,7 @@ public static class WindowResizing
         SetSize(size.Value, vm);
     }
     
-    public static void SetSize(ImageSizeCalculationHelper.ImageSize size, MainViewModel vm)
+    public static void SetSize(ImageSize size, MainViewModel vm)
     {
         vm.TitleMaxWidth = size.TitleMaxWidth;
         vm.PicViewer.ImageWidth = size.Width;
@@ -129,7 +129,7 @@ public static class WindowResizing
         vm.PicViewer.AspectRatio = size.AspectRatio;
     }
 
-    public static ImageSizeCalculationHelper.ImageSize? GetSize(MainViewModel vm)
+    public static ImageSize? GetSize(MainViewModel vm)
     {
         double firstWidth, firstHeight;
         var preloadValue = NavigationManager.GetCurrentPreLoadValue();
@@ -194,7 +194,7 @@ public static class WindowResizing
         return GetSize(firstWidth, firstHeight, secondWidth, secondHeight, vm.RotationAngle, vm);
     }
     
-    public static ImageSizeCalculationHelper.ImageSize? GetSize(double width, double height, double secondWidth, double secondHeight, double rotation,
+    public static ImageSize? GetSize(double width, double height, double secondWidth, double secondHeight, double rotation,
         MainViewModel vm)
     {
         width = width == 0 ? vm.PicViewer.ImageWidth : width;
@@ -222,7 +222,7 @@ public static class WindowResizing
             return null;
         }
 
-        ImageSizeCalculationHelper.ImageSize size;
+        ImageSize size;
         if (Settings.ImageScaling.ShowImageSideBySide && secondWidth > 0 && secondHeight > 0)
         {
             size = ImageSizeCalculationHelper.GetSideBySideImageSize(
@@ -290,28 +290,5 @@ public static class WindowResizing
         }
     }
 
-    public static void RestoreSize(Window window)
-    {
-        if (Dispatcher.UIThread.CheckAccess())
-        {
-            Set();
-        }
-        else
-        {
-            Dispatcher.UIThread.InvokeAsync(Set);
-        }
-
-        return;
-
-        void Set()
-        {
-            var x = (int)Settings.WindowProperties.Left;
-            var y = (int)Settings.WindowProperties.Top;
-            window.Position = new PixelPoint(x, y);
-            window.Width = Settings.WindowProperties.Width;
-            window.Height = Settings.WindowProperties.Height;
-        }
-    }
-
     #endregion
 }

+ 24 - 0
src/PicView.Core/Sizing/ImageSize.cs

@@ -0,0 +1,24 @@
+namespace PicView.Core.Sizing;
+
+public readonly struct ImageSize(
+    double width,
+    double height,
+    double secondaryWidth,
+    double scrollViewerWidth,
+    double scrollViewerHeight,
+    double titleMaxWidth,
+    double margin,
+    double aspectRatio)
+{
+    public double TitleMaxWidth { get; } = titleMaxWidth;
+    public double Width { get; } = width;
+    public double Height { get; } = height;
+
+    public double ScrollViewerWidth { get; } = scrollViewerWidth;
+    public double ScrollViewerHeight { get; } = scrollViewerHeight;
+
+    public double SecondaryWidth { get; } = secondaryWidth;
+    public double Margin { get; } = margin;
+
+    public double AspectRatio { get; } = aspectRatio;
+}

+ 122 - 137
src/PicView.Core/Sizing/ImageSizeCalculationHelper.cs

@@ -3,10 +3,12 @@
 public static class ImageSizeCalculationHelper
 {
     private const int MinTitleWidth = 250;
-    
+    private const int MaxRotationAngle = 360;
+    private const int MinRotationAngle = 0;
+
     public static ImageSize GetImageSize(
-        double width,
-        double height,
+        double imageWidth,
+        double imageHeight,
         ScreenSize screenSize,
         double minWidth,
         double minHeight,
@@ -19,141 +21,147 @@ public static class ImageSizeCalculationHelper
         double containerWidth,
         double containerHeight)
     {
-        if (width <= 0 || height <= 0 || rotationAngle > 360 || rotationAngle < 0)
+        if (imageWidth <= 0 || imageHeight <= 0 || rotationAngle > MaxRotationAngle || rotationAngle < MinRotationAngle)
         {
             return ErrorImageSize(minWidth, minHeight, interfaceSize, containerWidth);
         }
 
-        double aspectRatio;
-        double maxWidth, maxHeight;
-        var margin = 0d;
+        var isFullscreen = Settings.WindowProperties.Fullscreen || Settings.WindowProperties.Maximized;
 
-        var fullscreen = Settings.WindowProperties.Fullscreen ||
-                         Settings.WindowProperties.Maximized;
-            
-        var borderSpaceHeight = fullscreen ? 0 : uiTopSize + uiBottomSize + galleryHeight;
-        var borderSpaceWidth = fullscreen ? 0 : screenSize.Margin;
+        var borderSpaceHeight = CalculateBorderSpaceHeight(isFullscreen, uiTopSize, uiBottomSize, galleryHeight);
+        var borderSpaceWidth = isFullscreen ? 0 : screenSize.Margin;
 
-        var workAreaWidth = screenSize.WorkingAreaWidth - borderSpaceWidth;
-        var workAreaHeight = screenSize.WorkingAreaHeight - borderSpaceHeight;
+        var workArea = CalculateWorkArea(screenSize, borderSpaceWidth, borderSpaceHeight);
 
-        if (Settings.Zoom.ScrollEnabled)
-        {
-            workAreaWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
-            containerWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
+        var (maxAvailableWidth, maxAvailableHeight, adjustedContainerWidth, adjustedContainerHeight) =
+            CalculateMaxImageSize(Settings.Zoom.ScrollEnabled, Settings.ImageScaling.StretchImage,
+                Settings.WindowProperties.AutoFit,
+                workArea.width, workArea.height, screenSize.Margin, imageWidth, imageHeight, dpiScaling, galleryHeight,
+                containerWidth, containerHeight);
 
-            maxWidth = Settings.ImageScaling.StretchImage 
-                ? workAreaWidth 
-                : Math.Min(workAreaWidth - screenSize.Margin, width);
-            
-            maxHeight = workAreaHeight;
-        }
-        else if (Settings.WindowProperties.AutoFit)
+        var margin = CalculateGalleryMargin(Settings.Gallery.IsBottomGalleryShown,
+            Settings.UIProperties.ShowInterface, Settings.Gallery.ShowBottomGalleryInHiddenUI, galleryHeight);
+
+        var aspectRatio =
+            CalculateAspectRatio(rotationAngle, maxAvailableWidth, maxAvailableHeight, imageWidth, imageHeight);
+
+        double displayedWidth, displayedHeight, scrollWidth, scrollHeight;
+        if (Settings.Zoom.ScrollEnabled)
         {
-            maxWidth = Settings.ImageScaling.StretchImage
-                ? workAreaWidth - screenSize.Margin
-                : Math.Min(workAreaWidth - screenSize.Margin, width);
-                    
-            maxHeight = Settings.ImageScaling.StretchImage
-                ? workAreaHeight - screenSize.Margin
-                : Math.Min(workAreaHeight - screenSize.Margin, height);
+            (displayedWidth, displayedHeight, scrollWidth, scrollHeight) = CalculateScrolledImageSize(
+                isFullscreen, Settings.WindowProperties.AutoFit, screenSize, imageWidth, imageHeight, aspectRatio,
+                adjustedContainerWidth, adjustedContainerHeight, containerHeight, margin, dpiScaling
+            );
         }
         else
         {
-            maxWidth = Settings.ImageScaling.StretchImage
-                ? containerWidth
-                : Math.Min(containerWidth, width);
+            displayedWidth = imageWidth * aspectRatio;
+            displayedHeight = imageHeight * aspectRatio;
+            scrollWidth = double.NaN;
+            scrollHeight = double.NaN;
+        }
+
+        var titleMaxWidth = GetTitleMaxWidth(rotationAngle, displayedWidth, displayedHeight, minWidth, minHeight,
+            interfaceSize, containerWidth);
+        return new ImageSize(displayedWidth, displayedHeight, 0, scrollWidth, scrollHeight, titleMaxWidth, margin,
+            aspectRatio);
+    }
+
+    private static (double width, double height) CalculateWorkArea(ScreenSize screenSize, double borderSpaceWidth,
+        double borderSpaceHeight)
+        => (screenSize.WorkingAreaWidth - borderSpaceWidth, screenSize.WorkingAreaHeight - borderSpaceHeight);
+
+    private static double CalculateBorderSpaceHeight(bool fullscreen, double uiTop, double uiBottom, double gallery)
+        => fullscreen ? 0 : uiTop + uiBottom + gallery;
 
-            maxHeight = Settings.ImageScaling.StretchImage
-                ? containerHeight - galleryHeight
-                : Math.Min(containerHeight - galleryHeight, height);
+    private static (double maxWidth, double maxHeight, double containerWidth, double containerHeight)
+        CalculateMaxImageSize(
+            bool scrollEnabled, bool stretchImage, bool autoFit,
+            double workAreaWidth, double workAreaHeight, double margin,
+            double width, double height, double dpiScaling, double galleryHeight, double containerWidth,
+            double containerHeight)
+    {
+        if (scrollEnabled)
+        {
+            workAreaWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
+            containerWidth -= SizeDefaults.ScrollbarSize * dpiScaling;
+            return (stretchImage ? workAreaWidth : Math.Min(workAreaWidth - margin, width), workAreaHeight,
+                containerWidth, containerHeight);
         }
 
-        if (Settings.Gallery.IsBottomGalleryShown)
+        if (autoFit)
         {
-            if (!Settings.UIProperties.ShowInterface)
-            {
-                if (Settings.Gallery.ShowBottomGalleryInHiddenUI)
-                {
-                    margin = galleryHeight > 0 ? galleryHeight : 0;
-                }
-                else
-                {
-                    margin = 0;
-                }
-            }
-            else
-            {
-                margin = galleryHeight > 0 ? galleryHeight : 0;
-            }
+            var mw = stretchImage ? workAreaWidth - margin : Math.Min(workAreaWidth - margin, width);
+            var mh = stretchImage ? workAreaHeight - margin : Math.Min(workAreaHeight - margin, height);
+            return (mw, mh, containerWidth, containerHeight);
         }
 
-        // aspect ratio calculation
+        return (
+            stretchImage ? containerWidth : Math.Min(containerWidth, width),
+            stretchImage ? containerHeight - galleryHeight : Math.Min(containerHeight - galleryHeight, height),
+            containerWidth, containerHeight
+        );
+    }
+
+    private static double CalculateAspectRatio(double rotationAngle, double maxWidth, double maxHeight, double width,
+        double height)
+    {
         switch (rotationAngle)
         {
             case 0:
             case 180:
-                aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
-                break;
-
+                return Math.Min(maxWidth / width, maxHeight / height);
             case 90:
             case 270:
-                aspectRatio = Math.Min(maxWidth / height, maxHeight / width);
-                break;
-
+                return Math.Min(maxWidth / height, maxHeight / width);
             default:
-                var rotationRadians = rotationAngle * Math.PI / 180;
-                var newWidth = Math.Abs(width * Math.Cos(rotationRadians)) +
-                               Math.Abs(height * Math.Sin(rotationRadians));
-                var newHeight = Math.Abs(width * Math.Sin(rotationRadians)) +
-                                Math.Abs(height * Math.Cos(rotationRadians));
-                aspectRatio = Math.Min(maxWidth / newWidth, maxHeight / newHeight);
-                break;
+                var radians = rotationAngle * Math.PI / 180;
+                var rotatedWidth = Math.Abs(width * Math.Cos(radians)) + Math.Abs(height * Math.Sin(radians));
+                var rotatedHeight = Math.Abs(width * Math.Sin(radians)) + Math.Abs(height * Math.Cos(radians));
+                return Math.Min(maxWidth / rotatedWidth, maxHeight / rotatedHeight);
+        }
+    }
+
+    private static double CalculateGalleryMargin(bool isBottomGalleryShown, bool showInterface,
+        bool showGalleryInHidden, double galleryHeight)
+    {
+        if (!isBottomGalleryShown)
+        {
+            return 0;
         }
 
-        // Fit image by aspect ratio calculation
-        // and update values
-        double scrollWidth, scrollHeight, xWidth, xHeight;
-        if (Settings.Zoom.ScrollEnabled)
+        if (!showInterface)
         {
-            if (fullscreen)
-            {
-                xWidth = width * aspectRatio;
-                xHeight = height * aspectRatio;
+            return showGalleryInHidden && galleryHeight > 0 ? galleryHeight : 0;
+        }
 
-                scrollWidth = screenSize.Width;
-                scrollHeight = screenSize.Height;
-            }
-            else if (Settings.WindowProperties.AutoFit)
-            {
-                xWidth = width * aspectRatio;
-                xHeight = height * aspectRatio;
+        return galleryHeight > 0 ? galleryHeight : 0;
+    }
 
-                scrollWidth = Math.Max(xWidth + SizeDefaults.ScrollbarSize, SizeDefaults.WindowMinSize + SizeDefaults.ScrollbarSize + screenSize.Margin + 16);
-                scrollHeight = containerHeight - margin;
-            }
-            else
-            {
-                xWidth = containerWidth - SizeDefaults.ScrollbarSize + 10;
-                xHeight = height / width * xWidth;
-                
-                scrollWidth = containerWidth + SizeDefaults.ScrollbarSize;
-                scrollHeight = containerHeight - margin;
-            }
-        }
-        else
+    private static (double width, double height, double scrollWidth, double scrollHeight) CalculateScrolledImageSize(
+        bool fullscreen, bool autoFit, ScreenSize screenSize, double width, double height, double aspectRatio,
+        double containerWidth, double containerHeight, double origContainerHeight, double margin, double dpiScaling)
+    {
+        if (fullscreen)
         {
-            scrollWidth = double.NaN;
-            scrollHeight = double.NaN;
-
-            xWidth = width * aspectRatio;
-            xHeight = height * aspectRatio;
+            return (width * aspectRatio, height * aspectRatio, screenSize.Width, screenSize.Height);
         }
 
-        var titleMaxWidth = GetTitleMaxWidth(rotationAngle, xWidth, xHeight, minWidth, minHeight,
-            interfaceSize, containerWidth);
+        if (autoFit)
+        {
+            var imgWidth = width * aspectRatio;
+            var imgHeight = height * aspectRatio;
+            var sw = Math.Max(imgWidth + SizeDefaults.ScrollbarSize,
+                SizeDefaults.WindowMinSize + SizeDefaults.ScrollbarSize + screenSize.Margin + 16);
+            var sh = origContainerHeight - margin;
+            return (imgWidth, imgHeight, sw, sh);
+        }
 
-        return new ImageSize(xWidth, xHeight, 0, scrollWidth, scrollHeight, titleMaxWidth, margin, aspectRatio);
+        var cWidth = containerWidth - SizeDefaults.ScrollbarSize + 10;
+        var cHeight = height / width * cWidth;
+        var sWidth = containerWidth + SizeDefaults.ScrollbarSize;
+        var sHeight = origContainerHeight - margin;
+        return (cWidth, cHeight, sWidth, sHeight);
     }
 
     public static ImageSize GetSideBySideImageSize(
@@ -173,8 +181,8 @@ public static class ImageSizeCalculationHelper
         double containerWidth,
         double containerHeight)
     {
-        if (width <= 0 || height <= 0 || secondaryWidth <= 0 || secondaryHeight <= 0 || rotationAngle > 360 ||
-            rotationAngle < 0)
+        if (width <= 0 || height <= 0 || secondaryWidth <= 0 || secondaryHeight <= 0 || rotationAngle > MaxRotationAngle ||
+            rotationAngle < MinRotationAngle)
         {
             return ErrorImageSize(minWidth, minHeight, interfaceSize, containerWidth);
         }
@@ -225,7 +233,7 @@ public static class ImageSizeCalculationHelper
                     xWidth1 *= scaleFactor;
                     xWidth2 *= scaleFactor;
                     xHeight *= scaleFactor;
-                        
+
                     combinedWidth = xWidth1 + xWidth2;
                 }
             }
@@ -252,11 +260,10 @@ public static class ImageSizeCalculationHelper
                     xWidth1 *= scaleFactor;
                     xWidth2 *= scaleFactor;
                     xHeight *= scaleFactor;
-                        
+
                     combinedWidth = xWidth1 + xWidth2;
                 }
             }
-
         }
 
         double scrollWidth, scrollHeight;
@@ -300,7 +307,7 @@ public static class ImageSizeCalculationHelper
         double monitorMinHeight, double interfaceSize, double containerWidth)
     {
         double titleMaxWidth;
-        
+
         if (Settings.WindowProperties.AutoFit && !Settings.WindowProperties.Maximized)
         {
             switch (rotationAngle)
@@ -341,34 +348,12 @@ public static class ImageSizeCalculationHelper
         {
             return titleMaxWidth + (SizeDefaults.ScrollbarSize + 10);
         }
+
         return titleMaxWidth + SizeDefaults.ScrollbarSize;
     }
-    
-    private static ImageSize ErrorImageSize(double monitorMinWidth, double monitorMinHeight, double interfaceSize, double containerWidth)
-        => new(0, 0, 0, 0, 0, GetTitleMaxWidth(0, 0, 0, monitorMinWidth,
-                    monitorMinHeight, interfaceSize, containerWidth), 0, 0);
-        
 
-    public readonly struct ImageSize(
-        double width,
-        double height,
-        double secondaryWidth,
-        double scrollViewerWidth,
-        double scrollViewerHeight,
-        double titleMaxWidth,
-        double margin,
-        double aspectRatio)
-    {
-        public double TitleMaxWidth { get; } = titleMaxWidth;
-        public double Width { get; } = width;
-        public double Height { get; } = height;
-
-        public double ScrollViewerWidth { get; } = scrollViewerWidth;
-        public double ScrollViewerHeight { get; } = scrollViewerHeight;
-
-        public double SecondaryWidth { get; } = secondaryWidth;
-        public double Margin { get; } = margin;
-
-        public double AspectRatio { get; } = aspectRatio;
-    }
+    private static ImageSize ErrorImageSize(double monitorMinWidth, double monitorMinHeight, double interfaceSize,
+        double containerWidth)
+        => new(0, 0, 0, 0, 0, GetTitleMaxWidth(0, 0, 0, monitorMinWidth,
+            monitorMinHeight, interfaceSize, containerWidth), 0, 0);
 }

+ 21 - 3
src/PicView.Core/Sizing/ScreenSize.cs

@@ -14,17 +14,35 @@ public readonly record struct ScreenSize
     /// Gets the height of the screen's working area in device-independent pixels.
     /// </summary>
     public double WorkingAreaHeight { get; init; }
-    
+
+    /// <summary>
+    /// Gets the width of the screen in device-independent pixels.
+    /// </summary>
     public double Width { get; init; }
+
+    /// <summary>
+    /// Gets the height of the screen in device-independent pixels.
+    /// </summary>
     public double Height { get; init; }
-    
+
+    /// <summary>
+    /// Gets the horizontal position of the screen's bounds in device-independent pixels.
+    /// </summary>
     public double X { get; init; }
+
+    /// <summary>
+    /// Gets the vertical position of the screen's bounds in device-independent pixels.
+    /// </summary>
     public double Y { get; init; }
     
     /// <summary>
     /// Gets the DPI scaling factor of the screen.
     /// </summary>
     public double Scaling { get; init; }
-    
+
+    /// <summary>
+    /// Gets the margin setting for the screen, adjusted for the current scaling factor. If the margin
+    /// specified in the application settings is zero, this property returns zero.
+    /// </summary>
     public double Margin => Settings.WindowProperties.Margin is 0 ? 0 : Settings.WindowProperties.Margin / Scaling;
 }