Просмотр исходного кода

Move access to ImageIterator to NavigationManager. Refactor, rename, misc.

Ruben 8 месяцев назад
Родитель
Сommit
e76f266182
24 измененных файлов с 308 добавлено и 256 удалено
  1. 0 1
      src/PicView.Avalonia/Clipboard/ClipboardHelper.cs
  2. 6 7
      src/PicView.Avalonia/CustomControls/PicBox.cs
  3. 5 5
      src/PicView.Avalonia/Gallery/GalleryFunctions.cs
  4. 11 26
      src/PicView.Avalonia/Gallery/GalleryLoad.cs
  5. 1 1
      src/PicView.Avalonia/Gallery/GalleryNavigation.cs
  6. 1 2
      src/PicView.Avalonia/ImageHandling/ImageHelper.cs
  7. 5 40
      src/PicView.Avalonia/Navigation/ErrorHandling.cs
  8. 7 22
      src/PicView.Avalonia/Navigation/FileHistoryNavigation.cs
  9. 1 1
      src/PicView.Avalonia/Navigation/FileListManager.cs
  10. 6 2
      src/PicView.Avalonia/Navigation/ImageIterator.cs
  11. 183 38
      src/PicView.Avalonia/Navigation/NavigationManager.cs
  12. 17 14
      src/PicView.Avalonia/Navigation/UpdateImage.cs
  13. 15 8
      src/PicView.Avalonia/SettingsManagement/SettingsUpdater.cs
  14. 10 13
      src/PicView.Avalonia/StartUp/QuickLoad.cs
  15. 4 4
      src/PicView.Avalonia/UI/HideInterfaceLogic.cs
  16. 12 11
      src/PicView.Avalonia/UI/SetTitleHelper.cs
  17. 1 8
      src/PicView.Avalonia/ViewModels/ImageCropperViewModel.cs
  18. 1 20
      src/PicView.Avalonia/ViewModels/MainViewModel.cs
  19. 2 6
      src/PicView.Avalonia/Views/ExifView.axaml.cs
  20. 1 1
      src/PicView.Avalonia/Views/MainView.axaml.cs
  21. 1 4
      src/PicView.Avalonia/Views/SingleImageResizeView.axaml.cs
  22. 10 13
      src/PicView.Avalonia/Views/UC/EditableTitlebar.axaml.cs
  23. 2 2
      src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml.cs
  24. 6 7
      src/PicView.Avalonia/WindowBehavior/WindowResizing.cs

+ 0 - 1
src/PicView.Avalonia/Clipboard/ClipboardHelper.cs

@@ -65,7 +65,6 @@ public static class ClipboardHelper
         {
             return;
         }
-        await vm.ImageIterator.ClearAsync();
         await NavigationManager.LoadPicFromFile(duplicatedPath, vm);
     }
     

+ 6 - 7
src/PicView.Avalonia/CustomControls/PicBox.cs

@@ -12,9 +12,9 @@ using Avalonia.Svg.Skia;
 using ImageMagick;
 using PicView.Avalonia.AnimatedImage;
 using PicView.Avalonia.ImageHandling;
+using PicView.Avalonia.Navigation;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
-using PicView.Core.Navigation;
 using ReactiveUI;
 using Vector = Avalonia.Vector;
 
@@ -234,7 +234,7 @@ public class PicBox : Control
                 return;
             }
 
-            var preloadValue = vm.ImageIterator?.GetCurrentPreLoadValue();
+            var preloadValue = NavigationManager.GetCurrentPreLoadValue();
             if (preloadValue?.ImageModel != null)
             {
                 sourceSize = new Size(preloadValue.ImageModel.PixelWidth, preloadValue.ImageModel.PixelHeight);
@@ -265,18 +265,17 @@ public class PicBox : Control
             }
             if (isSideBySide)
             {
-                var nextPreloadValue = vm.ImageIterator?.GetNextPreLoadValue();
+                var nextPreloadValue = NavigationManager.GetNextPreLoadValue();
                 if (nextPreloadValue?.ImageModel != null)
                 {
                     secondarySourceSize = new Size(nextPreloadValue.ImageModel.PixelWidth, nextPreloadValue.ImageModel.PixelHeight);
                 }
                 else
                 {
-                    if (vm.ImageIterator is not null)
+                    if (NavigationManager.CanNavigate(vm))
                     {
-                        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]);
+                        magickImage.Ping(NavigationManager.GetNextFileName);
                         secondarySourceSize = new Size(magickImage.Width, magickImage.Height);
                     }
                     else return;
@@ -397,7 +396,7 @@ public class PicBox : Control
                 return new Size();
             }
 
-            var preloadValue = vm.ImageIterator?.GetCurrentPreLoadValue();
+            var preloadValue = NavigationManager.GetCurrentPreLoadValue();
             if (preloadValue is not null)
             {
                 return new Size(preloadValue.ImageModel.PixelWidth, preloadValue.ImageModel.PixelHeight);

+ 5 - 5
src/PicView.Avalonia/Gallery/GalleryFunctions.cs

@@ -46,7 +46,7 @@ public static class GalleryFunctions
 
         if (vm != null)
         {
-            vm.SelectedGalleryItemIndex = vm.ImageIterator.CurrentIndex;
+            vm.SelectedGalleryItemIndex = NavigationManager.GetCurrentIndex;
         }
 
         return true;
@@ -86,7 +86,7 @@ public static class GalleryFunctions
 
         if (vm != null)
         {
-            vm.SelectedGalleryItemIndex = vm.ImageIterator.CurrentIndex;
+            vm.SelectedGalleryItemIndex = NavigationManager.GetCurrentIndex;
         }
 
         return true;
@@ -152,7 +152,7 @@ public static class GalleryFunctions
                         ToggleGallery(vm);
                     }
 
-                    await NavigationManager.Navigate(vm.ImageIterator.ImagePaths.IndexOf(fileInfo.FullName), vm).ConfigureAwait(false);
+                    await NavigationManager.Navigate(fileInfo.FullName, vm).ConfigureAwait(false);
                 };
                 if (galleryListBox.Items.Count > index)
                 {
@@ -321,7 +321,7 @@ public static class GalleryFunctions
         }
 
         
-        _ = Task.Run(() => GalleryLoad.LoadGallery(vm, Path.GetDirectoryName(vm.ImageIterator.ImagePaths[0])));
+        _ = Task.Run(() => GalleryLoad.LoadGallery(vm, NavigationManager.GetInitialFileInfo?.DirectoryName));
     }
 
     public static void OpenCloseBottomGallery(MainViewModel vm)
@@ -357,7 +357,7 @@ public static class GalleryFunctions
             return;
         }
 
-        Task.Run(() => GalleryLoad.LoadGallery(vm, Path.GetDirectoryName(vm.ImageIterator.ImagePaths[0])));
+        Task.Run(() => GalleryLoad.LoadGallery(vm, NavigationManager.GetInitialFileInfo?.DirectoryName));
     }
 
     public static void OpenBottomGallery(MainViewModel vm)

+ 11 - 26
src/PicView.Avalonia/Gallery/GalleryLoad.cs

@@ -20,7 +20,7 @@ public static class GalleryLoad
         // TODO: When list larger than 500, lazy load this when scrolling instead.
         // Figure out how to support virtualization. 
 
-        if (vm.ImageIterator?.ImagePaths.Count == 0 || IsLoading || vm.ImageIterator is null)
+        if (IsLoading || !NavigationManager.CanNavigate(vm) || string.IsNullOrEmpty(currentDirectory))
         {
             return;
         }
@@ -69,10 +69,10 @@ public static class GalleryLoad
         _cancellationTokenSource = new CancellationTokenSource();
         _currentDirectory = currentDirectory;
         IsLoading = true;
