diff --git a/Directory.Build.props b/Directory.Build.props index 444044d..660064b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,7 +5,7 @@ disable enable Copyright © Mihnea Rădulescu 2017 - 2024 - 1.2024.07.16 + 1.2024.07.17 $(AssemblyVersion) embedded diff --git a/ImageFanReloaded.Core/Collections/Implementation/CollectionExtensions.cs b/ImageFanReloaded.Core/Collections/Implementation/CollectionExtensions.cs index d25a24e..64df339 100644 --- a/ImageFanReloaded.Core/Collections/Implementation/CollectionExtensions.cs +++ b/ImageFanReloaded.Core/Collections/Implementation/CollectionExtensions.cs @@ -4,9 +4,6 @@ namespace ImageFanReloaded.Core.Collections.Implementation; public static class CollectionExtensions { - public static bool IsIndexWithinBounds(this ICollection collection, int index) - => 0 <= index && index < collection.Count; - public static bool IsIndexWithinBounds(this IReadOnlyCollection collection, int index) - => 0 <= index && index < collection.Count; + => index >= 0 && index < collection.Count; } diff --git a/ImageFanReloaded.Core/Controls/IContentTabItem.cs b/ImageFanReloaded.Core/Controls/IContentTabItem.cs index af836ee..e1e50c3 100644 --- a/ImageFanReloaded.Core/Controls/IContentTabItem.cs +++ b/ImageFanReloaded.Core/Controls/IContentTabItem.cs @@ -48,6 +48,4 @@ public interface IContentTabItem void SetImageStatusBarText(string imageStatusBarText); void SaveMatchingTreeViewItem(FileSystemEntryInfo selectedFileSystemEntryInfo); - - bool IsThumbnailScrollViewerFocused { get; } } diff --git a/ImageFanReloaded.Core/Keyboard/Key.cs b/ImageFanReloaded.Core/Keyboard/Key.cs index 46e28ca..d31de6f 100644 --- a/ImageFanReloaded.Core/Keyboard/Key.cs +++ b/ImageFanReloaded.Core/Keyboard/Key.cs @@ -27,5 +27,8 @@ public enum Key I, Plus, - Minus + Minus, + + PageUp, + PageDown } diff --git a/ImageFanReloaded.Core/Settings/IGlobalParameters.cs b/ImageFanReloaded.Core/Settings/IGlobalParameters.cs index 28febf8..c606021 100644 --- a/ImageFanReloaded.Core/Settings/IGlobalParameters.cs +++ b/ImageFanReloaded.Core/Settings/IGlobalParameters.cs @@ -37,6 +37,9 @@ public interface IGlobalParameters Key PlusKey { get; } Key MinusKey { get; } + + Key PageUpKey { get; } + Key PageDownKey { get; } bool IsBackwardNavigationKey(Key aKey); bool IsForwardNavigationKey(Key aKey); diff --git a/ImageFanReloaded.Core/Settings/Implementation/GlobalParametersBase.cs b/ImageFanReloaded.Core/Settings/Implementation/GlobalParametersBase.cs index 63ebbf0..cdc0032 100644 --- a/ImageFanReloaded.Core/Settings/Implementation/GlobalParametersBase.cs +++ b/ImageFanReloaded.Core/Settings/Implementation/GlobalParametersBase.cs @@ -40,6 +40,9 @@ public abstract class GlobalParametersBase : IGlobalParameters public Key PlusKey { get; } public Key MinusKey { get; } + + public Key PageUpKey { get; } + public Key PageDownKey { get; } public bool IsBackwardNavigationKey(Key aKey) => _backwardNavigationKeys.Contains(aKey); public bool IsForwardNavigationKey(Key aKey) => _forwardNavigationKeys.Contains(aKey); @@ -109,6 +112,9 @@ protected GlobalParametersBase( PlusKey = Key.Plus; MinusKey = Key.Minus; + + PageUpKey = Key.PageUp; + PageDownKey = Key.PageDown; NameComparer = operatingSystemSettings.IsWindows ? new NaturalSortingComparer(StringComparer.InvariantCultureIgnoreCase) diff --git a/ImageFanReloaded.Test/GlobalParametersTest.cs b/ImageFanReloaded.Test/GlobalParametersTest.cs index 30c3132..8a94d83 100644 --- a/ImageFanReloaded.Test/GlobalParametersTest.cs +++ b/ImageFanReloaded.Test/GlobalParametersTest.cs @@ -76,6 +76,9 @@ public void GlobalParameters_IsCorrectlyInitialized() _globalParameters.PlusKey.Should().NotBe(Key.None); _globalParameters.MinusKey.Should().NotBe(Key.None); + _globalParameters.PageUpKey.Should().NotBe(Key.None); + _globalParameters.PageDownKey.Should().NotBe(Key.None); + _globalParameters.NameComparer.Should().NotBeNull(); _globalParameters.ImageFileExtensions.Should().NotBeEmpty(); diff --git a/ImageFanReloaded/Controls/ContentTabItem.axaml.cs b/ImageFanReloaded/Controls/ContentTabItem.axaml.cs index 1149c26..c8ad286 100644 --- a/ImageFanReloaded/Controls/ContentTabItem.axaml.cs +++ b/ImageFanReloaded/Controls/ContentTabItem.axaml.cs @@ -3,7 +3,6 @@ using System.Linq; using Avalonia; using Avalonia.Controls; -using ImageFanReloaded.Core.Collections.Implementation; using ImageFanReloaded.Core.Controls; using ImageFanReloaded.Core.CustomEventArgs; using ImageFanReloaded.Core.DiscAccess; @@ -45,8 +44,9 @@ public void EnableFolderTreeViewSelectedItemChanged() public bool ShouldHandleControlKeyFunctions(KeyModifiers keyModifiers, Key keyPressing) { var shouldHandleKeyPressing = ShouldSwitchControlFocus(keyModifiers, keyPressing) - || ShouldToggleRecursiveFolderAccess(keyModifiers, keyPressing) - || ShouldHandleThumbnailSelection(keyPressing); + || ShouldToggleRecursiveFolderAccess(keyModifiers, keyPressing) + || ShouldHandleThumbnailSelection(keyPressing) + || ShouldHandleThumbnailScrolling(keyPressing); return shouldHandleKeyPressing; } @@ -66,6 +66,10 @@ public void HandleControlKeyFunctions(KeyModifiers keyModifiers, Key keyPressing BringThumbnailIntoView(); DisplayImage(); } + else if (ShouldHandleThumbnailScrolling(keyPressing)) + { + HandleThumbnailScrolling(keyPressing); + } } public void SetFolderTreeViewSelectedItem() @@ -214,13 +218,13 @@ public void SaveMatchingTreeViewItem(FileSystemEntryInfo selectedFileSystemEntry } } } - - public bool IsThumbnailScrollViewerFocused => _thumbnailScrollViewer.IsFocused; #region Private private const string FakeTreeViewItemText = "Loading..."; + private const int ThumbnailScrollAdvanceCount = 20; + private readonly IList _thumbnailBoxCollection; private int _maxThumbnailIndex; @@ -304,7 +308,7 @@ private void OnImageChanged(object? sender, ImageChangedEventArgs e) var imageView = e.ImageView; var increment = e.Increment; - if (AdvanceSelectedThumbnailByIncrement(increment)) + if (AdvanceFromSelectedThumbnail(increment)) { imageView.SetImage(_selectedThumbnailBox!.ImageFile!, _folderAccessType.IsRecursive()); } @@ -323,26 +327,37 @@ private void SelectThumbnailBox(IThumbnailBox thumbnailBox) } } - private bool AdvanceSelectedThumbnailByIncrement(int increment) + private bool AdvanceFromSelectedThumbnail(int increment) { + var canAdvanceToNewSelectedThumbnailIndex = false; + if (_selectedThumbnailBox is not null) { var newSelectedThumbnailIndex = _selectedThumbnailIndex + increment; - if (_thumbnailBoxCollection.IsIndexWithinBounds(newSelectedThumbnailIndex)) + if (newSelectedThumbnailIndex < 0) + { + newSelectedThumbnailIndex = 0; + } + else if (newSelectedThumbnailIndex >= _thumbnailBoxCollection.Count) { + newSelectedThumbnailIndex = _thumbnailBoxCollection.Count - 1; + } + + if (_selectedThumbnailIndex != newSelectedThumbnailIndex) + { + canAdvanceToNewSelectedThumbnailIndex = true; + UnselectThumbnail(); _selectedThumbnailIndex = newSelectedThumbnailIndex; _selectedThumbnailBox = _thumbnailBoxCollection[_selectedThumbnailIndex]; SelectThumbnail(); - - return true; } } - return false; + return canAdvanceToNewSelectedThumbnailIndex; } private void BringThumbnailIntoView() => _selectedThumbnailBox?.BringThumbnailIntoView(); @@ -443,6 +458,17 @@ private bool ShouldHandleThumbnailSelection(Key keyPressing) return false; } + private bool ShouldHandleThumbnailScrolling(Key keyPressing) + { + if (_selectedThumbnailBox is not null && + (keyPressing == GlobalParameters!.PageUpKey || keyPressing == GlobalParameters!.PageDownKey)) + { + return true; + } + + return false; + } + private void SwitchControlFocus() { if (_folderTreeView.SelectedItem is null) @@ -484,17 +510,32 @@ private void ToggleRecursiveFolderAccess(KeyModifiers keyModifiers) RaiseFolderChangedEvent(); } + private void HandleThumbnailScrolling(Key keyPressing) + { + if (_selectedThumbnailBox is not null) + { + if (keyPressing == GlobalParameters!.PageUpKey) + { + AdvanceFromSelectedThumbnail(-ThumbnailScrollAdvanceCount); + } + else if (keyPressing == GlobalParameters!.PageDownKey) + { + AdvanceFromSelectedThumbnail(ThumbnailScrollAdvanceCount); + } + } + } + private void ThumbnailScrollViewerOnKeyPressing(object? sender, Avalonia.Input.KeyEventArgs e) { var keyPressing = e.Key.ToCoreKey(); if (GlobalParameters!.IsBackwardNavigationKey(keyPressing)) { - AdvanceSelectedThumbnailByIncrement(-1); + AdvanceFromSelectedThumbnail(-1); } else if (GlobalParameters!.IsForwardNavigationKey(keyPressing)) { - AdvanceSelectedThumbnailByIncrement(1); + AdvanceFromSelectedThumbnail(1); } e.Handled = true; diff --git a/ImageFanReloaded/Controls/MainWindow.axaml.cs b/ImageFanReloaded/Controls/MainWindow.axaml.cs index f748bf0..dff01df 100644 --- a/ImageFanReloaded/Controls/MainWindow.axaml.cs +++ b/ImageFanReloaded/Controls/MainWindow.axaml.cs @@ -265,7 +265,7 @@ private bool ShouldNavigateToNextTab(KeyModifiers keyModifiers, Key keyPressing) private bool ShouldDisplayHelp(Key keyPressing) => keyPressing == GlobalParameters!.F1Key; private bool ShouldAllowKeyPressingEventPropagation(IContentTabItem contentTabItem, Key keyPressing) - => contentTabItem.IsThumbnailScrollViewerFocused || GlobalParameters!.IsNavigationKey(keyPressing); + => GlobalParameters!.IsNavigationKey(keyPressing); private bool HasAtLeastOneContentTabItem() { diff --git a/ImageFanReloaded/Keyboard/KeyExtensions.cs b/ImageFanReloaded/Keyboard/KeyExtensions.cs index b540b7e..d26676a 100644 --- a/ImageFanReloaded/Keyboard/KeyExtensions.cs +++ b/ImageFanReloaded/Keyboard/KeyExtensions.cs @@ -31,6 +31,9 @@ public static ImageFanReloaded.Core.Keyboard.Key ToCoreKey(this Avalonia.Input.K Avalonia.Input.Key.Add => Core.Keyboard.Key.Plus, Avalonia.Input.Key.OemMinus => Core.Keyboard.Key.Minus, Avalonia.Input.Key.Subtract => Core.Keyboard.Key.Minus, + + Avalonia.Input.Key.PageUp => Core.Keyboard.Key.PageUp, + Avalonia.Input.Key.PageDown => Core.Keyboard.Key.PageDown, _ => Core.Keyboard.Key.None };