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

#66 Initial navigation test and hotkeys

Ruben 4 лет назад
Родитель
Сommit
38ef088648

+ 0 - 12
src/PicView.Data/IO/FileLists.cs

@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace PicView.Data.IO
-{
-    public class FileLists
-    {
-        
-    }
-}

+ 42 - 0
src/PicView.Data/IO/FileListsHelpercs.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace PicView.Data.IO
+{
+    public static class FileListHelper
+    {
+        public static List<string>? GetFileList(FileInfo fileInfo, SortFilesBy sortFilesBy)
+        {
+            if (fileInfo is null) { return null; }
+            
+            var getAttributes = File.GetAttributes(fileInfo.FullName);
+            var directory = getAttributes.HasFlag(FileAttributes.Directory) ? fileInfo.FullName : fileInfo.DirectoryName;
+
+            var items = Directory.EnumerateFiles(directory, "*.*",
+                    SearchOption.AllDirectories).AsParallel().Where(SupportedFiles.IsSupportedExt);
+
+            switch (sortFilesBy)
+            {
+                default:
+                case SortFilesBy.Name: // Alphanumeric sort
+                    var list = items.ToList();
+                    return list;
+
+                case SortFilesBy.FileSize:
+                    throw new NotImplementedException();
+                case SortFilesBy.Extension:
+                    throw new NotImplementedException();
+                case SortFilesBy.Creationtime:
+                    throw new NotImplementedException();
+                case SortFilesBy.Lastaccesstime:
+                    throw new NotImplementedException();
+                case SortFilesBy.Lastwritetime:
+                    throw new NotImplementedException();
+                case SortFilesBy.Random:
+                    return items.OrderBy(f => Guid.NewGuid()).ToList();
+            }
+        }
+    }
+}

+ 144 - 0
src/PicView.Data/IO/SupportedFiles.cs

@@ -0,0 +1,144 @@
+namespace PicView.Data.IO;
+
+public static class SupportedFiles
+    {
+        /// <summary>
+        /// Check if supported:
+        /// Returns true if common files,
+        /// False if uncommon,
+        /// Null if unsupported
+        /// </summary>
+        /// <param name="ext">File extension</param>
+        /// <returns></returns>
+        public static bool? IsSupportedFile(string file)
+        {
+            string ext = Path.GetExtension(file);
+            switch (ext)
+            {
+                // Standards
+                case { } when ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".jpeg", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".jpe", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".png", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".bmp", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".gif", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".jfif", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".ico", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".webp", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".wbmp", StringComparison.OrdinalIgnoreCase):
+                    return true;
+
+                // Photoshop
+                case { } when ext.Equals(".psd", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".psb", StringComparison.OrdinalIgnoreCase):
+
+                // additional
+                case { } when ext.Equals(".tif", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".tiff", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".dds", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".tga", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".heic", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".heif", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".hdr", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".xcf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".jxl", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".jp2", StringComparison.OrdinalIgnoreCase):
+
+                // Base64
+                case { } when ext.Equals(".b64", StringComparison.OrdinalIgnoreCase):
+
+                // Vector
+                case { } when ext.Equals(".svg", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".svgz", StringComparison.OrdinalIgnoreCase):
+
+
+                // Camera
+                case { } when ext.Equals(".3fr", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".arw", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".cr2", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".cr3", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".crw", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".dcr", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".dng", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".erf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".kdc", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".mdc", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".mef", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".mos", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".mrw", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".nef", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".nrw", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".orf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".pef", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".raf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".raw", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".rw2", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".srf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".x3f", StringComparison.OrdinalIgnoreCase):
+
+                // Others
+                case { } when ext.Equals(".pgm", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".ppm", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".cut", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".exr", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".dib", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".emf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".wmf", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".wpg", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".pcx", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".xbm", StringComparison.OrdinalIgnoreCase):
+                case { } when ext.Equals(".xpm", StringComparison.OrdinalIgnoreCase):
+
+                    return false;
+
+                default: return null;
+            }
+        }
+
+        public static bool IsSupportedExt(string file)
+        {
+            var supported = IsSupportedFile(file);
+            return supported is not null;
+        }
+
+        /// <summary>
+        /// Returns true if supported archive
+        /// </summary>
+        /// <param name="ext"></param>
+        /// <returns></returns>
+        public static bool IsSupportedArchives(string file)
+        {
+            return IsSupportedArchives(new FileInfo(file));
+        }
+
+        /// <summary>
+        /// Returns true if supported archive
+        /// </summary>
+        /// <param name="ext"></param>
+        /// <returns></returns>
+        public static bool IsSupportedArchives(FileInfo fileInfo)
+        {
+            switch (fileInfo.Extension)
+            {
+                // Standards
+                case { } when fileInfo.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".7zip", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".7z", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".rar", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".cbr", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".cb7", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".cbt", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".cbz", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".xz", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".bzip2", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".gzip", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".tar", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".wim", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase):
+                case { } when fileInfo.Extension.Equals(".cab", StringComparison.OrdinalIgnoreCase):
+                    return true;
+
+                default: return false;
+            }
+        }
+    }