-        var index = vm.ImageIterator.CurrentIndex;
+        var index = NavigationManager.GetCurrentIndex;
         var galleryItemSize = Math.Max(vm.GetBottomGalleryItemHeight, vm.GetFullGalleryItemHeight);
 
-        var endIndex = vm.ImageIterator.ImagePaths.Count;
+        var endIndex = NavigationManager.GetCount;
         // Set priority low when loading excess images to ensure app responsiveness
         var priority = endIndex switch
         {
@@ -88,14 +88,13 @@ public static class GalleryLoad
         {
             for (var i = 0; i < endIndex; i++)
             {
-                if (currentDirectory != _currentDirectory || _cancellationTokenSource.IsCancellationRequested ||
-                    vm.ImageIterator is null)
+                if (NavigationManager.GetInitialFileInfo?.DirectoryName != _currentDirectory || _cancellationTokenSource.IsCancellationRequested)
                 {
                     await _cancellationTokenSource.CancelAsync();
                     return;
                 }
 
-                fileInfos[i] = new FileInfo(vm.ImageIterator.ImagePaths[i]);
+                fileInfos[i] = new FileInfo(NavigationManager.GetFileNameAt(i));
                 var thumbData = GalleryThumbInfo.GalleryThumbHolder.GetThumbData(fileInfos[i]);
                 await Dispatcher.UIThread.InvokeAsync(() =>
                 {
@@ -115,11 +114,10 @@ public static class GalleryLoad
                             GalleryFunctions.ToggleGallery(vm);
                         }
 
-                        await NavigationManager.Navigate(vm.ImageIterator.ImagePaths.IndexOf(fileInfos[i1].FullName), vm)
-                            .ConfigureAwait(false);
+                        await NavigationManager.Navigate(fileInfos[i1].FullName, vm).ConfigureAwait(false);
                     };
                     galleryListBox.Items.Add(galleryItem);
-                    if (i != vm.ImageIterator?.CurrentIndex)
+                    if (i != NavigationManager.GetCurrentIndex)
                     {
                         return;
                     }
@@ -142,7 +140,7 @@ public static class GalleryLoad
                 }
 
                 var horizontalItems = (int)Math.Floor(galleryListBox.Bounds.Width / galleryItem.ImageBorder.MinWidth);
-                index = (vm.ImageIterator.CurrentIndex - horizontalItems) % vm.ImageIterator.ImagePaths.Count;
+                index = (NavigationManager.GetCurrentIndex - horizontalItems) % NavigationManager.GetCount;
             });
 
             index = index < 0 ? 0 : index;
@@ -183,8 +181,7 @@ public static class GalleryLoad
         {
             await Parallel.ForAsync(0, endPosition, options, async (i, _) =>
             {
-                if (currentDirectory != _currentDirectory || _cancellationTokenSource.IsCancellationRequested ||
-                    vm.ImageIterator is null)
+                if (NavigationManager.GetInitialFileInfo?.DirectoryName != _currentDirectory || _cancellationTokenSource.IsCancellationRequested)
                 {
                     await _cancellationTokenSource.CancelAsync();
                     return;
@@ -192,7 +189,7 @@ public static class GalleryLoad
 
                 ct.ThrowIfCancellationRequested();
 
-                if (i < 0 || i >= vm.ImageIterator.ImagePaths.Count)
+                if (i < 0 || i >= NavigationManager.GetCount)
                 {
                     return;
                 }
@@ -229,19 +226,7 @@ public static class GalleryLoad
                         galleryItem.GalleryImage.Source = thumb;
                     }
 
-                    if (vm.ImageIterator is null)
-                    {
-                        ct.ThrowIfCancellationRequested();
-                        GalleryFunctions.Clear();
-                        if (GalleryFunctions.IsBottomGalleryOpen)
-                        {
-                            mainView.GalleryView.GalleryMode = GalleryMode.BottomToClosed;
-                        }
-
-                        return;
-                    }
-
-                    if (nextIndex == vm.ImageIterator.CurrentIndex)
+                    if (nextIndex == NavigationManager.GetCurrentIndex)
                     {
                         galleryListBox.ScrollToCenterOfItem(galleryItem);
                     }

+ 1 - 1
src/PicView.Avalonia/Gallery/GalleryNavigation.cs

@@ -176,7 +176,7 @@ public static class GalleryNavigation
             return;
         }
         GalleryFunctions.ToggleGallery(vm);
-        if (vm.SelectedGalleryItemIndex != vm.ImageIterator.CurrentIndex) 
+        if (vm.SelectedGalleryItemIndex != NavigationManager.GetCurrentIndex) 
         {
             await NavigationManager.Navigate(vm.SelectedGalleryItemIndex, vm).ConfigureAwait(false);
         }

+ 1 - 2
src/PicView.Avalonia/ImageHandling/ImageHelper.cs

@@ -50,8 +50,7 @@ public static class ImageHelper
             }
             else
             {
-                var preloadValue = await vm.ImageIterator.GetPreLoadValueAsync(vm.ImageIterator.ImagePaths.IndexOf(path))
-                    .ConfigureAwait(false);
+                var preloadValue = await NavigationManager.GetPreLoadValueAsync(path).ConfigureAwait(false);
                 if (preloadValue?.ImageModel.Image is Bitmap bitmap)
                 {
                     source = bitmap;

+ 5 - 40
src/PicView.Avalonia/Navigation/ErrorHandling.cs

@@ -1,12 +1,9 @@
 using Avalonia;
-using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Threading;
-using PicView.Avalonia.Clipboard;
 using PicView.Avalonia.Gallery;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Core.Calculations;
-using PicView.Core.FileHandling;
 using PicView.Core.Gallery;
 using StartUpMenu = PicView.Avalonia.Views.StartUpMenu;
 
@@ -55,12 +52,12 @@ public static class ErrorHandling
             vm.GalleryMode = GalleryMode.Closed;
             GalleryFunctions.Clear();
             UIHelper.CloseMenus(vm);
-            vm.ImageIterator?.Dispose();
-            vm.ImageIterator = null;
             vm.GalleryMargin = new Thickness(0, 0, 0, 0);
             vm.GetIndex = 0;
             vm.PlatformService.StopTaskbarProgress();
             vm.IsLoading = false;
+
+            _ = NavigationManager.DisposeImageIteratorAsync();
         }
     }
 
@@ -83,7 +80,7 @@ public static class ErrorHandling
             return;
         }
         
-        if (vm.ImageIterator is null)
+        if (vm.ImageSource is null || !NavigationManager.CanNavigate(vm))
         {
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
@@ -94,10 +91,7 @@ public static class ErrorHandling
 
         try
         {
-            var index = vm.ImageIterator.CurrentIndex;
-            await vm.ImageIterator.DisposeAsync().ConfigureAwait(false);
-            vm.ImageIterator = new ImageIterator(vm.FileInfo, vm.ImageIterator.ImagePaths, index, vm);
-            await NavigationManager.Navigate(index, vm).ConfigureAwait(false);
+            await NavigationManager.FullReload(vm);
         }
         catch (Exception e)
         {
@@ -114,35 +108,6 @@ public static class ErrorHandling
     
     public static async Task ReloadImageAsync(MainViewModel vm)
     {
-        if (vm.ImageSource is null)
-        {
-            return;
-        }
-
-        if (NavigationManager.CanNavigate(vm))
-        {
-            var preloadValue = await vm.ImageIterator.GetPreLoadValueAsync(vm.ImageIterator.CurrentIndex).ConfigureAwait(false);
-            if (preloadValue?.ImageModel.Image is not null)
-            {
-                vm.ImageSource = preloadValue.ImageModel.Image;
-            }
-        }
-        else
-        {
-            var url = vm.Title.GetURL();
-            if (!string.IsNullOrEmpty(url))
-            {
-                await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
-            }
-            else 
-            {
-                if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
-                {
-                    return;
-                }
-                var clipboard = desktop.MainWindow.Clipboard;
-                await ClipboardHelper.PasteClipboardImage(vm, clipboard);
-            }
-        }
+        await NavigationManager.FullReload(vm);
     }
 }

+ 7 - 22
src/PicView.Avalonia/Navigation/FileHistoryNavigation.cs

@@ -83,25 +83,11 @@ public static class FileHistoryNavigation
         }
     }
 
