|
|
|
|
@ -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<View> 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<View> 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<View> GetFocusableElements()
|
|
|
|
|
{
|
|
|
|
|
return this.GetVisualTreeDescendants()
|
|
|
|
|
.OfType<View>()
|
|
|
|
|
.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)
|
|
|
|
|
|