+ 3 - 2
src/PicView.Data/Imaging/ImageDecoder.cs

@@ -1,4 +1,5 @@
-using Avalonia.Media.Imaging;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
 
 namespace PicView.Data.Imaging
 {
@@ -9,7 +10,7 @@ namespace PicView.Data.Imaging
         /// </summary>
         /// <param name="fileInfo">Cannot be null</param>
         /// <returns></returns>
-        public static async Task<Avalonia.Media.IImage?> ReturnPicAsync(FileInfo fileInfo)
+        public static async Task<IImage?> GetPicAsync(FileInfo fileInfo)
         {
             if (fileInfo == null) { return null; }
             if (fileInfo.Length <= 0) { return null; }

+ 90 - 0
src/PicView/Navigation/ImageIterator.cs

@@ -0,0 +1,90 @@
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using PicView.Data.Imaging;
+using PicView.Data.IO;
+
+namespace PicView.Navigation
+{
+    public class ImageIterator
+    {
+        public static int FolderIndex { get; private set; }
+        static List<string> Pics { get; set; }
+        
+        public ImageIterator()
+        {
+            Pics = new List<string>();
+        }
+
+        public ImageIterator(FileInfo fileInfo)
+        {
+            Pics = FileListHelper.GetFileList(fileInfo, SortFilesBy.Name) ?? throw new NotImplementedException();;
+        }
+        
+        public async Task<IImage> GetPicFromFileAsync(FileInfo fileInfo)
+        {
+            return await ImageDecoder.GetPicAsync(fileInfo).ConfigureAwait(false) ?? throw new NotImplementedException();;
+        }
+
+        public async Task<IImage> GetPicAtIndex(int index)
+        {
+            FolderIndex = index;
+            return await ImageDecoder.GetPicAsync(new FileInfo(Pics[index])).ConfigureAwait(false) ?? throw new NotImplementedException();;
+        }
+    
+        public async Task<IImage>  Next()
+        {
+            return await GetPicAtIndex(GetImageIterateIndex()).ConfigureAwait(false);
+        }
+
+        public async Task<IImage>  Prev()
+        {
+            return await GetPicAtIndex(GetImageIterateIndex(false)).ConfigureAwait(false);
+        }
+        
+        public async Task<IImage>  Last()
+        {
+            return await GetPicAtIndex(GetImageIterateIndex(false, true)).ConfigureAwait(false);
+        }
+
+        public async Task<IImage>  First()
+        {
+            return await GetPicAtIndex(GetImageIterateIndex(true, true)).ConfigureAwait(false);
+        }
+        
+        private static int GetImageIterateIndex(bool forward = true, bool end = false)
+        {
+            var next = FolderIndex;
+
+            if (end) // Go to first or last
+            {
+                next = forward ? Pics.Count - 1 : 0;
+            }
+            else // Go to next or previous
+            {
+                if (forward)
+                {
+                    // Go to next if able
+                    if (FolderIndex + 1 == Pics?.Count)
+                    {
+                        return -1;
+                    }
+
+                    next++;
+                }
+                else
+                {
+                    // Go to prev if able
+                    if (next - 1 < 0)
+                    {
+                        return -1;
+                    }
+
+                    next--;
+                }
+            }
+
+            return next;
+        }
+    }
+}
+

+ 42 - 15
src/PicView/ViewModels/MainWindowViewModel.cs

@@ -6,6 +6,7 @@ using System.Windows.Input;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Media;
+using PicView.Navigation;
 using PicView.Views;
 using Size = Avalonia.Size;
 
@@ -22,32 +23,58 @@ namespace PicView.ViewModels
             
             ExitCommand = ReactiveCommand.Create(desktop.MainWindow.Close);
             MinimizeCommand = ReactiveCommand.Create(() => desktop.MainWindow.WindowState = WindowState.Minimized);
+            
+            Next = ReactiveCommand.Create(async () =>
+            {
+                Iterator ??= new ImageIterator();
+                
+                Pic = await Iterator.Next().ConfigureAwait(false);
+                SetValues(desktop);
+            });
+            
+            Prev = ReactiveCommand.Create(async () =>
+            {
+                Iterator ??= new ImageIterator();
+                
+                Pic = await Iterator.Prev().ConfigureAwait(false);
+                SetValues(desktop);
+            });
+            
             LoadCommand = ReactiveCommand.Create(async () =>
             {
                 var args = Environment.GetCommandLineArgs();
-                if (args.Length < 1) { return; }
-                
-                FileInfo fileInfo = new(args[1]);
-                var pic = await Data.Imaging.ImageDecoder.ReturnPicAsync(fileInfo).ConfigureAwait(false);
-                if (pic is not null)
-                {
-                    Pic = pic;
-                    var (width, height) =
-                        Data.Sizing.ImageSizeHelper.GetScaledImageSize(pic.Size.Width, pic.Size.Height, desktop.MainWindow);
-                    Width = width;
-                    Height = height;
-                    Title = $"{width} x {height}";
-                }
-                else
+                if (args.Length < 1)
                 {
-                    Title = "No image loaded";
+                    Iterator = new ImageIterator();
+                    return;
                 }
+                
+                FileInfo fileInfo = new(args[1]);
+                Iterator = new ImageIterator(fileInfo);
+                Pic = await Iterator.GetPicFromFileAsync(fileInfo).ConfigureAwait(false);
+                SetValues(desktop);
             });
         }
+
+        private void SetValues(IClassicDesktopStyleApplicationLifetime desktop)
+        {
+            var (width, height) =
+                Data.Sizing.ImageSizeHelper.GetScaledImageSize(Pic.Size.Width, Pic.Size.Height, desktop.MainWindow);
+            Width = width;
+            Height = height;
+            Title = $"{Pic.Size.Width} x {Pic.Size.Height}";
+        }
         public ICommand? ExitCommand { get; }
         public ICommand? MinimizeCommand { get; }
         public ICommand? LoadCommand { get; }
         
+        private ImageIterator? Iterator { get; set; }
+        
+        public ICommand? Next { get; }
+        public ICommand? Prev { get; }
+        public ICommand? First { get; }
+        public ICommand? Last { get; }
+        
         private string _title = "Loading...";
         public string Title
         {

+ 13 - 3
src/PicView/Views/MainWindow.axaml

@@ -17,10 +17,19 @@
     xmlns:vm="using:PicView.ViewModels"
     Opened="TopLevel_OnOpened"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
-
+    
     <Design.DataContext>
         <vm:MainWindowViewModel />
     </Design.DataContext>
+    
+    <Window.KeyBindings>
+        <KeyBinding Command="{Binding Next}" Gesture="Right"/>
+        <KeyBinding Command="{Binding Next}" Gesture="D"/>
+        
+        <KeyBinding Command="{Binding Prev}" Gesture="Left"/>
+        <KeyBinding Command="{Binding Next}" Gesture="A"/>
+    </Window.KeyBindings>
+
 
     <Border
         BorderBrush="{StaticResource BorderColor}"
@@ -123,6 +132,7 @@
                             </Button>
 
                             <Button
+                                Command="{Binding Next}"
                                 Background="Transparent"
                                 BorderBrush="{StaticResource BorderColor}"
                                 BorderThickness="0,0,1,1"
@@ -188,13 +198,13 @@
             </Border>
             
             <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
-                <ZoomBorder PanButton="Left" Stretch="Uniform" EnableConstrains="True">
+                <Border>
                     <Image x:Name="MainImage"
                            Stretch="Uniform"
                            Source="{Binding Pic}"
                            Width="{Binding Width}"
                            Height="{Binding Height}" />
-                </ZoomBorder>
+                </Border>
                 
             </ScrollViewer>
         </DockPanel>