-    public static async Task NextAsync(MainViewModel vm)
-    {
-        if (!NavigationManager.CanNavigate(vm))
-        {
-            await OpenLastFileAsync(vm).ConfigureAwait(false);
-            return;
-        }
+    public static async Task NextAsync(MainViewModel vm) => await NextAsyncInternal(vm, true).ConfigureAwait(false);
 
-        if (vm.ImageIterator is null)
-        {
-            await OpenLastFileAsync(vm).ConfigureAwait(false);
-            return;
-        }
-
-        var index = vm.ImageIterator.CurrentIndex;
-        await LoadEntryAsync(vm, index, true).ConfigureAwait(false);
-    }
-
-    public static async Task PrevAsync(MainViewModel vm)
+    public static async Task PrevAsync(MainViewModel vm) => await NextAsyncInternal(vm, false).ConfigureAwait(false);
+    
+    private static async Task NextAsyncInternal(MainViewModel vm, bool next)
     {
         if (!NavigationManager.CanNavigate(vm))
         {
@@ -109,19 +95,18 @@ public static class FileHistoryNavigation
             return;
         }
         
-        if (vm.ImageIterator is null)
+        if (!NavigationManager.CanNavigate(vm))
         {
             await OpenLastFileAsync(vm).ConfigureAwait(false);
             return;
         }
 
-        var index = vm.ImageIterator.CurrentIndex;
-        await LoadEntryAsync(vm, index, false).ConfigureAwait(false);
+        await LoadEntryAsync(vm, NavigationManager.GetCurrentIndex, next).ConfigureAwait(false);
     }
 
     public static async Task LoadEntryAsync(MainViewModel vm, int index, bool next)
     {
-        var imagePaths = vm.ImageIterator.ImagePaths;
+        var imagePaths = NavigationManager.GetCollection;
 
         _fileHistory ??= new FileHistory();
         string? nextEntry;

+ 1 - 1
src/PicView.Avalonia/Navigation/FileListManager.cs

@@ -107,7 +107,7 @@ public static class FileListManager
                     return false;
                 }
 
-                vm.ImageIterator.UpdateFileListAndIndex(files, files.IndexOf(vm.FileInfo.FullName));
+                NavigationManager.UpdateFileListAndIndex(files, files.IndexOf(vm.FileInfo.FullName));
                 SetTitleHelper.SetTitle(vm);
                 return true;
             }

+ 6 - 2
src/PicView.Avalonia/Navigation/ImageIterator.cs

@@ -22,8 +22,12 @@ public class ImageIterator : IAsyncDisposable
     public List<string> ImagePaths { get; private set; }
 
     public int CurrentIndex { get; private set; }
+    
+    public int GetNonZeroIndex => CurrentIndex + 1 > GetCount ? 1 : CurrentIndex + 1;
 
     public int NextIndex => GetIteration(CurrentIndex, NavigateTo.Next);
+    
+    public int GetCount => ImagePaths.Count;
 
     public FileInfo InitialFileInfo { get; private set; } = null!;
     public bool IsReversed { get; private set; }
@@ -321,7 +325,7 @@ public class ImageIterator : IAsyncDisposable
         await PreLoader.ClearAsync().ConfigureAwait(false);
     }
 
-    public async Task Preload()
+    public async Task PreloadAsync()
     {
         await PreLoader.PreLoadAsync(CurrentIndex, IsReversed, ImagePaths).ConfigureAwait(false);
     }
@@ -370,7 +374,7 @@ public class ImageIterator : IAsyncDisposable
 
     #region Navigation
 
-    public async Task ReloadFileList()
+    public async Task ReloadFileListAsync()
     {
         ImagePaths = await Task.FromResult(_vm.PlatformService.GetFiles(InitialFileInfo)).ConfigureAwait(false);
         CurrentIndex = ImagePaths.IndexOf(_vm.FileInfo.FullName);

+ 183 - 38
src/PicView.Avalonia/Navigation/NavigationManager.cs

@@ -1,16 +1,20 @@
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Media.Imaging;
 using Avalonia.Threading;
 using ImageMagick;
+using PicView.Avalonia.Clipboard;
 using PicView.Avalonia.Crop;
 using PicView.Avalonia.Gallery;
 using PicView.Avalonia.ImageHandling;
 using PicView.Avalonia.Input;
+using PicView.Avalonia.Preloading;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.ArchiveHandling;
+using PicView.Core.FileHandling;
 using PicView.Core.Gallery;
 using PicView.Core.ImageDecoding;
 using PicView.Core.Localization;
@@ -19,7 +23,7 @@ using PicView.Core.Navigation;
 namespace PicView.Avalonia.Navigation;
 
 /// <summary>
-///     Helper class for navigation and image loading functionalities in the application.
+///     Manages image navigation within the application.
 /// </summary>
 public static class NavigationManager
 {
@@ -27,6 +31,9 @@ public static class NavigationManager
 
     private static TiffManager.TiffNavigationInfo? _tiffNavigationInfo;
 
+    // Should be updated to handle multiple iterators, in the future when adding tab support
+    private static ImageIterator? _imageIterator;
+
     #region Navigation
 
     /// <summary>
@@ -36,8 +43,8 @@ public static class NavigationManager
     /// <returns>True if navigation is possible, otherwise false.</returns>
     public static bool CanNavigate(MainViewModel vm)
     {
-        return vm?.ImageIterator?.ImagePaths is not null &&
-               vm.ImageIterator.ImagePaths.Count > 0 && !CropFunctions.IsCropping &&
+        return _imageIterator?.ImagePaths is not null &&
+               _imageIterator.ImagePaths.Count > 0 && !CropFunctions.IsCropping &&
                !UIHelper.IsDialogOpen && !vm.IsEditableTitlebarOpen;
         // TODO: should probably turn this into CanExecute observable for ReactiveUI
     }
@@ -62,26 +69,26 @@ public static class NavigationManager
         }
 
         var navigateTo = next ? NavigateTo.Next : NavigateTo.Previous;
-        var nextIteration = vm.ImageIterator.GetIteration(vm.ImageIterator.CurrentIndex, navigateTo);
-        var currentFileName = vm.ImageIterator.ImagePaths[vm.ImageIterator.CurrentIndex];
+        var nextIteration = _imageIterator.GetIteration(_imageIterator.CurrentIndex, navigateTo);
+        var currentFileName = _imageIterator.ImagePaths[_imageIterator.CurrentIndex];
         if (TiffManager.IsTiff(currentFileName))
         {
             await TiffNavigation(vm, currentFileName, nextIteration).ConfigureAwait(false);
         }
         else
         {
-            await CheckCancellationAndStartIterateToIndex(nextIteration, vm).ConfigureAwait(false);
+            await CheckCancellationAndStartIterateToIndex(nextIteration).ConfigureAwait(false);
         }
     }
 
     private static async Task TiffNavigation(MainViewModel vm, string currentFileName, int nextIteration)
     {
-        if (_tiffNavigationInfo is null && !vm.ImageIterator.IsReversed)
+        if (_tiffNavigationInfo is null && !_imageIterator.IsReversed)
         {
             var tiffPages = await Task.FromResult(TiffManager.LoadTiffPages(currentFileName)).ConfigureAwait(false);
             if (tiffPages.Count < 1)
             {
-                await CheckCancellationAndStartIterateToIndex(nextIteration, vm).ConfigureAwait(false);
+                await CheckCancellationAndStartIterateToIndex(nextIteration).ConfigureAwait(false);
                 return;
             }
 
@@ -95,11 +102,11 @@ public static class NavigationManager
 
         if (_tiffNavigationInfo is null)
         {
-            await CheckCancellationAndStartIterateToIndex(nextIteration, vm).ConfigureAwait(false);
+            await CheckCancellationAndStartIterateToIndex(nextIteration).ConfigureAwait(false);
         }
         else
         {
-            if (vm.ImageIterator.IsReversed)
+            if (_imageIterator.IsReversed)
             {
                 if (_tiffNavigationInfo.CurrentPage - 1 < 0)
                 {
@@ -120,14 +127,14 @@ public static class NavigationManager
             }
             else
             {
-                await UpdateImage.SetTiffImageAsync(_tiffNavigationInfo, vm.ImageIterator.CurrentIndex, vm.FileInfo, vm);
+                await UpdateImage.SetTiffImageAsync(_tiffNavigationInfo, _imageIterator.CurrentIndex, vm.FileInfo, vm);
             }
         }
         return;
         
         async Task ExitTiffNavigationAndNavigate()
         {
-            await CheckCancellationAndStartIterateToIndex(nextIteration, vm).ConfigureAwait(false);
+            await CheckCancellationAndStartIterateToIndex(nextIteration).ConfigureAwait(false);
             _tiffNavigationInfo?.Dispose();
             _tiffNavigationInfo = null;
         }
@@ -163,7 +170,19 @@ public static class NavigationManager
             return;
         }
 
-        await CheckCancellationAndStartIterateToIndex(index, vm).ConfigureAwait(false);
+        await CheckCancellationAndStartIterateToIndex(index).ConfigureAwait(false);
+    }
+    
+    public static async Task Navigate(string fileName, MainViewModel vm)
+    {
+        if (!CanNavigate(vm))
+        {
+            return;
+        }
+        
+        var index = _imageIterator.ImagePaths.IndexOf(fileName);
+
+        await CheckCancellationAndStartIterateToIndex(index).ConfigureAwait(false);
     }
 
     private static async Task NavigateIncrements(MainViewModel vm, bool next, bool is10, bool is100)
@@ -173,11 +192,11 @@ public static class NavigationManager
             return;
         }
 
