Răsfoiți Sursa

#66 improvements

Ruben 4 ani în urmă
părinte
comite
8b4e24a4d1

+ 19 - 3
src/PicView.Data/Sizing/ImageSizeHelper.cs

@@ -1,5 +1,7 @@
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Threading;
 
 namespace PicView.Data.Sizing;
 
@@ -19,10 +21,24 @@ public static class ImageSizeHelper
         maxHeight = Math.Min(screen.WorkingArea.Height - padding, height);
         
         var aspectRatio = Math.Min(maxWidth / width, maxHeight / height);
-
+        
         var interfaceSize = 190;
-        var x = rotation is 0 or 180 ? Math.Max(maxWidth, window.MinWidth) : Math.Max(maxHeight, window.MinHeight);
-        var titleMaxWidth = x - interfaceSize < interfaceSize ? interfaceSize : x - interfaceSize;
+        double titleMaxWidth = 0;
+        
+        if (Dispatcher.UIThread.CheckAccess())
+        {
+            var x = rotation is 0 or 180 ? Math.Max(maxWidth, window.MinWidth) : Math.Max(maxHeight, window.MinHeight);
+            titleMaxWidth = x - interfaceSize < interfaceSize ? interfaceSize : x - interfaceSize;
+        }
+        else
+        {
+            Dispatcher.UIThread.InvokeAsync(() =>
+            {
+                var x = rotation is 0 or 180 ? Math.Max(maxWidth, window.MinWidth) : Math.Max(maxHeight, window.MinHeight);
+                titleMaxWidth = x - interfaceSize < interfaceSize ? interfaceSize : x - interfaceSize;
+            }, Avalonia.Threading.DispatcherPriority.Normal).Wait();
+        }
+        
         return new[] { width * aspectRatio, height * aspectRatio, titleMaxWidth };
     }
 }

+ 70 - 40
src/PicView/Navigation/ImageIterator.cs

@@ -1,18 +1,38 @@
 using System.Diagnostics;
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using PicView.Data.Imaging;
 using PicView.Data.IO;
+using PicView.Data.Sizing;
 
 namespace PicView.Navigation
 {
     public class ImageIterator
     {
-        public int FolderIndex { get; private set; }
-        public List<string>? Pics { get; }
-        public bool Reverse { get; private set; }
+        public int FolderIndex { get; set; }
+        public List<string> Pics { get; }
+        private bool Reverse { get; set; }
         
-        public Preloader Preloader { get; }
+        private Preloader Preloader { get; }
+
+        public class Values
+        {
+            public IImage? Image { get; set; }
+            public double[]? Sizes { get; }
+            public string[] Titles { get; }
+            
+            public FileInfo FileInfo { get; }
+
+            public Values(IImage? image, double[]? sizes, string[] titles, FileInfo fileInfo)
+            {
+                Image = image;
+                Sizes = sizes;
+                Titles = titles;
+                FileInfo = fileInfo;
+            }
+        }
 
         public ImageIterator(FileInfo fileInfo)
         {
@@ -20,10 +40,15 @@ namespace PicView.Navigation
             Preloader = new Preloader();
         }
         
-        public async Task<IImage> GetPicFromFileAsync(FileInfo fileInfo)
+        public async Task<Values?> GetValuesFromFileAsync(FileInfo fileInfo)
         {
-            return await ImageDecoder.GetPicAsync(fileInfo).ConfigureAwait(false)
-                   ?? throw new InvalidOperationException();
+            var index = Pics.IndexOf(fileInfo.FullName);
+            var added = await Preloader.AddAsync(index, Pics, fileInfo).ConfigureAwait(false);
+            if (added is false)
+            {
+                // TODO error handling?
+            }
+            return GetValuesAtIndex(index);
         }
 
         public async Task PreloadAsync()
@@ -32,62 +57,67 @@ namespace PicView.Navigation
                 FolderIndex, Reverse, Pics ?? throw new Exception()).ConfigureAwait(false);
         }
 
