From 06abd875606a40c90840c549372eca056a9bbb1b Mon Sep 17 00:00:00 2001 From: Stepan Pilipenko Date: Tue, 18 Nov 2025 20:18:45 +0300 Subject: [PATCH] move focus --- FileSystemItem.cs | 10 ++++- MainPage.xaml.cs | 110 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/FileSystemItem.cs b/FileSystemItem.cs index 311955a..233f66e 100644 --- a/FileSystemItem.cs +++ b/FileSystemItem.cs @@ -9,6 +9,12 @@ public class FileSystemItem : INotifyPropertyChanged public string FullName { get; set; } = string.Empty; public bool IsDirectory { get; set; } + private static string _padding = DefinePadding(); + private static string DefinePadding() + { + return new string('\u00A0', 300); + } + public string DisplayText { get @@ -17,9 +23,9 @@ public class FileSystemItem : INotifyPropertyChanged return "[No Name]"; if (Name == "..") - return "⬆️ .."; + return "⬆️ .." + _padding; - return IsDirectory ? $"📁 {Name}" : $"📄 {Name}"; + return IsDirectory ? $"📁 {Name + _padding}" : $"📄 {Name + _padding}"; } } diff --git a/MainPage.xaml.cs b/MainPage.xaml.cs index 289409b..41c683a 100644 --- a/MainPage.xaml.cs +++ b/MainPage.xaml.cs @@ -55,6 +55,8 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged // Для двойного нажатия private FileSystemItem? _lastClickedItem; + private int _lastLeftSelectedIndex = 0; + private int _lastRightSelectedIndex = 0; private bool? _lastIsLeftPanel; public MainPage(IFileSystemService fileService) @@ -147,14 +149,87 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged UpdateVisualSelection(); } + // Переключаем фокус с указанием направления + private void MoveFocus(bool moveForward = true) + { + var allFocusable = GetFocusableElements(); + var current = allFocusable.FirstOrDefault(x => x.IsFocused); + var next = moveForward ? + GetNextFocusable(current, allFocusable) : + GetPreviousFocusable(current, allFocusable); + + next?.Focus(); + } + + // Следующий элемент (для Down/Tab) + private View GetNextFocusable(View current, List allElements) + { + if (current == null) return allElements.FirstOrDefault(); + + var currentIndex = allElements.IndexOf(current); + if (currentIndex == -1) return allElements.FirstOrDefault(); + + var nextIndex = (currentIndex + 1) % allElements.Count; + return allElements[nextIndex]; + } + + // Предыдущий элемент (для Up/Shift+Tab) + private View GetPreviousFocusable(View current, List allElements) + { + if (current == null) return allElements.LastOrDefault(); + + var currentIndex = allElements.IndexOf(current); + if (currentIndex == -1) return allElements.LastOrDefault(); + + var prevIndex = (currentIndex - 1 + allElements.Count) % allElements.Count; + return allElements[prevIndex]; + } + + private List GetFocusableElements() + { + return this.GetVisualTreeDescendants() + .OfType() + .Where(x => x.IsEnabled && x.IsVisible) + .OrderBy(GetVisualTreeOrder) // Сортируем по порядку в визуальном дереве + .ToList(); + } + + private int GetVisualTreeOrder(View view) + { + // Простой способ - используем порядок в визуальном дереве + var parent = view.Parent as Layout; + if (parent != null) + { + var index = parent.Children.IndexOf(view); + return index >= 0 ? index : 0; + } + return 0; + } + // Обработчики выделения (вызываются при изменении Selection в CollectionView) private void OnLeftSelectionChanged(object sender, SelectionChangedEventArgs e) { - // Обновляем индексы при программном изменении selection if (e.CurrentSelection.FirstOrDefault() is FileSystemItem selectedItem) { _leftSelectedIndex = LeftItems.IndexOf(selectedItem); _isLeftPanelActive = true; + + System.Diagnostics.Debug.WriteLine($"L !!! {_leftSelectedIndex} {_lastLeftSelectedIndex}"); + if (_lastLeftSelectedIndex < LeftItems.Count && _lastLeftSelectedIndex >= 0) + { + // Автоматически переключаем фокус при достижении границ + if (_leftSelectedIndex > _lastLeftSelectedIndex) + { + // Достигли конца списка - переходим к следующему элементу + MoveFocus(moveForward: true); + } + else if (_leftSelectedIndex < _lastLeftSelectedIndex) + { + // Достигли начала списка - переходим к предыдущему элементу + MoveFocus(moveForward: false); + } + } + UpdateVisualSelection(); } } @@ -165,6 +240,23 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged { _rightSelectedIndex = RightItems.IndexOf(selectedItem); _isLeftPanelActive = false; + + System.Diagnostics.Debug.WriteLine($"R !!! {_rightSelectedIndex} {_lastRightSelectedIndex}"); + if (_rightSelectedIndex < RightItems.Count && _rightSelectedIndex >= 0) + { + // Автоматически переключаем фокус при достижении границ + if (_rightSelectedIndex > _lastRightSelectedIndex) + { + // Достигли конца списка - переходим к следующему элементу + MoveFocus(moveForward: true); + } + else if (_rightSelectedIndex < _lastRightSelectedIndex) + { + // Достигли начала списка - переходим к предыдущему элементу + MoveFocus(moveForward: false); + } + } + UpdateVisualSelection(); } } @@ -197,8 +289,8 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged private void SetFocusToItem(CollectionView collectionView, FileSystemItem item) { // Используем Dispatcher чтобы дождаться рендеринга - Dispatcher.Dispatch(() => - { + //Dispatcher.Dispatch(() => + //{ var container = FindButtonContainer(collectionView, item); if (container is Button button) { @@ -206,10 +298,20 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged _currentFocusedButton = button; _lastClickedItem = item; + // Простой способ - используем текущие индексы + if (_isLeftPanelActive) + { + _lastLeftSelectedIndex = _leftSelectedIndex; + } + else + { + _lastRightSelectedIndex = _rightSelectedIndex; + } + // Прокручиваем к выбранному элементу collectionView.ScrollTo(item, position: ScrollToPosition.MakeVisible, animate: false); } - }); + //}); } private Button? FindButtonContainer(CollectionView collectionView, FileSystemItem item)