-        var currentIndex = vm.ImageIterator.CurrentIndex;
+        var currentIndex = _imageIterator.CurrentIndex;
         var direction = next ? NavigateTo.Next : NavigateTo.Previous;
-        var index = vm.ImageIterator.GetIteration(currentIndex, direction, false, is10, is100);
+        var index = _imageIterator.GetIteration(currentIndex, direction, false, is10, is100);
 
-        await CheckCancellationAndStartIterateToIndex(index, vm).ConfigureAwait(false);
+        await CheckCancellationAndStartIterateToIndex(index).ConfigureAwait(false);
     }
 
     public static Task Next10(MainViewModel vm) => NavigateIncrements(vm, true, true, false);
@@ -210,7 +229,7 @@ public static class NavigationManager
             }
 
             _cancellationTokenSource = new CancellationTokenSource();
-            await vm.ImageIterator.NextIteration(last ? NavigateTo.Last : NavigateTo.First, _cancellationTokenSource)
+            await _imageIterator.NextIteration(last ? NavigateTo.Last : NavigateTo.First, _cancellationTokenSource)
                 .ConfigureAwait(false);
             await ScrollToEndIfNecessary(last);
         }
@@ -340,11 +359,11 @@ public static class NavigationManager
             {
                 case ErrorHelper.LoadAbleFileType.File:
                     // Navigate to the image if it exists in the image iterator
-                    if (vm.ImageIterator is not null)
+                    if (_imageIterator is not null)
                     {
-                        if (vm.ImageIterator.ImagePaths.Contains(check.Value.Data))
+                        if (_imageIterator.ImagePaths.Contains(check.Value.Data))
                         {
-                            await vm.ImageIterator.IterateToIndex(vm.ImageIterator.ImagePaths.IndexOf(check.Value.Data),
+                            await _imageIterator.IterateToIndex(_imageIterator.ImagePaths.IndexOf(check.Value.Data),
                                     _cancellationTokenSource)
                                 .ConfigureAwait(false);
                             return;
@@ -415,14 +434,14 @@ public static class NavigationManager
 
         _cancellationTokenSource = new CancellationTokenSource();
 
-        if (vm.ImageIterator is not null)
+        if (_imageIterator is not null)
         {
-            if (fileInfo.DirectoryName == vm.ImageIterator.InitialFileInfo.DirectoryName)
+            if (fileInfo.DirectoryName == _imageIterator.InitialFileInfo.DirectoryName)
             {
-                var index = vm.ImageIterator.ImagePaths.IndexOf(fileInfo.FullName);
+                var index = _imageIterator.ImagePaths.IndexOf(fileInfo.FullName);
                 if (index != -1)
                 {
-                    await vm.ImageIterator.IterateToIndex(index, _cancellationTokenSource).ConfigureAwait(false);
+                    await _imageIterator.IterateToIndex(index, _cancellationTokenSource).ConfigureAwait(false);
                     await CheckTiffUpdate(vm, fileInfo.FullName, index);
                 }
                 else
@@ -571,7 +590,7 @@ public static class NavigationManager
     /// <returns>A task representing the asynchronous operation.</returns>
     public static async Task LoadPicFromBase64Async(string base64, MainViewModel vm)
     {
-        vm.ImageIterator = null;
+        _imageIterator = null;
         vm.ImageSource = null;
         vm.IsLoading = true;
         SetTitleHelper.SetLoadingTitle(vm);
@@ -678,15 +697,144 @@ public static class NavigationManager
 
     #endregion
 
-    #region Private helpers
+    #region Helpers
+
+    #region ImageIterator
+
+    public static void InitializeImageIterator(MainViewModel vm)
+    {
+        _imageIterator ??= new ImageIterator(vm.FileInfo, vm);
+    }
+    
+    public static async Task DisposeImageIteratorAsync()
+    {
+        if (_imageIterator is null)
+        {
+            return;
+        }
+        await _imageIterator.DisposeAsync();
+    }
+    
+    public static bool IsCollectionEmpty => _imageIterator.ImagePaths is null || _imageIterator.ImagePaths.Count < 0;
+    public static List<string> GetCollection => _imageIterator.ImagePaths;
+    
+    public static void UpdateFileListAndIndex(List<string> fileList, int index) => _imageIterator?.UpdateFileListAndIndex(fileList, index);
+    
+    public static int? GetFileNameIndex(string fileName) =>
+        IsCollectionEmpty ? null : _imageIterator.ImagePaths.IndexOf(fileName);
+
+    /// <summary>
+    ///     Returns the file name at a given index in the image collection.
+    /// </summary>
+    /// <param name="index">The index of the file to retrieve.</param>
+    /// <returns>The file name at the given index.</returns>
+    public static string? GetFileNameAt(int index)
+    {
+        if (IsCollectionEmpty)
+        {
+            return null;
+        }
+
+        if (index < 0 || index >= _imageIterator.ImagePaths.Count)
+        {
+            return null;
+        }
+
+        return _imageIterator.ImagePaths[index];
+    }
+    
+    /// <summary>
+    ///     Gets the current file name.
+    /// </summary>
+    public static string? GetCurrentFileName => GetFileNameAt(_imageIterator?.CurrentIndex ?? -1);
+    
+    /// <summary>
+    ///     Gets the next file name.
+    /// </summary>
+    public static string? GetNextFileName => GetFileNameAt(_imageIterator?.NextIndex ?? -1);
+
+    public static int GetCurrentIndex => _imageIterator?.CurrentIndex ?? -1;
+    
+    public static int GetNextIndex => _imageIterator?.NextIndex ?? -1;
+    
+    public static int GetNonZeroIndex => _imageIterator?.GetNonZeroIndex ?? -1;
+    
+    public static int GetCount => _imageIterator?.GetCount ?? -1;
+    
+    public static FileInfo? GetInitialFileInfo => _imageIterator?.InitialFileInfo;
+    
+    public static PreLoadValue? GetPreLoadValue(int index) => _imageIterator?.GetPreLoadValue(index) ?? null;
+    public static async Task<PreLoadValue?> GetPreLoadValueAsync(int index) => await _imageIterator?.GetPreLoadValueAsync(index) ?? null;
+    public static async Task<PreLoadValue?> GetPreLoadValueAsync(string fileName) => await _imageIterator?.GetPreLoadValueAsync(GetFileNameIndex(fileName) ?? GetCurrentIndex) ?? null;
+    public static PreLoadValue? GetCurrentPreLoadValue() => _imageIterator?.GetCurrentPreLoadValue() ?? null;
+    public static async Task<PreLoadValue?> GetCurrentPreLoadValueAsync() => await _imageIterator?.GetCurrentPreLoadValueAsync() ?? null;
+    public static PreLoadValue? GetNextPreLoadValue() => _imageIterator?.GetNextPreLoadValue() ?? null;
+    public static async Task<PreLoadValue?> GetNextPreLoadValueAsync() => await _imageIterator?.GetNextPreLoadValueAsync() ?? null;
+    
+    public static async Task ReloadFileListAsync() => await _imageIterator?.ReloadFileListAsync();
+    
+    public static void AddToPreloader(int index, ImageModel imageModel) => _imageIterator?.Add(index, imageModel);
+    public static async Task PreloadAsync() => await _imageIterator?.PreloadAsync();
+    
+    public static void RemoveCurrentItemFromPreLoader() => _imageIterator?.RemoveItemFromPreLoader(_imageIterator.CurrentIndex);
+    public static void RemoveItemFromPreLoader(string fileName) => _imageIterator?.RemoveItemFromPreLoader(fileName);
+
+    #endregion
+
+
+    #region Reload
+
+    public static async Task QuickReload()
+    {
+        if (_imageIterator is null)
+        {
+            return;
+        }
+        await _imageIterator.QuickReload();
+    }
+    
+    public static async Task FullReload(MainViewModel vm)
+    {
+        if (vm.ImageSource is null)
+        {
+            return;
+        }
+        
+        if (_imageIterator is null)
+        {
+            var url = vm.Title.GetURL();
+            if (!string.IsNullOrEmpty(url))
+            {
+                await LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
+            }
+            else 
+            {
+                if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
+                {
+                    return;
+                }
+                var clipboard = desktop.MainWindow.Clipboard;
+                await ClipboardHelper.PasteClipboardImage(vm, clipboard);
+            }
+            return;
+        }
+
+        var index = _imageIterator.CurrentIndex;
+        await _imageIterator.DisposeAsync().ConfigureAwait(false);
+        _imageIterator = new ImageIterator(vm.FileInfo, vm);
+        await Navigate(index, vm).ConfigureAwait(false);
+    }
+
+    #endregion
+    
+
 
     /// <summary>
     ///     Checks if the previous iteration has been cancelled and starts the iteration at the given index in a new task.
     /// </summary>
     /// <param name="index">The index to iterate to.</param>
-    /// <param name="vm">The main view model instance.</param>
     /// <returns>A task representing the asynchronous operation.</returns>
-    private static async Task CheckCancellationAndStartIterateToIndex(int index, MainViewModel vm)
+    private static async Task CheckCancellationAndStartIterateToIndex(int index)
     {
         await Task.Run(() =>
         {
@@ -696,7 +844,7 @@ public static class NavigationManager
             }
 
             _cancellationTokenSource = new CancellationTokenSource();
-            _ = vm.ImageIterator.NextIteration(index, _cancellationTokenSource).ConfigureAwait(false);
+            _ = _imageIterator.NextIteration(index, _cancellationTokenSource).ConfigureAwait(false);
             _cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(5));
         }).ConfigureAwait(false);
     }
@@ -712,7 +860,7 @@ public static class NavigationManager
         return await Task.Run(() =>
         {
             var indexChange = next ? 1 : -1;
-            var currentFolder = Path.GetDirectoryName(vm.ImageIterator?.ImagePaths[vm.ImageIterator.CurrentIndex]);
+            var currentFolder = Path.GetDirectoryName(_imageIterator?.ImagePaths[_imageIterator.CurrentIndex]);
             var parentFolder = Path.GetDirectoryName(currentFolder);
             var directories = Directory.GetDirectories(parentFolder, "*", SearchOption.TopDirectoryOnly);
             var directoryIndex = Array.IndexOf(directories, currentFolder);
@@ -765,7 +913,7 @@ public static class NavigationManager
         vm.ImageType = imageModel.ImageType;
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
-            nextImageModel = (await vm.ImageIterator.GetNextPreLoadValueAsync()).ImageModel;
+            nextImageModel = (await _imageIterator.GetNextPreLoadValueAsync()).ImageModel;
             vm.SecondaryImageSource = nextImageModel.Image;
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
@@ -789,19 +937,16 @@ public static class NavigationManager
             Dispatcher.UIThread.Post(() => { vm.ImageViewer?.MainImage?.InvalidateVisual(); });
         }
 
-        if (vm.ImageIterator is not null)
-        {
-            await vm.ImageIterator.DisposeAsync();
-        }
+        await DisposeImageIteratorAsync();
         
         if (files is null)
         {
-            vm.ImageIterator = new ImageIterator(fileInfo, vm);
-            index = vm.ImageIterator.CurrentIndex;
+            _imageIterator = new ImageIterator(fileInfo, vm);
+            index = _imageIterator.CurrentIndex;
         }
         else
         {
-            vm.ImageIterator = new ImageIterator(fileInfo, files, index, vm);
+            _imageIterator = new ImageIterator(fileInfo, files, index, vm);
         }
         
         var isTiffUpdated = await CheckTiffUpdate(vm, fileInfo.FullName, index); 

+ 17 - 14
src/PicView.Avalonia/Navigation/UpdateImage.cs

@@ -32,8 +32,13 @@ public static class UpdateImage
         PreLoadValue? preLoadValue,
         PreLoadValue? nextPreloadValue = null)
     {
-        preLoadValue ??= await vm.ImageIterator.GetPreLoadValueAsync(index).ConfigureAwait(false);
-        if (preLoadValue.ImageModel?.Image is null && index == vm.ImageIterator.CurrentIndex)
+        preLoadValue ??= await NavigationManager.GetPreLoadValueAsync(index).ConfigureAwait(false);
+        if (preLoadValue is null)
+        {
+            await ErrorHandling.ReloadAsync(vm).ConfigureAwait(false);
+            return;
+        }
+        if (preLoadValue.ImageModel?.Image is null && index == NavigationManager.GetCurrentIndex)
         {
             var fileInfo = preLoadValue.ImageModel?.FileInfo ?? new FileInfo(imagePaths[index]);
             preLoadValue.ImageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
@@ -41,25 +46,22 @@ public static class UpdateImage
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
-            nextPreloadValue ??= await vm.ImageIterator.GetNextPreLoadValueAsync().ConfigureAwait(false);
-            if (nextPreloadValue.ImageModel?.Image is null && index == vm.ImageIterator.CurrentIndex)
+            nextPreloadValue ??= await NavigationManager.GetNextPreLoadValueAsync().ConfigureAwait(false);
+            if (nextPreloadValue.ImageModel?.Image is null && index == NavigationManager.GetCurrentIndex)
             {
-                var fileInfo = nextPreloadValue.ImageModel?.FileInfo ?? new FileInfo(
-                    imagePaths[
-                        vm.ImageIterator.GetIteration(index, isReversed ? NavigateTo.Previous : NavigateTo.Next,
-                            true)]);
+                var fileInfo = nextPreloadValue.ImageModel?.FileInfo ?? new FileInfo(NavigationManager.GetNextFileName);
                 nextPreloadValue.ImageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
             }
         }
 
-        if (index != vm.ImageIterator.CurrentIndex)
+        if (index != NavigationManager.GetCurrentIndex)
         {
             return;
         }
 
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
-            if (index != vm.ImageIterator.CurrentIndex)
+            if (index != NavigationManager.GetCurrentIndex)
             {
                 return;
             }
@@ -98,7 +100,7 @@ public static class UpdateImage
                 if (TiffManager.IsTiff(preLoadValue.ImageModel.FileInfo.FullName))
                 {
                     SetTitleHelper.TrySetTiffTitle(preLoadValue.ImageModel.PixelWidth,
-                        preLoadValue.ImageModel.PixelHeight, vm.ImageIterator.CurrentIndex,
+                        preLoadValue.ImageModel.PixelHeight, NavigationManager.GetCurrentIndex,
                         preLoadValue.ImageModel.FileInfo, vm);
                 }
                 else
@@ -254,7 +256,7 @@ public static class UpdateImage
             }
         }, DispatcherPriority.Render);
 
-        vm.ImageIterator = null;
+        
         int width, height;
         if (imageType is ImageType.Svg)
         {
@@ -295,6 +297,7 @@ public static class UpdateImage
         vm.PixelHeight = height;
 
         await dispatchAction(() => { UIHelper.GetGalleryView.IsVisible = false; }, DispatcherPriority.Render);
+        await NavigationManager.DisposeImageIteratorAsync();
     }
 
     #endregion
@@ -337,11 +340,11 @@ public static class UpdateImage
             GalleryNavigation.CenterScrollToSelectedItem(vm);
         }
 
-        vm.ImageSource = GetThumbnails.GetExifThumb(vm.ImageIterator.ImagePaths[index]);
+        vm.ImageSource = GetThumbnails.GetExifThumb(NavigationManager.GetFileNameAt(index));
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
             vm.SecondaryImageSource =
-                GetThumbnails.GetExifThumb(vm.ImageIterator.ImagePaths[vm.ImageIterator.NextIndex]);
+                GetThumbnails.GetExifThumb(NavigationManager.GetNextFileName);
         }
         else
         {

+ 15 - 8
src/PicView.Avalonia/SettingsManagement/SettingsUpdater.cs

@@ -59,8 +59,8 @@ public static class SettingsUpdater
                 vm.PlatformService.StopTaskbarProgress();
                 if (NavigationManager.CanNavigate(vm))
                 {
-                    vm.PlatformService.SetTaskbarProgress((ulong)vm.ImageIterator?.CurrentIndex!,
-                        (ulong)vm.ImageIterator?.ImagePaths?.Count!);
+                    vm.PlatformService.SetTaskbarProgress((ulong)NavigationManager.GetCurrentIndex,
+                        (ulong)NavigationManager.GetCount);
                 }
                 WindowResizing.SetSize(vm);
             });
