diff --git a/src/SharpIDE.Application/Features/FileWatching/IdeFileOperationsService.cs b/src/SharpIDE.Application/Features/FileWatching/IdeFileOperationsService.cs index 691b7ea..913675b 100644 --- a/src/SharpIDE.Application/Features/FileWatching/IdeFileOperationsService.cs +++ b/src/SharpIDE.Application/Features/FileWatching/IdeFileOperationsService.cs @@ -34,9 +34,11 @@ public class IdeFileOperationsService(SharpIdeSolutionModificationService sharpI await _sharpIdeSolutionModificationService.RemoveFile(file); } + // TODO: Pass class/interface/enum type to create different templates public async Task CreateCsFile(IFolderOrProject parentNode, string newFileName) { var newFilePath = Path.Combine(GetFileParentNodePath(parentNode), newFileName); + if (File.Exists(newFilePath)) throw new InvalidOperationException($"File {newFilePath} already exists."); var className = Path.GetFileNameWithoutExtension(newFileName); var @namespace = NewFileTemplates.ComputeNamespace(parentNode); var fileText = NewFileTemplates.CsharpClass(className, @namespace); @@ -45,6 +47,16 @@ public class IdeFileOperationsService(SharpIdeSolutionModificationService sharpI return sharpIdeFile; } + public async Task CopyFile(IFolderOrProject destinationParentNode, string sourceFilePath, string newFileName) + { + var newFilePath = Path.Combine(GetFileParentNodePath(destinationParentNode), newFileName); + if (File.Exists(newFilePath)) throw new InvalidOperationException($"File {newFilePath} already exists."); + var fileContents = await File.ReadAllTextAsync(sourceFilePath); + File.Copy(sourceFilePath, newFilePath); + var sharpIdeFile = await _sharpIdeSolutionModificationService.CreateFile(destinationParentNode, newFilePath, newFileName, fileContents); + return sharpIdeFile; + } + private static string GetFileParentNodePath(IFolderOrProject parentNode) => parentNode switch { SharpIdeFolder folder => folder.Path, diff --git a/src/SharpIDE.Godot/Features/Problems/RefCountedContainer.cs b/src/SharpIDE.Godot/Features/Problems/RefCountedContainer.cs index 7964002..84b0067 100644 --- a/src/SharpIDE.Godot/Features/Problems/RefCountedContainer.cs +++ b/src/SharpIDE.Godot/Features/Problems/RefCountedContainer.cs @@ -1,6 +1,4 @@ using Godot; -using Microsoft.CodeAnalysis; -using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Godot.Features.Problems; diff --git a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs index c22faf9..b20d61f 100644 --- a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs +++ b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs @@ -27,6 +27,8 @@ public partial class SolutionExplorerPanel : MarginContainer public SharpIdeSolutionModel SolutionModel { get; set; } = null!; private Tree _tree = null!; private TreeItem _rootItem = null!; + private enum ClipboardOperation { Cut, Copy } + private (SharpIdeFile, ClipboardOperation)? _itemOnClipboard; public override void _Ready() { _tree = GetNode("Tree"); @@ -34,6 +36,57 @@ public partial class SolutionExplorerPanel : MarginContainer GodotGlobalEvents.Instance.FileExternallySelected.Subscribe(OnFileExternallySelected); } + public override void _UnhandledKeyInput(InputEvent @event) + { + // Copy + if (@event is InputEventKey { Pressed: true, Keycode: Key.C, CtrlPressed: true }) + { + var selected = _tree.GetSelected(); + if (selected is null) return; + var genericMetadata = selected.GetMetadata(0).As(); + if (genericMetadata is RefCountedContainer fileContainer) + { + _itemOnClipboard = (fileContainer.Item, ClipboardOperation.Copy); + } + } + // Cut + else if (@event is InputEventKey { Pressed: true, Keycode: Key.X, CtrlPressed: true }) + { + var selected = _tree.GetSelected(); + if (selected is null) return; + var genericMetadata = selected.GetMetadata(0).As(); + if (genericMetadata is RefCountedContainer fileContainer) + { + _itemOnClipboard = (fileContainer.Item, ClipboardOperation.Cut); + } + } + // Paste + else if (@event is InputEventKey { Pressed: true, Keycode: Key.V, CtrlPressed: true }) + { + var selected = _tree.GetSelected(); + if (selected is null || _itemOnClipboard is null) return; + var genericMetadata = selected.GetMetadata(0).As(); + IFolderOrProject? folderOrProject = genericMetadata switch + { + RefCountedContainer f => f.Item, + RefCountedContainer p => p.Item, + _ => null + }; + if (folderOrProject is null) return; + + var (fileToPaste, operation) = _itemOnClipboard.Value; + _itemOnClipboard = null; + _ = Task.GodotRun(async () => + { + if (operation is ClipboardOperation.Copy) + { + await _ideFileOperationsService.CopyFile(folderOrProject, fileToPaste.Path, fileToPaste.Name); + } + }); + + } + } + private void TreeOnItemMouseSelected(Vector2 mousePosition, long mouseButtonIndex) { var selected = _tree.GetSelected();