| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Linq; |
| | | 4 | | using SwitchBlade.Contracts; |
| | | 5 | | |
| | | 6 | | namespace SwitchBlade.Services |
| | | 7 | | { |
| | | 8 | | /// <summary> |
| | | 9 | | /// Implementation of INavigationService. |
| | | 10 | | /// Handles selection preservation and navigation calculations. |
| | | 11 | | /// </summary> |
| | | 12 | | public class NavigationService : INavigationService |
| | | 13 | | { |
| | | 14 | | public WindowItem? ResolveSelection( |
| | | 15 | | IList<WindowItem> filteredWindows, |
| | | 16 | | IntPtr? previousHwnd, |
| | | 17 | | string? previousTitle, |
| | | 18 | | int previousIndex, |
| | | 19 | | RefreshBehavior behavior, |
| | | 20 | | bool resetSelection) |
| | 9 | 21 | | { |
| | 9 | 22 | | if (filteredWindows == null || filteredWindows.Count == 0) |
| | 2 | 23 | | return null; |
| | | 24 | | |
| | | 25 | | // Force first item if reset requested (user typing) |
| | 7 | 26 | | if (resetSelection) |
| | 1 | 27 | | return filteredWindows[0]; |
| | | 28 | | |
| | 6 | 29 | | switch (behavior) |
| | | 30 | | { |
| | | 31 | | case RefreshBehavior.PreserveIdentity: |
| | 2 | 32 | | return ResolveByIdentity(filteredWindows, previousHwnd, previousTitle); |
| | | 33 | | |
| | | 34 | | case RefreshBehavior.PreserveIndex: |
| | 1 | 35 | | return ResolveByIndex(filteredWindows, previousIndex); |
| | | 36 | | |
| | | 37 | | case RefreshBehavior.PreserveScroll: |
| | | 38 | | default: |
| | 3 | 39 | | return ResolveByScroll(filteredWindows, previousHwnd, previousTitle, previousIndex); |
| | | 40 | | } |
| | 9 | 41 | | } |
| | | 42 | | |
| | | 43 | | private static WindowItem ResolveByIdentity(IList<WindowItem> windows, IntPtr? hwnd, string? title) |
| | 2 | 44 | | { |
| | 6 | 45 | | var match = windows.FirstOrDefault(w => w.Hwnd == hwnd && w.Title == title); |
| | 2 | 46 | | return match ?? windows[0]; |
| | 2 | 47 | | } |
| | | 48 | | |
| | | 49 | | private static WindowItem ResolveByIndex(IList<WindowItem> windows, int previousIndex) |
| | 1 | 50 | | { |
| | 1 | 51 | | int idx = Math.Clamp(previousIndex, 0, windows.Count - 1); |
| | 1 | 52 | | return windows[idx]; |
| | 1 | 53 | | } |
| | | 54 | | |
| | | 55 | | private static WindowItem ResolveByScroll(IList<WindowItem> windows, IntPtr? hwnd, string? title, int previousIn |
| | 3 | 56 | | { |
| | | 57 | | // No previous selection -> select first |
| | 3 | 58 | | if (hwnd == null || hwnd == IntPtr.Zero) |
| | 1 | 59 | | return windows[0]; |
| | | 60 | | |
| | | 61 | | // Try to find same item |
| | 6 | 62 | | var sameItem = windows.FirstOrDefault(w => w.Hwnd == hwnd && w.Title == title); |
| | 2 | 63 | | if (sameItem != null) |
| | 1 | 64 | | return sameItem; |
| | | 65 | | |
| | | 66 | | // Fallback to index |
| | 1 | 67 | | int idx = Math.Clamp(previousIndex, 0, windows.Count - 1); |
| | 1 | 68 | | return windows[idx]; |
| | 3 | 69 | | } |
| | | 70 | | |
| | | 71 | | public int CalculateMoveIndex(int currentIndex, int direction, int itemCount) |
| | 7 | 72 | | { |
| | 8 | 73 | | if (itemCount == 0) return -1; |
| | | 74 | | |
| | | 75 | | // Nothing selected |
| | 6 | 76 | | if (currentIndex < 0) |
| | 2 | 77 | | return direction > 0 ? 0 : itemCount - 1; |
| | | 78 | | |
| | 4 | 79 | | int newIndex = currentIndex + direction; |
| | 4 | 80 | | return Math.Clamp(newIndex, 0, itemCount - 1); |
| | 7 | 81 | | } |
| | | 82 | | |
| | | 83 | | public int CalculatePageMoveIndex(int currentIndex, int direction, int pageSize, int itemCount) |
| | 6 | 84 | | { |
| | 8 | 85 | | if (itemCount == 0 || pageSize <= 0) return -1; |
| | | 86 | | |
| | 4 | 87 | | int idx = currentIndex < 0 ? 0 : currentIndex; |
| | 4 | 88 | | int newIndex = idx + (direction * pageSize); |
| | 4 | 89 | | return Math.Clamp(newIndex, 0, itemCount - 1); |
| | 6 | 90 | | } |
| | | 91 | | } |
| | | 92 | | } |