@@ -120,7 +120,7 @@ public static class SettingsUpdater
         vm.IsIncludingSubdirectories = false;
         Settings.Sorting.IncludeSubDirectories = false;
         
-        await vm.ImageIterator.ReloadFileList().ConfigureAwait(false);
+        await NavigationManager.ReloadFileListAsync().ConfigureAwait(false);
         SetTitleHelper.SetTitle(vm);
     }
     
@@ -129,7 +129,7 @@ public static class SettingsUpdater
         vm.IsIncludingSubdirectories = true;
         Settings.Sorting.IncludeSubDirectories = true;
         
-        await vm.ImageIterator.ReloadFileList();
+        await NavigationManager.ReloadFileListAsync().ConfigureAwait(false);
         SetTitleHelper.SetTitle(vm);
     }
     
@@ -150,8 +150,8 @@ public static class SettingsUpdater
             {
                 await Dispatcher.UIThread.InvokeAsync(() =>
                 {
-                    vm.PlatformService.SetTaskbarProgress((ulong)vm.ImageIterator?.CurrentIndex!,
-                        (ulong)vm.ImageIterator?.ImagePaths?.Count!);
+                    vm.PlatformService.SetTaskbarProgress((ulong)NavigationManager.GetCurrentIndex,
+                        (ulong)NavigationManager.GetCount);
                 });
             }
         }
