@ -1,6 +1,6 @@
using System.Collections.ObjectModel ;
using System.ComponentModel ;
using System.ComponentModel ;
using System.Runtime.CompilerServices ;
using CommanderApp.Services ;
using Microsoft.Maui.Controls ;
namespace CommanderApp ;
@ -8,468 +8,583 @@ namespace CommanderApp;
public partial class MainPage : ContentPage , INotifyPropertyChanged
{
private readonly IFileSystemService _fileService ;
private readonly IPanelManager _panelManager ;
private readonly IFileOperations _fileOperations ;
private readonly IKeyboardService _keyboardService ;
private readonly IPathHelper _pathHelper ;
public ObservableCollection < FileSystemItem > LeftItems { get ; } = new ( ) ;
public ObservableCollection < FileSystemItem > RightItems { get ; } = new ( ) ;
// Для визуального выделения
private readonly Color ActiveIndicatorColor = Color . FromArgb ( "#007ACC" ) ;
private readonly Color InactiveIndicatorColor = Colors . Transparent ;
private readonly Color FocusedButtonColor = Color . FromArgb ( "#E3F2FD" ) ;
private readonly Color NormalButtonColor = Colors . Transparent ;
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 ;
// Для отслеживания двойного клика
private DateTime _lastClickTime = DateTime . MinValue ;
private object _lastClickedButton = null ;
public string LeftPath
{
get = > _ leftPath;
get = > _panelManager . LeftPanelPath ;
set
{
if ( _ leftPath ! = value )
if ( _ pane lManager. L eftPanel Path ! = value )
{
_ leftPath = value ;
Notify PropertyChanged( ) ;
_ panelManager. UpdatePanelPaths ( value , _panelManager . RightPanelPath ) ;
On PropertyChanged( ) ;
}
}
}
public string RightPath
{
get = > _ rightPath;
get = > _ panelManage r. R ightPanel Path;
set
{
if ( _ rightPath ! = value )
if ( _ panelManage r. R ightPanel Path ! = value )
{
_ rightPath = value ;
Notify PropertyChanged( ) ;
_ panelManager. UpdatePanelPaths ( _panelManager . LeftPanelPath , value ) ;
On PropertyChanged( ) ;
}
}
}
private string _currentLeftPath = string . Empty ;
private string _currentRightPath = string . Empty ;
private FileSystemItem ? _selectedLeftItem ;
private FileSystemItem ? _selectedRightItem ;
// Для двойного нажатия
private FileSystemItem ? _lastClickedItem ;
private int _lastLeftSelectedIndex = 0 ;
private int _lastRightSelectedIndex = 0 ;
private bool? _lastIsLeftPanel ;
public MainPage ( IFileSystemService fileService )
public MainPage ( IFileSystemService fileService ,
IPanelManager panelManager ,
IFileOperations fileOperations ,
IKeyboardService keyboardService ,
IPathHelper pathHelper )
{
InitializeComponent ( ) ;
_fileService = fileService ;
_panelManager = panelManager ;
_fileOperations = fileOperations ;
_keyboardService = keyboardService ;
_pathHelper = pathHelper ;
BindingContext = this ;
// Инициализация шаблонов
InitializeCollectionViews ( ) ;
System . Diagnostics . Debug . WriteLine ( "=== MainPage constructor ===" ) ;
// Подписываемся на события
_panelManager . StateChanged + = OnPanelStateChanged ;
_keyboardService . KeyPressed + = OnKeyPressed ;
// Загружаем начальные директории
var root = _fileService . GetRootPath ( ) ;
LoadDirectory ( root , true ) ;
LoadDirectory ( root , false ) ;
// Устанавливаем фокус на страницу
this . Focus ( ) ;
System . Diagnostics . Debug . WriteLine ( "=== MainPage constructor completed ===" ) ;
}
pr ivate void InitializeCollectionViews ( )
pr otected override void OnAppearing ( )
{
LeftPanel . ItemTemplate = PanelCollectionView . CreateItemTemplate ( isLeftPanel : true , page : this ) ;
RightPanel . ItemTemplate = PanelCollectionView . CreateItemTemplate ( isLeftPanel : false , page : this ) ;
base . OnAppearing ( ) ;
System . Diagnostics . Debug . WriteLine ( "=== OnAppearing ===" ) ;
Dispatcher . Dispatch ( async ( ) = >
{
await Task . Delay ( 300 ) ;
SetInitialFocus ( ) ;
} ) ;
}
protected override void OnHandlerChanged ( )
{
base . OnHandlerChanged ( ) ;
System . Diagnostics . Debug . WriteLine ( $"=== OnHandlerChanged ===" ) ;
_keyboardService . SetupKeyboardHandling ( this ) ;
}
//protected override void OnHandlerChanged()
//{
// base.OnHandlerChanged();
// this.Focus();
//}
public void HandleItemClick ( FileSystemItem item , bool isLeftPanel )
private void OnKeyPressed ( object sender , KeyPressedEventArgs e )
{
if ( _lastIsLeftPanel = = isLeftPanel & & _lastClickedItem = = item )
{
// Двойной клик
if ( isLeftPanel )
OnLeftItemDoubleTapped ( item ) ;
else
OnRightItemDoubleTapped ( item ) ;
}
else
System . Diagnostics . Debug . WriteLine ( $"=== KEY PRESSED: {e.Key} on {e.Platform} ===" ) ;
MainThread . BeginInvokeOnMainThread ( ( ) = >
{
// Одинарный клик - выделение
if ( isLeftPanel )
{
_isLeftPanelActive = true ;
_leftSelectedIndex = LeftItems . IndexOf ( item ) ;
_selectedLeftItem = item ;
_selectedRightItem = null ;
}
else
switch ( e . Key )
{
_isLeftPanelActive = false ;
_rightSelectedIndex = RightItems . IndexOf ( item ) ;
_selectedRightItem = item ;
_selectedLeftItem = null ;
case "w" : // W - вверх
_panelManager . MoveSelection ( - 1 ) ;
break ;
case "s" : // S - вниз
_panelManager . MoveSelection ( 1 ) ;
break ;
case "a" : // A - левая панель
_panelManager . SwitchToLeftPanel ( ) ;
break ;
case "d" : // D - правая панель
_panelManager . SwitchToRightPanel ( ) ;
break ;
case " " : // Space - вход/запуск
case "enter" : // Enter - вход/запуск
OpenSelectedItem ( ) ;
break ;
case "f5" : // F5 - Копирование
OnCopyClicked ( this , EventArgs . Empty ) ;
break ;
case "f6" : // F6 - Перемещение
OnMoveClicked ( this , EventArgs . Empty ) ;
break ;
case "f7" : // F7 - Создать папку
OnMkdirClicked ( this , EventArgs . Empty ) ;
break ;
case "f8" : // F8 - Удаление
OnDeleteClicked ( this , EventArgs . Empty ) ;
break ;
case "f10" : // F10 - Выход
OnExitClicked ( this , EventArgs . Empty ) ;
break ;
case "h" : // H - Home
OnHomeClicked ( this , EventArgs . Empty ) ;
break ;
}
UpdateVisualSelection ( ) ;
}
_lastIsLeftPanel = isLeftPanel ;
_lastClickedItem = item ;
} ) ;
}
private void LoadDirectory ( string path , bool isLeft )
private void OnPanelStateChanged ( object sender , PanelStateChangedEventArgs e )
{
var items = _fileService . GetDirectoryContents ( path ) . ToList ( ) ;
if ( isLeft )
UpdateVisualState ( ) ;
// Устанавливаем фокус на выбранный элемент
if ( e . IsLeftPanelActive & & e . LeftSelectedIndex > = 0 & & e . LeftSelectedIndex < LeftPanel . Children . Count )
{
LeftItems . Clear ( ) ;
foreach ( var item in items ) LeftItems . Add ( item ) ;
_currentLeftPath = path ;
LeftPath = path ;
// Сбрасываем выделение при загрузке новой директории
_leftSelectedIndex = items . Count > 0 ? 0 : - 1 ;
var button = LeftPanel . Children [ e . LeftSelectedIndex ] as Button ;
button ? . Focus ( ) ;
}
else
else if ( ! e . IsLeftPanelActive & & e . RightSelectedIndex > = 0 & & e . RightSelectedIndex < RightPanel . Children . Count )
{
RightItems . Clear ( ) ;
foreach ( var item in items ) RightItems . Add ( item ) ;
_currentRightPath = path ;
RightPath = path ;
_rightSelectedIndex = items . Count > 0 ? 0 : - 1 ;
var button = RightPanel . Children [ e . RightSelectedIndex ] as Button ;
button ? . Focus ( ) ;
}
UpdateVisualSelection ( ) ;
}
// Переключаем фокус с указанием направления
private void MoveFocus ( bool moveForward , bool isLeft )
{
var allFocusable = GetFocusableElements ( isLeft ) ;
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 )
private void SetInitialFocus ( )
{
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 ] ;
try
{
System . Diagnostics . Debug . WriteLine ( "=== Setting initial focus ===" ) ;
if ( LeftPanel . Children . Count > 0 )
{
var firstButton = LeftPanel . Children [ 0 ] as Button ;
if ( firstButton ! = null )
{
var focused = firstButton . Focus ( ) ;
System . Diagnostics . Debug . WriteLine ( $"First button focus result: {focused}" ) ;
if ( focused )
{
_panelManager . SetSelection ( 0 , true ) ;
}
}
}
}
catch ( Exception ex )
{
System . Diagnostics . Debug . WriteLine ( $"Error setting initial focus: {ex.Message}" ) ;
}
}
// Предыдущий элемент (для Up/Shift+Tab)
private View GetPreviousFocusable ( View current , List < View > allElements )
private void OpenSelectedItem ( )
{
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 ] ;
var item = _panelManager . SelectedItem ;
if ( item ! = null )
{
System . Diagnostics . Debug . WriteLine ( $"Opening: {item.Name} (IsDirectory: {item.IsDirectory})" ) ;
if ( item . IsDirectory )
{
LoadDirectory ( item . FullName , _panelManager . IsLeftPanelActive ) ;
}
else
{
_ = _fileOperations . OpenFileAsync ( item . FullName ) ;
}
}
else
{
System . Diagnostics . Debug . WriteLine ( "No item selected" ) ;
}
}
private List < View > GetFocusableElements ( bool isLeft )
private void UpdateVisualState ( )
{
List < View > ? elements = null ;
if ( isLeft )
// Визуальное выделение активной панели
if ( _panelManager. IsLeftPanelActive )
{
elements = LeftPanel . GetVisualTreeDescendants ( ) . OfType < View > ( )
. Where ( x = > x . IsEnabled & & x . IsVisible & & x is Button )
. OrderBy ( GetVisualTreeOrder ) // Сортируем по порядку в визуальном дереве
. ToList ( ) ;
LeftPanelIndicator . BackgroundColor = ActiveIndicatorColor ;
RightPanelIndicator . BackgroundColor = InactiveIndicatorColor ;
}
else
{
elements = RightPanel . GetVisualTreeDescendants ( ) . OfType < View > ( )
. Where ( x = > x . IsEnabled & & x . IsVisible & & x is Button )
. OrderBy ( GetVisualTreeOrder ) // Сортируем по порядку в визуальном дереве
. ToList ( ) ;
LeftPanelIndicator . BackgroundColor = InactiveIndicatorColor ;
RightPanelIndicator . BackgroundColor = ActiveIndicatorColor ;
}
//foreach (var el in elements)
//{
// System.Diagnostics.Debug.WriteLine($"!!! {el.ToString()}");
//}
return elements ;
UpdateButtonSelection ( ) ;
}
private int GetVisualTreeOrder ( View view )
private void UpdateButtonSelection ( )
{
// Простой способ - используем порядок в визуальном дереве
var parent = view . Parent as Layout ;
if ( parent ! = null )
// Сбрасываем выделение всех кнопок
foreach ( var child in LeftPanel . Children )
{
if ( child is Button button )
{
button . BackgroundColor = NormalButtonColor ;
}
}
foreach ( var child in RightPanel . Children )
{
if ( child is Button button )
{
button . BackgroundColor = NormalButtonColor ;
}
}
// Выделяем активную кнопку
if ( _panelManager . IsLeftPanelActive & & _panelManager . LeftSelectedIndex > = 0 & & _panelManager . LeftSelectedIndex < LeftPanel . Children . Count )
{
if ( LeftPanel . Children [ _panelManager . LeftSelectedIndex ] is Button leftButton )
{
leftButton . BackgroundColor = FocusedButtonColor ;
}
}
else if ( ! _panelManager . IsLeftPanelActive & & _panelManager . RightSelectedIndex > = 0 & & _panelManager . RightSelectedIndex < RightPanel . Children . Count )
{
var index = parent . Children . IndexOf ( view ) ;
return index > = 0 ? index : 0 ;
if ( RightPanel . Children [ _panelManager . RightSelectedIndex ] is Button rightButton )
{
rightButton . BackgroundColor = FocusedButtonColor ;
}
}
return 0 ;
}
// Обработчики выделения (вызываются при изменении Selection в CollectionView)
private void OnLeftSelectionChanged ( object sender , SelectionChangedEventArgs e )
private void LoadDirectory ( string path , bool isLeft )
{
if ( e . CurrentSelection . FirstOrDefault ( ) is FileSystemItem selectedItem )
try
{
_leftSelectedIndex = LeftItems . IndexOf ( selectedItem ) ;
_isLeftPanelActive = true ;
var items = _fileService . GetDirectoryContents ( path ) . ToList ( ) ;
var panel = isLeft ? LeftPanel : RightPanel ;
var collection = isLeft ? _panelManager . LeftItems : _panelManager . RightItems ;
// System.Diagnostics.Debug.WriteLine($"L !!! {_leftSelectedIndex} {_lastLeftSelectedIndex}");
if ( _lastLeftSelectedIndex < LeftItems . Count & & _lastLeftSelectedIndex > = 0 )
panel . Children . Clear ( ) ;
collection . Clear ( ) ;
foreach ( var item in items )
{
// Автоматически переключаем фокус при достижении границ
if ( _leftSelectedIndex > _lastLeftSelectedIndex )
{
// Достигли конца списка - переходим к следующему элементу
MoveFocus ( moveForward : true , isLeft : true ) ;
}
else if ( _leftSelectedIndex < _lastLeftSelectedIndex )
{
// Достигли начала списка - переходим к предыдущему элементу
MoveFocus ( moveForward : false , isLeft : true ) ;
}
collection . Add ( item ) ;
AddItemToPanel ( item , panel , isLeft ) ;
}
if ( isLeft )
{
LeftPath = path ;
_panelManager . SetSelection ( items . Count > 0 ? 0 : - 1 , true ) ;
}
else
{
RightPath = path ;
_panelManager . SetSelection ( items . Count > 0 ? 0 : - 1 , false ) ;
}
UpdateVisualSelection ( ) ;
System . Diagnostics . Debug . WriteLine ( $"Loaded {items.Count} items to {(isLeft ? " LEFT " : " RIGHT ")} panel" ) ;
UpdateVisualState ( ) ;
// Устанавливаем фокус на первый элемент
if ( items . Count > 0 )
{
Dispatcher . Dispatch ( ( ) = >
{
var firstButton = panel . Children [ 0 ] as Button ;
firstButton ? . Focus ( ) ;
} ) ;
}
}
catch ( Exception ex )
{
System . Diagnostics . Debug . WriteLine ( $"Error loading directory: {ex.Message}" ) ;
DisplayAlert ( "Error" , $"Cannot load directory: {ex.Message}" , "OK" ) ;
}
}
private void OnRightSelectionChanged ( object sender , SelectionChangedEventArgs e )
private void AddItemToPanel( FileSystemItem item , VerticalStackLayout panel , bool isLeft )
{
if ( e . CurrentSelection . FirstOrDefault ( ) is FileSystemItem selectedItem )
var button = new Button
{
_rightSelectedIndex = RightItems . IndexOf ( selectedItem ) ;
_isLeftPanelActive = false ;
// System.Diagnostics.Debug.WriteLine($"R !!! {_rightSelectedIndex} {_lastRightSelectedIndex}");
if ( _lastRightSelectedIndex < RightItems . Count & & _lastRightSelectedIndex > = 0 )
Text = item . DisplayText ,
Padding = new Thickness ( 15 , 8 ) ,
HeightRequest = 40 ,
BackgroundColor = NormalButtonColor ,
HorizontalOptions = LayoutOptions . FillAndExpand ,
TextColor = Colors . Black ,
FontSize = 14 ,
BorderColor = Colors . Transparent ,
CornerRadius = 0
} ;
// Обработчик клика - выделение при клике, открытие при двойном клике
button . Clicked + = ( s , e ) = > HandleItemClick ( button , item , isLeft ) ;
// Обработчик фокуса
button . Focused + = ( s , e ) = >
{
var index = panel . Children . IndexOf ( button ) ;
if ( index > = 0 )
{
// Автоматически переключаем фокус при достижении границ
if ( _rightSelectedIndex > _lastRightSelectedIndex )
{
// Достигли конца списка - переходим к следующему элементу
MoveFocus ( moveForward : true , isLeft : false ) ;
}
else if ( _rightSelectedIndex < _lastRightSelectedIndex )
{
// Достигли начала списка - переходим к предыдущему элементу
MoveFocus ( moveForward : false , isLeft : false ) ;
}
_panelManager . SetSelection ( index , isLeft ) ;
}
} ;
UpdateVisualSelection ( ) ;
}
panel . Children . Add ( button ) ;
}
private void UpdateVisualSelection( )
private void HandleItemClick ( Button button , FileSystemItem item , bool isLeft )
{
//Сбрасываем предыдущий фокус
if ( _currentFocusedButton ! = null )
{
VisualStateManager . GoToState ( _currentFocusedButton , "Normal" ) ;
_currentFocusedButton = null ;
}
var currentTime = DateTime . Now ;
var isDoubleClick = ( currentTime - _lastClickTime ) . TotalMilliseconds < 500
& & _lastClickedButton = = button ;
if ( _isLeftPanelActive & & _leftSelectedIndex > = 0 & & _leftSelectedIndex < LeftItems . Count )
_lastClickTime = currentTime ;
_lastClickedButton = button ;
System . Diagnostics . Debug . WriteLine ( $"=== CLICK: {item.Name} in {(isLeft ? " LEFT " : " RIGHT ")} panel, Double: {isDoubleClick} ===" ) ;
// ВСЕГДА выделяем элемент при клике
var panel = isLeft ? LeftPanel : RightPanel ;
var index = panel . Children . IndexOf ( button ) ;
if ( index > = 0 )
{
var item = LeftItems [ _leftSelectedIndex ] ;
SetFocusToItem ( LeftPanel , item ) ;
_selectedLeftItem = item ;
_selectedRightItem = null ;
_panelManager . SetSelection ( index , isLeft ) ;
// Устанавливаем фокус на кнопку
button. Focus ( ) ;
}
else if ( ! _isLeftPanelActive & & _rightSelectedIndex > = 0 & & _rightSelectedIndex < RightItems . Count )
// Если это двойной клик - открываем/запускаем
if ( isDoubleClick )
{
var item = RightItems [ _rightSelectedIndex ] ;
SetFocusToItem ( RightPanel , item ) ;
_selectedRightItem = item ;
_selectedLeftItem = null ;
if ( item . IsDirectory )
{
LoadDirectory ( item . FullName , isLeft ) ;
}
else
{
_ = _fileOperations . OpenFileAsync ( item . FullName ) ;
}
}
}
private void SetFocusToItem ( CollectionView collectionView , FileSystemItem item )
// Команды тулбара
private async void OnCopyClicked ( object sender , EventArgs e )
{
// Используем Dispatcher чтобы дождаться рендеринга
Dispatcher . Dispatch ( ( ) = >
System . Diagnostics . Debug . WriteLine ( "=== BUTTON: F5 Copy ===" ) ;
var sourceItem = _panelManager . SelectedItem ;
var targetPath = _panelManager . IsLeftPanelActive ? RightPath : LeftPath ;
if ( sourceItem ! = null )
{
var container = FindButtonContainer ( collectionView , item ) ;
if ( container is Button button )
try
{
VisualStateManager . GoToState ( button , "Focused" ) ;
_currentFocusedButton = button ;
_lastClickedItem = item ;
// Простой способ - используем текущие индексы
if ( _isLeftPanelActive )
var targetFullPath = _pathHelper . CombinePaths ( targetPath , sourceItem . Name ) ;
bool success ;
if ( sourceItem . IsDirectory )
{
_lastLeftSelectedIndex = _leftSelectedIndex ;
success = await _fileOperations . CopyDirectoryAsync ( sourceItem . FullName , targetFullPath ) ;
if ( success )
await DisplayAlert ( "Success" , $"Directory '{sourceItem.Name}' copied successfully" , "OK" ) ;
}
else
{
_lastRightSelectedIndex = _rightSelectedIndex ;
success = await _fileOperations . CopyAsync ( sourceItem . FullName , targetFullPath ) ;
if ( success )
await DisplayAlert ( "Success" , $"File '{sourceItem.Name}' copied successfully" , "OK" ) ;
}
if ( success )
{
// Обновляем целевую панель
LoadDirectory ( targetPath , ! _panelManager . IsLeftPanelActive ) ;
}
else
{
await DisplayAlert ( "Error" , "Copy operation failed" , "OK" ) ;
}
// Прокручиваем к выбранному элементу
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 )
catch ( Exception ex )
{
return button ;
await DisplayAlert ( "Error" , $"Copy failed: {ex.Message}" , "OK" ) ;
}
}
return null ;
}
public void OnLeftItemDoubleTapped ( FileSystemItem item )
{
if ( item . IsDirectory )
{
LoadDirectory ( item . FullName , true ) ;
}
}
public void OnRightItemDoubleTapped ( FileSystemItem item )
{
if ( item . IsDirectory )
else
{
LoadDirectory ( item . FullName , false ) ;
await DisplayAlert ( "Info" , "Please select an item to copy" , "OK" ) ;
}
}
// Остальные методы без изменений
private async void OnCopyClicked ( object sender , EventArgs e )
{
await ProcessFileOperation ( async ( src , destDir ) = >
{
if ( Directory . Exists ( src ) )
await CopyDirectory ( src , Path . Combine ( destDir , Path . GetFileName ( src ) ) ) ;
else
File . Copy ( src , Path . Combine ( destDir , Path . GetFileName ( src ) ) , overwrite : true ) ;
} , "Copy" ) ;
}
private async void OnMoveClicked ( object sender , EventArgs e )
{
await ProcessFileOperation ( ( src , destDir ) = >
System . Diagnostics . Debug . WriteLine ( "=== BUTTON: F6 Move ===" ) ;
var sourceItem = _panelManager . SelectedItem ;
var targetPath = _panelManager . IsLeftPanelActive ? RightPath : LeftPath ;
if ( sourceItem ! = null )
{
var dest = Path . Combine ( destDir , Path . GetFileName ( src ) ) ;
if ( Directory . Exists ( src ) )
Directory . Move ( src , dest ) ;
else
File . Move ( src , dest , overwrite : true ) ;
return Task . CompletedTask ;
} , "Move" ) ;
}
private async void OnDeleteClicked ( object sender , EventArgs e )
{
var item = _selectedLeftItem ? ? _selectedRightItem ;
if ( item = = null ) return ;
var confirm = await DisplayAlert ( "Delete" , $"Delete '{item.Name}'?" , "Yes" , "No" ) ;
if ( confirm )
try
{
var targetFullPath = _pathHelper . CombinePaths ( targetPath , sourceItem . Name ) ;
bool success ;
if ( sourceItem . IsDirectory )
{
success = await _fileOperations . CopyDirectoryAsync ( sourceItem . FullName , targetFullPath ) ;
if ( success )
success = await _fileOperations . DeleteAsync ( sourceItem . FullName ) ;
}
else
{
success = await _fileOperations . MoveAsync ( sourceItem . FullName , targetFullPath ) ;
}
if ( success )
{
await DisplayAlert ( "Success" , $"Item '{sourceItem.Name}' moved successfully" , "OK" ) ;
// Обновляем о б е панели
LoadDirectory ( _panelManager . IsLeftPanelActive ? LeftPath : RightPath , _panelManager . IsLeftPanelActive ) ;
LoadDirectory ( targetPath , ! _panelManager . IsLeftPanelActive ) ;
}
else
{
await DisplayAlert ( "Error" , "Move operation failed" , "OK" ) ;
}
}
catch ( Exception ex )
{
await DisplayAlert ( "Error" , $"Move failed: {ex.Message}" , "OK" ) ;
}
}
else
{
if ( item . IsDirectory )
Directory . Delete ( item . FullName , recursive : true ) ;
else
File . Delete ( item . FullName ) ;
LoadDirectory ( _currentLeftPath , true ) ;
LoadDirectory ( _currentRightPath , false ) ;
await DisplayAlert ( "Info" , "Please select an item to move" , "OK" ) ;
}
}
private async void OnMkdirClicked ( object sender , EventArgs e )
{
var result = await DisplayPromptAsync ( "New Folder" , "Folder name:" , "Create" , "Cancel" ) ;
if ( string . IsNullOrWhiteSpace ( result ) ) return ;
string targetPath = ( _selectedLeftItem ! = null | | LeftItems . Count > 0 ) ? _currentLeftPath : _currentRightPath ;
string newPath = Path . Combine ( targetPath , result . Trim ( ) ) ;
if ( ! Directory . Exists ( newPath ) )
{
Directory . CreateDirectory ( newPath ) ;
LoadDirectory ( _currentLeftPath , true ) ;
LoadDirectory ( _currentRightPath , false ) ;
}
else
System . Diagnostics . Debug . WriteLine ( "=== BUTTON: F7 Mkdir ===" ) ;
var currentPath = _panelManager . IsLeftPanelActive ? LeftPath : RightPath ;
// Запрашиваем имя новой папки
var folderName = await DisplayPromptAsync ( "Create Folder" , "Enter folder name:" , "Create" , "Cancel" , "New Folder" ) ;
if ( ! string . IsNullOrWhiteSpace ( folderName ) )
{
await DisplayAlert ( "Error" , "Folder already exists." , "OK" ) ;
try
{
var newFolderPath = _pathHelper . CombinePaths ( currentPath , folderName ) ;
var success = await _fileOperations . CreateDirectoryAsync ( newFolderPath ) ;
if ( success )
{
await DisplayAlert ( "Success" , $"Folder '{folderName}' created successfully" , "OK" ) ;
// Обновляем текущую панель
LoadDirectory ( currentPath , _panelManager . IsLeftPanelActive ) ;
}
else
{
await DisplayAlert ( "Error" , "Failed to create folder" , "OK" ) ;
}
}
catch ( Exception ex )
{
await DisplayAlert ( "Error" , $"Cannot create folder: {ex.Message}" , "OK" ) ;
}
}
}
private void OnExitClicked ( object sender , EventArgs e )
{
Application . Current ? . Quit ( ) ;
}
private async Task CopyDirectory ( string sourceDir , string targetDir )
private async void OnDeleteClicked ( object sender , EventArgs e )
{
Directory . CreateDirectory ( targetDir ) ;
foreach ( var file in Directory . GetFiles ( sourceDir ) )
System . Diagnostics . Debug . WriteLine ( "=== BUTTON: F8 Delete ===" ) ;
var item = _panelManager . SelectedItem ;
if ( item ! = null )
{
await Task . Run ( ( ) = > File . Copy ( file , Path . Combine ( targetDir , Path . GetFileName ( file ) ) , overwrite : true ) ) ;
// Подтверждение удаления
var result = await DisplayAlert ( "Confirm Delete" ,
$"Are you sure you want to delete '{item.Name}'?" ,
"Delete" , "Cancel" ) ;
if ( result )
{
try
{
var success = await _fileOperations . DeleteAsync ( item . FullName ) ;
if ( success )
{
await DisplayAlert ( "Success" , $"Item '{item.Name}' deleted successfully" , "OK" ) ;
// Обновляем текущую панель
LoadDirectory ( _panelManager . IsLeftPanelActive ? LeftPath : RightPath , _panelManager . IsLeftPanelActive ) ;
}
else
{
await DisplayAlert ( "Error" , "Delete operation failed" , "OK" ) ;
}
}
catch ( Exception ex )
{
await DisplayAlert ( "Error" , $"Delete failed: {ex.Message}" , "OK" ) ;
}
}
}
foreach ( var dir in Directory . GetDirectories ( sourceDir ) )
else
{
await CopyDirectory ( dir , Path . Combine ( targetDir , Path . GetFileName ( dir ) ) ) ;
await DisplayAlert( "Info" , "Please select an item to delete" , "OK" ) ;
}
}
private async Task ProcessFileOperation ( Func < string , string , Task > operation , string actionName )
private void OnHomeClicked ( object sender , EventArgs e)
{
var srcItem = _selectedLeftItem ? ? _selectedRightItem ;
if ( srcItem = = null )
{
await DisplayAlert ( "Error" , "Select a file or folder first." , "OK" ) ;
return ;
}
string destDir = ( srcItem = = _selectedLeftItem ) ? _currentRightPath : _currentLeftPath ;
System . Diagnostics . Debug . WriteLine ( "=== BUTTON: Home ===" ) ;
try
{
await operation ( srcItem . FullName , destDir ) ;
LoadDirectory ( _currentLeftPath , true ) ;
LoadDirectory ( _currentRightPath , false ) ;
// Получаем домашнюю директорию пользователя
var homePath = _pathHelper . GetUserHomePath ( ) ;
// Переходим в домашнюю директорию на активной панели
LoadDirectory ( homePath , _panelManager . IsLeftPanelActive ) ;
System . Diagnostics . Debug . WriteLine ( $"Navigated to home directory: {homePath}" ) ;
}
catch ( Exception ex )
{
await DisplayAlert ( "Error" , $"{actionName} failed: {ex.Message}" , "OK" ) ;
DisplayAlert ( "Error" , $" Cannot navigate to home directory : {ex.Message}", "OK" ) ;
}
}
p ublic new event PropertyChangedEventHandler ? PropertyChanged ;
p rivate void OnExitClicked ( object sender , EventArgs e ) = > Application . Current ? . Quit ( ) ;
protected virtual void NotifyPropertyChanged ( [ CallerMemberName ] string? propertyName = null )
// INotifyPropertyChanged
public event PropertyChangedEventHandler ? PropertyChanged ;
protected void SetProperty < T > ( ref T field , T value , [ CallerMemberName ] string? propertyName = null )
{
if ( EqualityComparer < T > . Default . Equals ( field , value ) ) return ;
field = value ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( propertyName ) ) ;
}
}