diff --git a/App.xaml.cs b/App.xaml.cs index ebd66b4..46d1a1a 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -1,15 +1,28 @@ -namespace CommanderApp +using CommanderApp.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace CommanderApp; + +public partial class App : Application { - public partial class App : Application + public App() + { + InitializeComponent(); + + var services = new ServiceCollection(); + ConfigureServices(services); + var serviceProvider = services.BuildServiceProvider(); + + MainPage = serviceProvider.GetService(); + } + + private void ConfigureServices(ServiceCollection services) { - public App() - { - InitializeComponent(); - } - - protected override Window CreateWindow(IActivationState? activationState) - { - return new Window(new AppShell()); - } + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } } \ No newline at end of file diff --git a/FileOperations.cs b/FileOperations.cs new file mode 100644 index 0000000..5f0c295 --- /dev/null +++ b/FileOperations.cs @@ -0,0 +1,129 @@ +namespace CommanderApp.Services; + +public class FileOperations : IFileOperations +{ + public async Task CopyAsync(string sourcePath, string targetPath, bool overwrite = true) + { + try + { + await Task.Run(() => File.Copy(sourcePath, targetPath, overwrite)); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Copy failed: {ex.Message}"); + return false; + } + } + + public async Task MoveAsync(string sourcePath, string targetPath, bool overwrite = true) + { + try + { + await Task.Run(() => + { + if (File.Exists(targetPath) && overwrite) + File.Delete(targetPath); + File.Move(sourcePath, targetPath); + }); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Move failed: {ex.Message}"); + return false; + } + } + + public async Task DeleteAsync(string path) + { + try + { + await Task.Run(() => + { + if (Directory.Exists(path)) + Directory.Delete(path, true); + else if (File.Exists(path)) + File.Delete(path); + }); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Delete failed: {ex.Message}"); + return false; + } + } + + public async Task CreateDirectoryAsync(string path) + { + try + { + await Task.Run(() => Directory.CreateDirectory(path)); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"CreateDirectory failed: {ex.Message}"); + return false; + } + } + + public async Task OpenFileAsync(string filePath) + { + try + { + await Task.Run(() => + { +#if WINDOWS + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = filePath, + UseShellExecute = true + }); +#else + var process = new System.Diagnostics.Process(); + process.StartInfo.FileName = "open"; + process.StartInfo.Arguments = $"\"{filePath}\""; + process.StartInfo.UseShellExecute = false; + process.Start(); +#endif + }); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"OpenFile failed: {ex.Message}"); + return false; + } + } + + public async Task CopyDirectoryAsync(string sourceDir, string targetDir) + { + try + { + await Task.Run(() => + { + Directory.CreateDirectory(targetDir); + + foreach (var file in Directory.GetFiles(sourceDir)) + { + var destFile = Path.Combine(targetDir, Path.GetFileName(file)); + File.Copy(file, destFile, true); + } + + foreach (var directory in Directory.GetDirectories(sourceDir)) + { + var destDir = Path.Combine(targetDir, Path.GetFileName(directory)); + CopyDirectoryAsync(directory, destDir).Wait(); + } + }); + return true; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"CopyDirectory failed: {ex.Message}"); + return false; + } + } +} \ No newline at end of file diff --git a/FileSystemService.cs b/FileSystemService.cs index 80838bd..cb618d6 100644 --- a/FileSystemService.cs +++ b/FileSystemService.cs @@ -1,6 +1,5 @@ using System.IO; using System.Linq; -using Microsoft.Maui.Storage; namespace CommanderApp; @@ -12,14 +11,12 @@ public class FileSystemService : IFileSystemService return FileSystem.AppDataDirectory; #else var path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - // Убедимся, что путь корректен return Path.GetFullPath(path); #endif } public IEnumerable GetDirectoryContents(string path) { - // Нормализуем путь path = Path.GetFullPath(path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); if (!Directory.Exists(path)) @@ -65,7 +62,6 @@ public class FileSystemService : IFileSystemService catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Error reading directory {path}: {ex.Message}"); - // Возвращаем хотя бы ".." если возможно if (items.Count == 0 && Directory.GetParent(path) != null) { items.Add(new FileSystemItem diff --git a/IFileOperations.cs b/IFileOperations.cs new file mode 100644 index 0000000..1a62cd0 --- /dev/null +++ b/IFileOperations.cs @@ -0,0 +1,18 @@ +namespace CommanderApp.Services; + +public interface IFileOperations +{ + Task CopyAsync(string sourcePath, string targetPath, bool overwrite = true); + Task MoveAsync(string sourcePath, string targetPath, bool overwrite = true); + Task DeleteAsync(string path); + Task CreateDirectoryAsync(string path); + Task OpenFileAsync(string filePath); + Task CopyDirectoryAsync(string sourceDir, string targetDir); +} + +public class FileOperationResult +{ + public bool Success { get; set; } + public string Message { get; set; } = string.Empty; + public Exception Exception { get; set; } +} \ No newline at end of file diff --git a/IFileSystemService.cs b/IFileSystemService.cs index 1947800..cfc820c 100644 --- a/IFileSystemService.cs +++ b/IFileSystemService.cs @@ -1,8 +1,7 @@ -namespace CommanderApp +namespace CommanderApp; + +public interface IFileSystemService { - public interface IFileSystemService - { - IEnumerable GetDirectoryContents(string path); - string GetRootPath(); - } -} + string GetRootPath(); + IEnumerable GetDirectoryContents(string path); +} \ No newline at end of file diff --git a/IKeyboardService.cs b/IKeyboardService.cs new file mode 100644 index 0000000..0af10ea --- /dev/null +++ b/IKeyboardService.cs @@ -0,0 +1,13 @@ +namespace CommanderApp.Services; + +public interface IKeyboardService +{ + void SetupKeyboardHandling(ContentPage page); + event EventHandler KeyPressed; +} + +public class KeyPressedEventArgs : EventArgs +{ + public string Key { get; set; } = string.Empty; + public string Platform { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/IPanelManager.cs b/IPanelManager.cs new file mode 100644 index 0000000..1c3d52a --- /dev/null +++ b/IPanelManager.cs @@ -0,0 +1,35 @@ +using System.Collections.ObjectModel; + +namespace CommanderApp.Services; + +public interface IPanelManager +{ + event EventHandler StateChanged; + + bool IsLeftPanelActive { get; } + FileSystemItem SelectedItem { get; } + string ActivePanelPath { get; } + string LeftPanelPath { get; } + string RightPanelPath { get; } + int LeftSelectedIndex { get; } + int RightSelectedIndex { get; } + + void SwitchToLeftPanel(); + void SwitchToRightPanel(); + void MoveSelection(int direction); + void SetSelection(int index, bool isLeftPanel); + void UpdatePanelPaths(string leftPath, string rightPath); + void ClearSelection(); + + // Для привязки данных + ObservableCollection LeftItems { get; } + ObservableCollection RightItems { get; } +} + +public class PanelStateChangedEventArgs : EventArgs +{ + public bool IsLeftPanelActive { get; set; } + public int LeftSelectedIndex { get; set; } + public int RightSelectedIndex { get; set; } + public FileSystemItem SelectedItem { get; set; } +} \ No newline at end of file diff --git a/IPathHelper.cs b/IPathHelper.cs new file mode 100644 index 0000000..772a3f2 --- /dev/null +++ b/IPathHelper.cs @@ -0,0 +1,10 @@ +namespace CommanderApp.Services; + +public interface IPathHelper +{ + string GetUserHomePath(); + string GetRootPath(); + string CombinePaths(params string[] paths); + string GetFileName(string path); + string GetDirectoryName(string path); +} \ No newline at end of file diff --git a/KeyboardService.cs b/KeyboardService.cs new file mode 100644 index 0000000..864af70 --- /dev/null +++ b/KeyboardService.cs @@ -0,0 +1,129 @@ +#if WINDOWS +using Microsoft.UI.Xaml.Input; +using Windows.System; +#endif + +namespace CommanderApp.Services; + +public class KeyboardService : IKeyboardService +{ + public event EventHandler KeyPressed; + + public void SetupKeyboardHandling(ContentPage page) + { +#if WINDOWS + SetupWindowsKeyboardHandling(page); +#elif MACCATALYST + SetupMacKeyboardHandling(page); +#endif + } + +#if WINDOWS + private void SetupWindowsKeyboardHandling(ContentPage page) + { + try + { + if (page.Handler?.PlatformView is Microsoft.UI.Xaml.FrameworkElement frameworkElement) + { + frameworkElement.KeyDown += OnWindowsKeyDown; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Windows keyboard setup error: {ex.Message}"); + } + } + + private void OnWindowsKeyDown(object sender, KeyRoutedEventArgs e) + { + var key = e.Key switch + { + VirtualKey.W => "w", + VirtualKey.S => "s", + VirtualKey.A => "a", + VirtualKey.D => "d", + VirtualKey.Space => " ", + VirtualKey.Enter => "enter", + VirtualKey.F5 => "f5", + VirtualKey.F6 => "f6", + VirtualKey.F7 => "f7", + VirtualKey.F8 => "f8", + VirtualKey.F10 => "f10", + VirtualKey.H => "h", + _ => null + }; + + if (key != null) + { + KeyPressed?.Invoke(this, new KeyPressedEventArgs { Key = key, Platform = "Windows" }); + e.Handled = true; + } + } +#endif + +#if MACCATALYST + private void SetupMacKeyboardHandling(ContentPage page) + { + try + { + if (page.Handler?.PlatformView is UIKit.UIView uiView) + { + var keyHandler = new MacKeyHandler(OnMacKeyPressed); + keyHandler.Frame = uiView.Bounds; + keyHandler.AutoresizingMask = UIKit.UIViewAutoresizing.All; + uiView.AddSubview(keyHandler); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"Mac keyboard setup error: {ex.Message}"); + } + } + + private void OnMacKeyPressed(string key) + { + KeyPressed?.Invoke(this, new KeyPressedEventArgs { Key = key, Platform = "Mac" }); + } + + public class MacKeyHandler : UIKit.UIView + { + private readonly Action _keyHandler; + + public MacKeyHandler(Action keyHandler) + { + _keyHandler = keyHandler; + this.BecomeFirstResponder(); + } + + public override bool CanBecomeFirstResponder => true; + + public override UIKit.UIKeyCommand[] KeyCommands => new[] + { + UIKit.UIKeyCommand.Create((Foundation.NSString)"w", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleW:")), + UIKit.UIKeyCommand.Create((Foundation.NSString)"s", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleS:")), + UIKit.UIKeyCommand.Create((Foundation.NSString)"a", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleA:")), + UIKit.UIKeyCommand.Create((Foundation.NSString)"d", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleD:")), + UIKit.UIKeyCommand.Create((Foundation.NSString)" ", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleSpace:")), + UIKit.UIKeyCommand.Create((Foundation.NSString)"h", (UIKit.UIKeyModifierFlags)0, new ObjCRuntime.Selector("handleH:")) + }; + + [Foundation.Export("handleW:")] + void HandleW(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke("w"); + + [Foundation.Export("handleS:")] + void HandleS(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke("s"); + + [Foundation.Export("handleA:")] + void HandleA(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke("a"); + + [Foundation.Export("handleD:")] + void HandleD(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke("d"); + + [Foundation.Export("handleSpace:")] + void HandleSpace(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke(" "); + + [Foundation.Export("handleH:")] + void HandleH(UIKit.UIKeyCommand cmd) => _keyHandler?.Invoke("h"); + } +#endif +} \ No newline at end of file diff --git a/MainPage.xaml b/MainPage.xaml index 8a0fca6..dc0db9c 100644 --- a/MainPage.xaml +++ b/MainPage.xaml @@ -1,51 +1,61 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" + x:Class="CommanderApp.MainPage" + Title="MAUI Commander">