@@ -194,8 +194,15 @@ public static class SettingsUpdater
         vm.IsShowingSideBySide = true;
         if (NavigationManager.CanNavigate(vm))
         {
-            var preloadValue = await vm.ImageIterator?.GetNextPreLoadValueAsync();
-            vm.SecondaryImageSource = preloadValue?.ImageModel.Image;
+            var preloadValue = await NavigationManager.GetNextPreLoadValueAsync();
+            if (preloadValue is null)
+            {
+#if DEBUG
+                Console.WriteLine($"{nameof(TurnOnSideBySide)} {nameof(preloadValue)} is null");       
+#endif
+                return;
+            }
+            vm.SecondaryImageSource = preloadValue.ImageModel.Image;
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
                 WindowResizing.SetSize(vm.ImageWidth, vm.ImageHeight, preloadValue.ImageModel.PixelWidth,

+ 10 - 13
src/PicView.Avalonia/StartUp/QuickLoad.cs

@@ -44,8 +44,8 @@ public static class QuickLoad
         PreLoadValue? secondaryPreloadValue = null;
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
-            vm.ImageIterator = new ImageIterator(fileInfo, vm);
-            secondaryPreloadValue = await vm.ImageIterator.GetNextPreLoadValueAsync();
+            NavigationManager.InitializeImageIterator(vm);
+            secondaryPreloadValue = await NavigationManager.GetNextPreLoadValueAsync();
             vm.SecondaryImageSource = secondaryPreloadValue?.ImageModel?.Image;
         }
         await Dispatcher.UIThread.InvokeAsync(() =>
@@ -64,8 +64,8 @@ public static class QuickLoad
 
         vm.IsLoading = false;
         
-        vm.ImageIterator ??= new ImageIterator(fileInfo, vm);
-        vm.GetIndex = vm.ImageIterator.CurrentIndex + 1;
+        NavigationManager.InitializeImageIterator(vm);
+        vm.GetIndex = NavigationManager.GetNonZeroIndex;
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
@@ -82,7 +82,7 @@ public static class QuickLoad
         {
             if (TiffManager.IsTiff(imageModel.FileInfo.FullName))
             {
-                SetTitleHelper.TrySetTiffTitle(imageModel.PixelWidth, imageModel.PixelHeight, vm.ImageIterator.CurrentIndex, fileInfo, vm);
+                SetTitleHelper.TrySetTiffTitle(imageModel.PixelWidth, imageModel.PixelHeight, NavigationManager.GetCurrentIndex, fileInfo, vm);
             }
             else
             {
@@ -114,24 +114,21 @@ public static class QuickLoad
             FileHistoryNavigation.Add(fileInfo.FullName);
         }
 
-        vm.ImageIterator.Add(vm.ImageIterator.CurrentIndex, imageModel);
+        NavigationManager.AddToPreloader(NavigationManager.GetCurrentIndex, imageModel);
         
-        var tasks = new List<Task>
-        {
-            vm.ImageIterator.AddAsync(vm.ImageIterator.CurrentIndex)
-        };
+        var tasks = new List<Task>();
         
-        if (vm.ImageIterator.ImagePaths.Count > 1)
+        if (NavigationManager.GetCount > 1)
         {
             if (Settings.UIProperties.IsTaskbarProgressEnabled)
             {
                 await Dispatcher.UIThread.InvokeAsync(() =>
                 {
-                    vm.PlatformService.SetTaskbarProgress((ulong)vm.ImageIterator.CurrentIndex, (ulong)vm.ImageIterator.ImagePaths.Count);
+                    vm.PlatformService.SetTaskbarProgress((ulong)NavigationManager.GetCurrentIndex, (ulong)NavigationManager.GetCount);
                 });
             }
 
-            tasks.Add(vm.ImageIterator.Preload());
+            tasks.Add(NavigationManager.PreloadAsync());
         }
 
         if (Settings.Gallery.IsBottomGalleryShown)

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

@@ -142,7 +142,7 @@ public static class HideInterfaceLogic
                 return;
             }
             
-            if (vm.ImageIterator is null)
+            if (!NavigationManager.CanNavigate(vm))
             {
                 Dispatcher.UIThread.Invoke(() =>
                 {
@@ -152,7 +152,7 @@ public static class HideInterfaceLogic
                 return;
             }
 
-            if (vm.ImageIterator.ImagePaths?.Count <= 1)
+            if (NavigationManager.GetCount <= 1)
             {
                 Dispatcher.UIThread.Invoke(() =>
                 {
@@ -236,14 +236,14 @@ public static class HideInterfaceLogic
             return;
         }
 
-        if (vm.ImageIterator is null)
+        if (!NavigationManager.CanNavigate(vm))
         {
             parent.Opacity = 0;
             childControl.Opacity = 0;
             return;
         }
 
-        if (vm.ImageIterator.ImagePaths?.Count <= 1)
+        if (NavigationManager.GetCount <= 1)
         {
             parent.Opacity = 0;
             childControl.Opacity = 0;

+ 12 - 11
src/PicView.Avalonia/UI/SetTitleHelper.cs

@@ -1,4 +1,5 @@
 using PicView.Avalonia.ImageHandling;
+using PicView.Avalonia.Navigation;
 using PicView.Avalonia.ViewModels;
 using PicView.Core.FileHandling;
 using PicView.Core.ImageDecoding;
@@ -11,7 +12,7 @@ public static class SetTitleHelper
 {
     public static void SetTitle(MainViewModel vm)
     {
-        if (vm.ImageIterator is null || vm.FileInfo is null)
+        if (!NavigationManager.CanNavigate(vm))
         {
             string title;
             var s = vm.Title;
@@ -35,8 +36,8 @@ public static class SetTitleHelper
             return;
         }
 
-        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(vm.PixelWidth, vm.PixelHeight, vm.ImageIterator.CurrentIndex,
-            vm.FileInfo, vm.ZoomValue, vm.ImageIterator.ImagePaths);
+        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(vm.PixelWidth, vm.PixelHeight, NavigationManager.GetCurrentIndex,
+            vm.FileInfo, vm.ZoomValue, NavigationManager.GetCollection);
         vm.WindowTitle = windowTitles.TitleWithAppName;
         vm.Title = windowTitles.BaseTitle;
         vm.TitleTooltip = windowTitles.FilePathTitle;
@@ -74,8 +75,8 @@ public static class SetTitleHelper
             return;
         }
 
-        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel.PixelWidth, imageModel.PixelHeight,  vm.ImageIterator.CurrentIndex,
-            imageModel.FileInfo,  vm.ZoomValue,  vm.ImageIterator.ImagePaths);
+        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel.PixelWidth, imageModel.PixelHeight,  NavigationManager.GetCurrentIndex,
+            imageModel.FileInfo,  vm.ZoomValue,  NavigationManager.GetCollection);
         vm.WindowTitle = windowTitles.TitleWithAppName;
         vm.Title = windowTitles.BaseTitle;
         vm.TitleTooltip = windowTitles.FilePathTitle;
@@ -93,7 +94,7 @@ public static class SetTitleHelper
     public static void SetTiffTitle(TiffManager.TiffNavigationInfo tiffNavigationInfo, int width, int height, int index, FileInfo fileInfo, MainViewModel vm)
     {
         var name = tiffNavigationInfo.Pages[tiffNavigationInfo.CurrentPage].FileName + $" [{tiffNavigationInfo.CurrentPage + 1}/{tiffNavigationInfo.PageCount}]";
-        var singeImageWindowTitles = ImageTitleFormatter.GenerateTiffTitleStrings(width, height, index, fileInfo, tiffNavigationInfo, 1, vm.ImageIterator.ImagePaths);
+        var singeImageWindowTitles = ImageTitleFormatter.GenerateTiffTitleStrings(width, height, index, fileInfo, tiffNavigationInfo, 1, NavigationManager.GetCollection);
         vm.WindowTitle = singeImageWindowTitles.TitleWithAppName;
         vm.Title = singeImageWindowTitles.BaseTitle; 
         vm.TitleTooltip = singeImageWindowTitles.BaseTitle;
@@ -110,7 +111,7 @@ public static class SetTitleHelper
                 Pages = TiffManager.LoadTiffPages(fileInfo.FullName)
             };
             SetTiffTitle(tiffNavigationInfo, width, height,
-                vm.ImageIterator.CurrentIndex, fileInfo, vm);
+                NavigationManager.GetCurrentIndex, fileInfo, vm);
         }
         else
         {
@@ -132,10 +133,10 @@ public static class SetTitleHelper
             return;
         }
 
-        var firstWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel1.PixelWidth, imageModel1.PixelHeight,  vm.ImageIterator.CurrentIndex,
-            imageModel1.FileInfo,  vm.ZoomValue,  vm.ImageIterator.ImagePaths);
-        var secondWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel2.PixelWidth, imageModel2.PixelHeight,  vm.ImageIterator.NextIndex,
-            imageModel2.FileInfo,  vm.ZoomValue,  vm.ImageIterator.ImagePaths);
+        var firstWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel1.PixelWidth, imageModel1.PixelHeight,  NavigationManager.GetCurrentIndex,
+            imageModel1.FileInfo,  vm.ZoomValue,  NavigationManager.GetCollection);
+        var secondWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel2.PixelWidth, imageModel2.PixelHeight,  NavigationManager.GetNextIndex,
+            imageModel2.FileInfo,  vm.ZoomValue,  NavigationManager.GetCollection);
         var windowTitle = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd {secondWindowTitles.BaseTitle} - PicView";
         var title = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd  {secondWindowTitles.BaseTitle}";
         var titleTooltip = $"{firstWindowTitles.FilePathTitle} \u21dc || \u21dd  {secondWindowTitles.FilePathTitle}";

