|
|
|
|
@ -9,13 +9,18 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged
|
|
|
|
|
{
|
|
|
|
|
private readonly IFileSystemService _fileService;
|
|
|
|
|
|
|
|
|
|
// Коллекции больше не нужны для привязки, но оставим для логики
|
|
|
|
|
public ObservableCollection<FileSystemItem> LeftItems { get; } = new();
|
|
|
|
|
public ObservableCollection<FileSystemItem> RightItems { get; } = new();
|
|
|
|
|
|
|
|
|
|
private string _leftPath = string.Empty;
|
|
|
|
|
private string _rightPath = string.Empty;
|
|
|
|
|
|
|
|
|
|
// Для управления выделением и фокусом
|
|
|
|
|
private Button? _currentFocusedButton;
|
|
|
|
|
private bool _isLeftPanelActive = true;
|
|
|
|
|
private int _leftSelectedIndex = -1;
|
|
|
|
|
private int _rightSelectedIndex = -1;
|
|
|
|
|
|
|
|
|
|
public string LeftPath
|
|
|
|
|
{
|
|
|
|
|
get => _leftPath;
|
|
|
|
|
@ -48,111 +53,195 @@ public partial class MainPage : ContentPage, INotifyPropertyChanged
|
|
|
|
|
private FileSystemItem? _selectedLeftItem;
|
|
|
|
|
private FileSystemItem? _selectedRightItem;
|
|
|
|
|
|
|
|
|
|
// Для двойного нажатия
|
|
|
|
|
private FileSystemItem? _lastClickedItem;
|
|
|
|
|
private bool? _lastIsLeftPanel;
|
|
|
|
|
|
|
|
|
|
public MainPage(IFileSystemService fileService)
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
_fileService = fileService;
|
|
|
|
|
BindingContext = this;
|
|
|
|
|
|
|
|
|
|
// Инициализация шаблонов
|
|
|
|
|
InitializeCollectionViews();
|
|
|
|
|
|
|
|
|
|
var root = _fileService.GetRootPath();
|
|
|
|
|
LoadDirectory(root, true);
|
|
|
|
|
LoadDirectory(root, false);
|
|
|
|
|
|
|
|
|
|
// Устанавливаем фокус на страницу
|
|
|
|
|
this.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void LoadDirectory(string path, bool isLeft)
|
|
|
|
|
private void InitializeCollectionViews()
|
|
|
|
|
{
|
|
|
|
|
var items = _fileService.GetDirectoryContents(path).ToList();
|
|
|
|
|
var panel = isLeft ? LeftPanel : RightPanel;
|
|
|
|
|
var targetItems = isLeft ? LeftItems : RightItems;
|
|
|
|
|
var currentPathProperty = isLeft ? nameof(_currentLeftPath) : nameof(_currentRightPath);
|
|
|
|
|
LeftPanel.ItemTemplate = PanelCollectionView.CreateItemTemplate(isLeftPanel: true, page: this);
|
|
|
|
|
RightPanel.ItemTemplate = PanelCollectionView.CreateItemTemplate(isLeftPanel: true, page: this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Очистка панели
|
|
|
|
|
panel.Clear();
|
|
|
|
|
targetItems.Clear();
|
|
|
|
|
|
|
|
|
|
// Заполнение
|
|
|
|
|
foreach (var item in items)
|
|
|
|
|
protected override void OnHandlerChanged()
|
|
|
|
|
{
|
|
|
|
|
targetItems.Add(item);
|
|
|
|
|
base.OnHandlerChanged();
|
|
|
|
|
this.Focus();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var button = new Button
|
|
|
|
|
public void HandleItemClick(FileSystemItem item, bool isLeftPanel)
|
|
|
|
|
{
|
|
|
|
|
Text = item.DisplayText,
|
|
|
|
|
TextColor = Colors.Black,
|
|
|
|
|
Padding = new Thickness(10),
|
|
|
|
|
HeightRequest = 40,
|
|
|
|
|
BackgroundColor = Colors.Transparent,
|
|
|
|
|
BorderWidth = 0,
|
|
|
|
|
CommandParameter = item
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
button.Clicked += isLeft ? OnLeftItemActivated : OnRightItemActivated;
|
|
|
|
|
|
|
|
|
|
// Добавляем визуальные состояния
|
|
|
|
|
var normal = new VisualState { Name = "Normal" };
|
|
|
|
|
var focused = new VisualState { Name = "Focused" };
|
|
|
|
|
focused.Setters.Add(new Setter { Property = Button.BorderColorProperty, Value = Colors.Black });
|
|
|
|
|
focused.Setters.Add(new Setter { Property = Button.BorderWidthProperty, Value = 2 });
|
|
|
|
|
|
|
|
|
|
var pressed = new VisualState { Name = "Pressed" };
|
|
|
|
|
pressed.Setters.Add(new Setter { Property = Button.BackgroundColorProperty, Value = Color.FromArgb("#e0e0e0") });
|
|
|
|
|
|
|
|
|
|
var commonStates = new VisualStateGroup { Name = "CommonStates" };
|
|
|
|
|
commonStates.States.Add(normal);
|
|
|
|
|
commonStates.States.Add(focused);
|
|
|
|
|
commonStates.States.Add(pressed);
|
|
|
|
|
|
|
|
|
|
var groups = new VisualStateGroupList();
|
|
|
|
|
groups.Add(commonStates);
|
|
|
|
|
|
|
|
|
|
VisualStateManager.SetVisualStateGroups(button, groups);
|
|
|
|
|
|
|
|
|
|
panel.Add(button);
|
|
|
|
|
if (_lastIsLeftPanel == isLeftPanel && _lastClickedItem == item)
|
|
|
|
|
{
|
|
|
|
|
// Двойной клик
|
|
|
|
|
if (isLeftPanel)
|
|
|
|
|
OnLeftItemDoubleTapped(item);
|
|
|
|
|
else
|
|
|
|
|
OnRightItemDoubleTapped(item);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Одинарный клик - выделение
|
|
|
|
|
if (isLeftPanel)
|
|
|
|
|
{
|
|
|
|
|
_isLeftPanelActive = true;
|
|
|
|
|
_leftSelectedIndex = LeftItems.IndexOf(item);
|
|
|
|
|
_selectedLeftItem = item;
|
|
|
|
|
_selectedRightItem = null;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_isLeftPanelActive = false;
|
|
|
|
|
_rightSelectedIndex = RightItems.IndexOf(item);
|
|
|
|
|
_selectedRightItem = item;
|
|
|
|
|
_selectedLeftItem = null;
|
|
|
|
|
}
|
|
|
|
|
UpdateVisualSelection();
|
|
|
|
|
}
|
|
|
|
|
_lastIsLeftPanel = isLeftPanel;
|
|
|
|
|
_lastClickedItem = item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Обновление пути
|
|
|
|
|
private void LoadDirectory(string path, bool isLeft)
|
|
|
|
|
{
|
|
|
|
|
var items = _fileService.GetDirectoryContents(path).ToList();
|
|
|
|
|
|
|
|
|
|
if (isLeft)
|
|
|
|
|
{
|
|
|
|
|
LeftItems.Clear();
|
|
|
|
|
foreach (var item in items) LeftItems.Add(item);
|
|
|
|
|
_currentLeftPath = path;
|
|
|
|
|
LeftPath = path;
|
|
|
|
|
|
|
|
|
|
// Сбрасываем выделение при загрузке новой директории
|
|
|
|
|
_leftSelectedIndex = items.Count > 0 ? 0 : -1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RightItems.Clear();
|
|
|
|
|
foreach (var item in items) RightItems.Add(item);
|
|
|
|
|
_currentRightPath = path;
|
|
|
|
|
RightPath = path;
|
|
|
|
|
|
|
|
|
|
_rightSelectedIndex = items.Count > 0 ? 0 : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateVisualSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnLeftItemActivated(object sender, EventArgs e)
|
|
|
|
|
// Обработчики выделения (вызываются при изменении Selection в CollectionView)
|
|
|
|
|
private void OnLeftSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var button = (Button)sender;
|
|
|
|
|
var item = (FileSystemItem)button.CommandParameter;
|
|
|
|
|
// Обновляем индексы при программном изменении selection
|
|
|
|
|
if (e.CurrentSelection.FirstOrDefault() is FileSystemItem selectedItem)
|
|
|
|
|
{
|
|
|
|
|
_leftSelectedIndex = LeftItems.IndexOf(selectedItem);
|
|
|
|
|
_isLeftPanelActive = true;
|
|
|
|
|
UpdateVisualSelection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnRightSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.CurrentSelection.FirstOrDefault() is FileSystemItem selectedItem)
|
|
|
|
|
{
|
|
|
|
|
_rightSelectedIndex = RightItems.IndexOf(selectedItem);
|
|
|
|
|
_isLeftPanelActive = false;
|
|
|
|
|
UpdateVisualSelection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateVisualSelection()
|
|
|
|
|
{
|
|
|
|
|
//Сбрасываем предыдущий фокус
|
|
|
|
|
if (_currentFocusedButton != null)
|
|
|
|
|
{
|
|
|
|
|
VisualStateManager.GoToState(_currentFocusedButton, "Normal");
|
|
|
|
|
_currentFocusedButton = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_isLeftPanelActive && _leftSelectedIndex >= 0 && _leftSelectedIndex < LeftItems.Count)
|
|
|
|
|
{
|
|
|
|
|
var item = LeftItems[_leftSelectedIndex];
|
|
|
|
|
SetFocusToItem(LeftPanel, item);
|
|
|
|
|
_selectedLeftItem = item;
|
|
|
|
|
_selectedRightItem = null;
|
|
|
|
|
|
|
|
|
|
if (item.IsDirectory)
|
|
|
|
|
}
|
|
|
|
|
else if (!_isLeftPanelActive && _rightSelectedIndex >= 0 && _rightSelectedIndex < RightItems.Count)
|
|
|
|
|
{
|
|
|
|
|
LoadDirectory(item.FullName, true);
|
|
|
|
|
var item = RightItems[_rightSelectedIndex];
|
|
|
|
|
SetFocusToItem(RightPanel, item);
|
|
|
|
|
_selectedRightItem = item;
|
|
|
|
|
_selectedLeftItem = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnRightItemActivated(object sender, EventArgs e)
|
|
|
|
|
private void SetFocusToItem(CollectionView collectionView, FileSystemItem item)
|
|
|
|
|
{
|
|
|
|
|
// Используем Dispatcher чтобы дождаться рендеринга
|
|
|
|
|
Dispatcher.Dispatch(() =>
|
|
|
|
|
{
|
|
|
|
|
var button = (Button)sender;
|
|
|
|
|
var item = (FileSystemItem)button.CommandParameter;
|
|
|
|
|
var container = FindButtonContainer(collectionView, item);
|
|
|
|
|
if (container is Button button)
|
|
|
|
|
{
|
|
|
|
|
VisualStateManager.GoToState(button, "Focused");
|
|
|
|
|
_currentFocusedButton = button;
|
|
|
|
|
_lastClickedItem = item;
|
|
|
|
|
|
|
|
|
|
_selectedRightItem = item;
|
|
|
|
|
_selectedLeftItem = null;
|
|
|
|
|
// Прокручиваем к выбранному элементу
|
|
|
|
|
collectionView.ScrollTo(item, position: ScrollToPosition.MakeVisible, animate: false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Button? FindButtonContainer(CollectionView collectionView, FileSystemItem item)
|
|
|
|
|
{
|
|
|
|
|
// Ищем кнопку в логических дочерних элементах
|
|
|
|
|
foreach (var child in collectionView.LogicalChildren)
|
|
|
|
|
{
|
|
|
|
|
if (child is Button button && button.BindingContext == item)
|
|
|
|
|
{
|
|
|
|
|
return button;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnLeftItemDoubleTapped(FileSystemItem item)
|
|
|
|
|
{
|
|
|
|
|
if (item.IsDirectory)
|
|
|
|
|
{
|
|
|
|
|
LoadDirectory(item.FullName, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnRightItemDoubleTapped(FileSystemItem item)
|
|
|
|
|
{
|
|
|
|
|
if (item.IsDirectory)
|
|
|
|
|
{
|
|
|
|
|
LoadDirectory(item.FullName, false);
|
|
|
|
|
_selectedRightItem = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Остальные методы без изменений
|
|
|
|
|
private async void OnCopyClicked(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
await ProcessFileOperation(async (src, destDir) =>
|
|
|
|
|
|