-        public Preloader.PreloadValue GetPicAtIndex(int index)
+        public Values? GetValuesAtIndex(int index)
         {
             FolderIndex = index;
-            return Preloader.Get(index) ?? throw new InvalidOperationException();
-        }
-    
-        public Preloader.PreloadValue Next()
-        {
-            return GetPicAtIndex(GetImageIterateIndex(NavigateTo.Next));
-        }
 
-        public Preloader.PreloadValue Prev()
-        {
-            return GetPicAtIndex(GetImageIterateIndex(NavigateTo.Prev));
-        }
-        
-        public Preloader.PreloadValue Last()
-        {
-            return GetPicAtIndex(GetImageIterateIndex(NavigateTo.Last));
+            var preloadValue = Preloader.Get(index);
+            if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop ||
+                preloadValue?.Image is null)
+            {
+                return null;
+            }
+
+            var size =
+                ImageSizeHelper.GetScaledImageSize(
+                    preloadValue.Image.Size.Width, preloadValue.Image.Size.Height, 0,
+                    desktop.MainWindow);
+            var titles = Data.TextData.TitleHelper.TitleString(
+                (int)preloadValue.Image.Size.Width, (int)preloadValue.Image.Size.Height, index,
+                preloadValue.FileInfo, Pics);
+            return new Values(preloadValue.Image, size, titles, preloadValue.FileInfo ?? new FileInfo(Pics[index]));
         }
 
-        public Preloader.PreloadValue First()
+        public Values? GetValues(NavigateTo navigateTo)
         {
-            return GetPicAtIndex(GetImageIterateIndex(NavigateTo.First));
+            return GetValuesAtIndex(GetIteration(navigateTo));
         }
         