+ 1 - 8
src/PicView.Avalonia/ViewModels/ImageCropperViewModel.cs

@@ -163,14 +163,7 @@ public class ImageCropperViewModel : ViewModelBase
     }
 
     private (string fileName, FileInfo fileInfo, Bitmap? bitmap) PrepareCropData(MainViewModel vm)
-    {
-        if (vm.ImageIterator?.ImagePaths is null || vm.ImageIterator.ImagePaths.Count < 0)
-        {
-            return CreateNewCroppedImage();
-        }
-        
-        return (vm.FileInfo.FullName, vm.FileInfo, null);
-    }
+      => NavigationManager.IsCollectionEmpty ? CreateNewCroppedImage() : (vm.FileInfo.FullName, vm.FileInfo, null);
 
     private (string fileName, FileInfo fileInfo, Bitmap bitmap) CreateNewCroppedImage()
     {

+ 1 - 20
src/PicView.Avalonia/ViewModels/MainViewModel.cs

@@ -1186,12 +1186,6 @@ public class MainViewModel : ViewModelBase
 
     #endregion Fields
 
-    #region Services
-
-    public ImageIterator? ImageIterator;
-
-    #endregion Services
-
     #region Methods
 
     #region Sorting Order
@@ -1216,10 +1210,7 @@ public class MainViewModel : ViewModelBase
         var success = await ConversionHelper.ResizeImageByPercentage(FileInfo, percentage);
         if (success)
         {
-            if (ImageIterator is not null)
-            {
-                await ImageIterator.QuickReload().ConfigureAwait(false);
-            }
+            await NavigationManager.QuickReload();
         }
         else
         {
@@ -1299,21 +1290,11 @@ public class MainViewModel : ViewModelBase
         {
             try
             {
-                var index = ImageIterator?.ImagePaths.IndexOf(path);
-                if (!index.HasValue)
-                {
-                    return;
-                }
-
                 var errorMsg = FileDeletionHelper.DeleteFileWithErrorMsg(path, recycle: false);
                 if (!string.IsNullOrWhiteSpace(errorMsg))
                 {
                     _ = TooltipHelper.ShowTooltipMessageAsync(errorMsg);
                 }
-                else
-                {
-                    ImageIterator?.RemoveItemFromPreLoader(index.Value);
-                }
             }
             catch (Exception e)
             {

+ 2 - 6
src/PicView.Avalonia/Views/ExifView.axaml.cs

@@ -160,10 +160,7 @@ public partial class ExifView : UserControl
                 var success = await ConversionHelper.ResizeByWidth(vm.FileInfo, widthValue).ConfigureAwait(false);
                 if (success)
                 {
-                    if (vm.ImageIterator is not null)
-                    {
-                        await vm.ImageIterator.QuickReload().ConfigureAwait(false);
-                    }
+                    await NavigationManager.QuickReload().ConfigureAwait(false);
                 }
             }
         }
@@ -178,8 +175,7 @@ public partial class ExifView : UserControl
                 var success = await ConversionHelper.ResizeByHeight(vm.FileInfo, heightValue).ConfigureAwait(false);
                 if (success)
                 {
-                    vm.ImageIterator?.RemoveCurrentItemFromPreLoader();
-                    await NavigationManager.Navigate(vm.ImageIterator.CurrentIndex, vm).ConfigureAwait(false);
+                    await NavigationManager.QuickReload().ConfigureAwait(false);
                 }
             }
         }

+ 1 - 1
src/PicView.Avalonia/Views/MainView.axaml.cs

@@ -139,7 +139,7 @@ public partial class MainView : UserControl
 #endif
             var secondaryAccentBrush = (SolidColorBrush)secondaryAccentColor;
             var fileLocation = FileHistoryNavigation.GetFileLocation(index);
-            var selected = vm.ImageIterator?.CurrentIndex == vm.ImageIterator?.ImagePaths.IndexOf(fileLocation);
+            var selected = NavigationManager.GetCurrentIndex == NavigationManager.GetCollection.IndexOf(fileLocation);
             var header = Path.GetFileNameWithoutExtension(fileLocation);
             header = header.Length > 60 ? header.Shorten(60) : header;
         

+ 1 - 4
src/PicView.Avalonia/Views/SingleImageResizeView.axaml.cs

@@ -239,10 +239,7 @@ public partial class SingleImageResizeView : UserControl
     {
         if (destination == file)
         {
-            if (NavigationManager.CanNavigate(vm) && vm.ImageIterator is not null)
-            {
-                await vm.ImageIterator.QuickReload().ConfigureAwait(false);
-            }
+            await NavigationManager.QuickReload().ConfigureAwait(false);
         }
         else if (Path.GetDirectoryName(file) == Path.GetDirectoryName(destination))
         {

+ 10 - 13
src/PicView.Avalonia/Views/UC/EditableTitlebar.axaml.cs

@@ -143,7 +143,14 @@ public partial class EditableTitlebar : UserControl
         // Check if the file is being moved to a different directory
         if (Path.GetDirectoryName(oldPath) != Path.GetDirectoryName(newPath))
         {
-            vm.ImageIterator?.RemoveCurrentItemFromPreLoader();
+            await Dispatcher.UIThread.InvokeAsync(() =>
+            {
+                TextBox.ClearSelection();
+                Cursor = new Cursor(StandardCursorType.Arrow);
+                MainKeyboardShortcuts.IsKeysEnabled = true;
+                UIHelper.GetMainView.Focus();
+            });
+            NavigationManager.RemoveCurrentItemFromPreLoader();
             await NavigationManager.Navigate(true, vm);
             FileHelper.RenameFile(oldPath, newPath);
             return;
@@ -161,7 +168,7 @@ public partial class EditableTitlebar : UserControl
             if (saved)
             {
                 // Delete old file
-                vm.ImageIterator?.RemoveItemFromPreLoader(oldPath);
+                NavigationManager.RemoveItemFromPreLoader(oldPath);
                 
                 var deleteMsg = FileDeletionHelper.DeleteFileWithErrorMsg(oldPath, false);
                 if (!string.IsNullOrWhiteSpace(deleteMsg))
@@ -199,17 +206,7 @@ public partial class EditableTitlebar : UserControl
                 UIHelper.GetMainView.Focus();
             });
             vm.IsEditableTitlebarOpen = false;
-            await vm.ImageIterator?.ResynchronizeAsync();
-            vm.FileInfo = new FileInfo(newPath);
-            SetTitleHelper.RefreshTitle(vm);
-            if (vm.Title == TranslationHelper.Translation.UnexpectedError)
-            {
-                var preloadValue = await vm.ImageIterator?.GetCurrentPreLoadValueAsync();
-                if (preloadValue?.ImageModel is not null)
-                {
-                    SetTitleHelper.SetTitle(vm, preloadValue.ImageModel);
-                }
-            }
+            await NavigationManager.LoadPicFromFile(newPath, vm);
         }
     }
     

+ 2 - 2
src/PicView.Avalonia/Views/UC/Menus/ImageMenu.axaml.cs

@@ -66,9 +66,9 @@ public partial class ImageMenu  : AnimatedMenu
             {
                 number = 0;
             }
-            else if (number > vm.ImageIterator?.ImagePaths?.Count)
+            else if (number > NavigationManager.GetCount)
             {
-                number = vm.ImageIterator?.ImagePaths?.Count - 1 ?? 0;
+                number = NavigationManager.GetCount - 1;
             }
             else
             {

+ 6 - 7
src/PicView.Avalonia/WindowBehavior/WindowResizing.cs

@@ -4,10 +4,10 @@ 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.Navigation;
 
 namespace PicView.Avalonia.WindowBehavior;
 
@@ -51,7 +51,7 @@ public static class WindowResizing
     public static void SetSize(MainViewModel vm)
     {
         double firstWidth, firstHeight;
-        var preloadValue = vm.ImageIterator?.GetCurrentPreLoadValue();
+        var preloadValue = NavigationManager.GetCurrentPreLoadValue();
         if (preloadValue == null)
         {
             if (vm.FileInfo is null)
@@ -86,19 +86,18 @@ public static class WindowResizing
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
-            var secondaryPreloadValue = vm.ImageIterator?.GetNextPreLoadValue();
+            var secondaryPreloadValue = NavigationManager.GetNextPreLoadValue();
             double secondWidth, secondHeight;
             if (secondaryPreloadValue != null)
             {
                 secondWidth = GetWidth();
                 secondHeight = GetHeight();
             }
-            else if (vm.ImageIterator is not null)
+            else if (NavigationManager.CanNavigate(vm))
             {
-                var nextIndex = vm.ImageIterator.GetIteration(vm.ImageIterator.CurrentIndex,
-                    vm.ImageIterator.IsReversed ? NavigateTo.Previous : NavigateTo.Next);
+                var nextFileName = NavigationManager.GetNextFileName;
                 var magickImage = new MagickImage();
-                magickImage.Ping(vm.ImageIterator.ImagePaths[nextIndex]);
+                magickImage.Ping(nextFileName);
                 secondWidth = magickImage.Width;
                 secondHeight = magickImage.Height;
             }