-        public int GetImageIterateIndex(NavigateTo navigateTo)
+        private int GetIteration(NavigateTo navigateTo)
         {
 #if DEBUG
             Debug.Assert(Pics != null, nameof(Pics) + " != null");
 #endif
-            
-            var next = FolderIndex;
-
             switch (navigateTo)
             {
                 case NavigateTo.Next:
-                    // Go to next if able
-                    if (FolderIndex + 1 == Pics?.Count)
+                    Reverse = false;
+                    
+                    if (true) // TODO replace with looping settings
                     {
-                        return -1;
+                        return FolderIndex == Pics.Count - 1 ? 0 : FolderIndex + 1;
                     }
-                    Reverse = false;
-                    return next + 1;
-                case NavigateTo.Prev:
-                    // Go to prev if able
-                    if (next - 1 < 0)
+                    if (FolderIndex + 1 >= Pics?.Count)
                     {
-                        return -1;
+                        return Pics.Count - 1;
                     }
+                    return FolderIndex + 1;
+                case NavigateTo.Prev:
                     Reverse = true;
-                    return next - 1;
+                    
+                    if (true) // TODO replace with looping settings
+                    {
+                        return FolderIndex == 0 ? Pics.Count - 1 : FolderIndex - 1;
+                    }
+                    if (FolderIndex - 1 < 0)
+                    {
+                        return 0;
+                    }
+                    return FolderIndex - 1;
                 case NavigateTo.First:
                     return 0;
                 case NavigateTo.Last:
-                    return -1;
+                    return Pics.Count - 1;
                 default:
                     throw new ArgumentOutOfRangeException(nameof(navigateTo), navigateTo, null);
             }

+ 6 - 5
src/PicView/Navigation/Preloader.cs

@@ -39,6 +39,7 @@ namespace PicView.Navigation
         /// Add file to preloader from index. Returns true if new value added.
         /// </summary>
         /// <param name="i">Index of Pics</param>
+        /// <param name="pics"></param>
         /// <param name="fileInfo"></param>
         /// <param name="image"></param>
         public async Task<bool> AddAsync(int i, List<string> pics, FileInfo? fileInfo = null, IImage? image = null)
@@ -168,7 +169,7 @@ namespace PicView.Navigation
         /// <param name="index"></param>
         /// <param name="reverse"></param>
         /// <param name="pics"></param>
-        public Task PreLoad(int index, bool reverse, List<string> pics) => Task.Run(() =>
+        public Task PreLoad(int index, bool reverse, List<string> pics) => Task.Run(async() =>
         {
             var loadInfront = pics.Count >= 10 ? 5 : 3;
             var loadBehind = pics.Count >= 10 ? 3 : 2;
@@ -181,14 +182,14 @@ namespace PicView.Navigation
                 for (var i = index - 1; i > endPoint; i--)
                 {
                     if (pics.Count == 0 || pics.Count == _sources.Count) { return; }
-                    _ = AddAsync(i % pics.Count, pics).ConfigureAwait(false);
+                    await AddAsync(i % pics.Count, pics).ConfigureAwait(false);
                 }
 
                 // Add second elements
                 for (var i = index + 1; i < (index + 1) + loadBehind; i++)
                 {
                     if (pics.Count == 0 || pics.Count == _sources.Count) { return; }
-                    _ = AddAsync(i % pics.Count, pics).ConfigureAwait(false);
+                    await AddAsync(i % pics.Count, pics).ConfigureAwait(false);
                 }
 
                 //Clean up in front
@@ -205,13 +206,13 @@ namespace PicView.Navigation
                 for (var i = index + 1; i < (index + 1) + loadInfront; i++)
                 {
                     if (pics.Count == 0 || pics.Count == _sources.Count) { return; }
-                    _ = AddAsync(i % pics.Count, pics).ConfigureAwait(false);
+                    await AddAsync(i % pics.Count, pics).ConfigureAwait(false);
                 }
                 // Add second elements behind
                 for (var i = index - 1; i > endPoint; i--)
                 {
                     if (pics.Count == 0 || pics.Count == _sources.Count) { return; }
-                    _ = AddAsync(i % pics.Count, pics).ConfigureAwait(false);
+                    await AddAsync(i % pics.Count, pics).ConfigureAwait(false);
                 }
 
                 //Clean up behind

+ 41 - 84
src/PicView/ViewModels/MainWindowViewModel.cs

@@ -4,9 +4,11 @@ using System.Reactive.Linq;
 using System.Runtime.Serialization;
 using ReactiveUI;
 using System.Windows.Input;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Media;
+using PicView.Data.Imaging;
 using PicView.Navigation;
 using PicView.Views;
 
@@ -16,7 +18,7 @@ namespace PicView.ViewModels
     {
         public MainWindowViewModel()
         {
-            if (Avalonia.Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
+            if (Application.Current.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
             {
                 return;
             }
@@ -25,99 +27,54 @@ namespace PicView.ViewModels
             MinimizeCommand = ReactiveCommand.Create(() =>
                 desktop.MainWindow.WindowState = WindowState.Minimized);
 
-            Next = ReactiveCommand.Create(async () =>
-            {
-                if (Iterator is null) { return; }
-
-                var preloadValue = LoadImage(NavigateTo.Next);
-                
-                SetValues(desktop, preloadValue.FileInfo ?? throw new InvalidOperationException());
-                
-                await Iterator.PreloadAsync().ConfigureAwait(false);
-            });
+            Task NextTask(ImageIterator.Values _) => SetValues(Iterator?.GetValues(NavigateTo.Next));
+            Next = ReactiveCommand.CreateFromTask((Func<ImageIterator.Values?, Task>) NextTask);
             
-            Prev = ReactiveCommand.Create(async () =>
-            {
-                if (Iterator is null) { return; }
-
-                var preloadValue = LoadImage(NavigateTo.Prev);
-                
-                SetValues(desktop, preloadValue.FileInfo ?? throw new InvalidOperationException());
-                
-                await Iterator.PreloadAsync().ConfigureAwait(false);
-            });
+            Task PrevTask(ImageIterator.Values _) => SetValues(Iterator?.GetValues(NavigateTo.Prev));
+            Prev = ReactiveCommand.CreateFromTask((Func<ImageIterator.Values?, Task>) PrevTask);
             
-            Last = ReactiveCommand.Create(async () =>
-            {
-                if (Iterator is null) { return; }
-                
-                var preloadValue = LoadImage(NavigateTo.Last);
-                
-                SetValues(desktop, preloadValue.FileInfo ?? throw new InvalidOperationException());
-                
-                await Iterator.PreloadAsync().ConfigureAwait(false);
-            });
+            Task LastTask(ImageIterator.Values _) => SetValues(Iterator?.GetValues(NavigateTo.Last));
+            Last = ReactiveCommand.CreateFromTask((Func<ImageIterator.Values?, Task>) LastTask);
             
-            First = ReactiveCommand.Create(async () =>
-            {
-                if (Iterator is null) { return; }
-                
-                var preloadValue = LoadImage(NavigateTo.First);
-                
-                SetValues(desktop, preloadValue.FileInfo ?? throw new InvalidOperationException());
-                
-                await Iterator.PreloadAsync().ConfigureAwait(false);
-            });
+            Task FirstTask(ImageIterator.Values _) => SetValues(Iterator?.GetValues(NavigateTo.First));
+            First = ReactiveCommand.CreateFromTask((Func<ImageIterator.Values?, Task>) FirstTask);
             
-            LoadCommand = ReactiveCommand.Create(async () =>
-            {
-                var args = Environment.GetCommandLineArgs();
-                if (args.Length < 1) { return; }
-                
-                FileInfo fileInfo = new(args[1]);
-                Iterator = new ImageIterator(fileInfo);
-                Pic = await Iterator.GetPicFromFileAsync(fileInfo).ConfigureAwait(false);
-                SetValues(desktop, fileInfo);
-
-                await Iterator.PreloadAsync().ConfigureAwait(false);
-            });
+            LoadCommand = ReactiveCommand.Create(LoadTask);
         }
 
-        private Preloader.PreloadValue LoadImage(NavigateTo navigateTo)
+        private async Task LoadTask()
         {
-            var preloadValue = navigateTo switch
-            {
-                NavigateTo.Next => Iterator?.Next() ?? throw new InvalidOperationException(),
-                NavigateTo.Prev => Iterator?.Prev() ?? throw new InvalidOperationException(),
-                NavigateTo.First => Iterator?.First() ?? throw new InvalidOperationException(),
-                NavigateTo.Last => Iterator?.Last() ?? throw new InvalidOperationException(),
-                _ => throw new ArgumentOutOfRangeException(nameof(navigateTo), navigateTo, null)
-            };
-            while (preloadValue.IsLoading)
+            var args = Environment.GetCommandLineArgs();
+            if (args.Length < 1) { return; }
+                
+            FileInfo fileInfo = new(args[1]);
+            Iterator = new ImageIterator(fileInfo);
+            var values = await Iterator.GetValuesFromFileAsync(fileInfo).ConfigureAwait(false);
+            await SetValues(values).ConfigureAwait(false);
+        }
+        
+        private async Task SetValues(ImageIterator.Values? values)
+        {
+            if (values is null) { return; }
+            
+            values.Image ??= await ImageDecoder.GetPicAsync(values.FileInfo).ConfigureAwait(false);
+            
+            Pic = values.Image;
+                
+            if (values.Sizes != null)
             {
-                WindowTitle = 
-                    Title = 
-                        Tooltip = "Loading...";
+                Width = values.Sizes[0];
+                Height = values.Sizes[1];
+                TitleWidth = values.Sizes[2];
             }
 
-            Pic = preloadValue.Image;
-            return preloadValue;
-        }
-
-        private void SetValues(IClassicDesktopStyleApplicationLifetime desktop, FileInfo fileInfo)
-        {
-            if (Pic == null || Iterator?.Pics == null) { throw new Exception(); }
+            WindowTitle = values?.Titles[0] ?? "Loading...";
+            Title = values?.Titles[1] ?? "Loading...";
+            Tooltip = values?.Titles[2] ?? "Loading...";
+            
+            if (Iterator is null) { return; }
             
-            var sizes = 
-                Data.Sizing.ImageSizeHelper.GetScaledImageSize(Pic.Size.Width, Pic.Size.Height, 0, desktop.MainWindow);
-            TitleWidth = sizes[2];
-            Width = sizes[0];
-            Height = sizes[1];
-            var data = Data.TextData.TitleHelper.TitleString(
-                (int)Pic.Size.Width, (int)Pic.Size.Height, Iterator.FolderIndex, fileInfo, Iterator.Pics);
-            WindowTitle = data[0];
-            Title = data[1];
-            Tooltip = data[2];
+            await Iterator.PreloadAsync().ConfigureAwait(false);
         }
         
         public ICommand? ExitCommand { get; }
@@ -126,7 +83,7 @@ namespace PicView.ViewModels
         
         private ImageIterator? Iterator { get; set; }
         
-        public ICommand? Next { get; }
+        public ReactiveCommand<ImageIterator.Values?, Unit>? Next { get; }
         public ICommand? Prev { get; }
         public ICommand? First { get; }
         public ICommand? Last { get; }

+ 2 - 1
src/PicView/Views/MainWindow.axaml

@@ -13,7 +13,8 @@
     WindowStartupLocation="CenterScreen"
     x:Class="PicView.Views.MainWindow"
     xmlns="https://github.com/avaloniaui"
-    xmlns:customTitleBars="clr-namespace:PicView.Views.CustomTitleBars" PointerWheelChanged="InputElement_OnPointerWheelChanged"
+    xmlns:customTitleBars="clr-namespace:PicView.Views.CustomTitleBars" 
+    PointerWheelChanged="InputElement_OnPointerWheelChanged"
     xmlns:vm="using:PicView.ViewModels"
     Opened="TopLevel_OnOpened"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

+ 8 - 17
src/PicView/Views/MainWindow.axaml.cs

@@ -6,12 +6,14 @@ using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Markup.Xaml;
 using Avalonia.Platform;
+using Avalonia.ReactiveUI;
 using Avalonia.Win32;
 using PicView.ViewModels;
+using ReactiveUI;
 
 namespace PicView.Views
 {
-    public partial class MainWindow : Window
+    public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
     {
         public MainWindow()
         {
@@ -29,24 +31,13 @@ namespace PicView.Views
         private void TopLevel_OnOpened(object? sender, EventArgs e)
         {
             (DataContext as MainWindowViewModel)?.LoadCommand?.Execute(null);
-
-            IWindowBaseImpl owner = new WindowImpl();
-            var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
             
-            ClientSizeProperty.Changed.Subscribe(
-            x =>
+            ClientSizeProperty.Changed.Subscribe(size =>
             {
-                var newSize = new Size(
-                    ClientSize.Width + (x.OldValue.Value.Width - x.NewValue.Value.Width) / 2,
-                    ClientSize.Height + (x.OldValue.Value.Height - x.NewValue.Value.Height) / 2);
-                var rect = new PixelRect(
-                    PixelPoint.Origin,
-                    PixelSize.FromSize(newSize, scaling));
-                var screen = Screens.ScreenFromPoint(owner?.Position ?? Position);
-                if (screen != null)
-                {
-                  Position = screen.WorkingArea.CenterRect(rect).Position;
-                }
+                var x = (size.OldValue.Value.Width - size.NewValue.Value.Width) / 2;
+                var y = (size.OldValue.Value.Height - size.NewValue.Value.Height) / 2;
+
+                Position = new PixelPoint(Position.X + (int)x, Position.Y + (int)y);
             });
         }