From 22964255e4e011ae2937ef8cdfba533c07176ccc Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 17 Apr 2026 16:13:36 -0500 Subject: [PATCH 1/3] Add tree view only shortcuts. --- .../Content/Tree/ContentItemTreeNode.cs | 22 --- Source/Editor/Content/Tree/TreeViewPanel.cs | 183 ++++++++++++++++++ Source/Editor/Windows/ContentWindow.cs | 10 +- 3 files changed, 187 insertions(+), 28 deletions(-) create mode 100644 Source/Editor/Content/Tree/TreeViewPanel.cs diff --git a/Source/Editor/Content/Tree/ContentItemTreeNode.cs b/Source/Editor/Content/Tree/ContentItemTreeNode.cs index 09d1ec01b..6fd2f1ebc 100644 --- a/Source/Editor/Content/Tree/ContentItemTreeNode.cs +++ b/Source/Editor/Content/Tree/ContentItemTreeNode.cs @@ -128,28 +128,6 @@ public sealed class ContentItemTreeNode : TreeNode, IContentItemOwner return base.OnMouseDoubleClickHeader(ref location, button); } - /// - public override bool OnKeyDown(KeyboardKeys key) - { - if (IsFocused) - { - switch (key) - { - case KeyboardKeys.Return: - Editor.Instance.Windows.ContentWin.Open(Item); - return true; - case KeyboardKeys.F2: - Editor.Instance.Windows.ContentWin.Rename(Item); - return true; - case KeyboardKeys.Delete: - Editor.Instance.Windows.ContentWin.Delete(Item); - return true; - } - } - - return base.OnKeyDown(key); - } - /// protected override void DoDragDrop() { diff --git a/Source/Editor/Content/Tree/TreeViewPanel.cs b/Source/Editor/Content/Tree/TreeViewPanel.cs new file mode 100644 index 000000000..90666b3b6 --- /dev/null +++ b/Source/Editor/Content/Tree/TreeViewPanel.cs @@ -0,0 +1,183 @@ +using System.Collections.Generic; +using FlaxEditor.GUI.Tree; +using FlaxEditor.Options; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.Content; + +/// +/// The content tree view panel. +/// +public class TreeViewPanel : Panel +{ + /// + /// The content tree assigned to this panel. + /// + public Tree ContentTree; + + private InputActionsContainer _inputActions; + private bool _isCutting; + private List _cutItems = new List(); + + /// + /// Initializes a new instance of the class. + /// + public TreeViewPanel() + : base(ScrollBars.None) + { + // Setup input actions + _inputActions = new InputActionsContainer(new[] + { + new InputActionsContainer.Binding(options => options.Rename, Rename), + new InputActionsContainer.Binding(options => options.Delete, Delete), + new InputActionsContainer.Binding(options => options.Duplicate, Duplicate), + new InputActionsContainer.Binding(options => options.Copy, Copy), + new InputActionsContainer.Binding(options => options.Paste, Paste), + new InputActionsContainer.Binding(options => options.Cut, Cut), + }); + } + + private void Rename() + { + if (ContentTree == null || !Visible) + return; + + var selection = ContentTree.Selection; + if (selection.Count > 0) + { + var node = selection[0]; + if (node is ContentItemTreeNode contentNode) + { + Editor.Instance.Windows.ContentWin.Rename(contentNode.Item); + } + } + } + + private void Delete() + { + if (ContentTree == null || !Visible) + return; + + var selection = ContentTree.Selection; + if (selection.Count > 0) + { + foreach (var node in selection) + { + if (node is ContentItemTreeNode contentNode) + { + Editor.Instance.Windows.ContentWin.Delete(contentNode.Item); + } + } + } + } + + private void Duplicate() + { + if (ContentTree == null || !Visible) + return; + + var selection = ContentTree.Selection; + if (selection.Count > 0) + { + foreach (var node in selection) + { + if (node is ContentItemTreeNode contentNode) + { + Editor.Instance.Windows.ContentWin.Duplicate(contentNode.Item); + } + } + } + } + + private void Copy() + { + if (ContentTree == null || !Visible) + return; + + var selection = ContentTree.Selection; + if (selection.Count == 0) + return; + var filePaths = new List(); + foreach (var node in selection) + if (node is ContentItemTreeNode contentNode) + filePaths.Add(contentNode.Item.Path); + + Clipboard.Files = filePaths.ToArray(); + UpdateContentItemCut(false); + } + + private void Paste() + { + if (ContentTree == null || !Visible) + return; + + var files = Clipboard.Files; + if (files == null || files.Length == 0) + return; + + Editor.Instance.Windows.ContentWin.Paste(files, _isCutting); + UpdateContentItemCut(false); + } + + private void Cut() + { + if (ContentTree == null || !Visible) + return; + + Copy(); + UpdateContentItemCut(true); + } + + private void UpdateContentItemCut(bool cut) + { + _isCutting = cut; + + // Add selection to cut list + if (cut) + { + var selection = ContentTree.Selection; + foreach (var node in selection) + { + if (node is ContentItemTreeNode contentNode) + _cutItems.Add(contentNode.Item); + } + } + + // Update item with if it is being cut. + foreach (var item in _cutItems) + { + item.IsBeingCut = cut; + } + + // Clean up cut items + if (!cut) + _cutItems.Clear(); + } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (!Visible) + return false; + if (ContentTree == null) + return base.OnKeyDown(key); + + if (_inputActions.Process(Editor.Instance, this, key)) + return true; + + var selection = ContentTree.Selection; + if (selection.Count > 0) + { + if (key == KeyboardKeys.Return) + { + foreach (var node in selection) + { + if (node is ContentItemTreeNode contentNode) + Editor.Instance.Windows.ContentWin.Open(contentNode.Item); + } + } + } + return base.OnKeyDown(key); + } +} diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 2141b283e..5a73530bc 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -32,7 +32,7 @@ namespace FlaxEditor.Windows private string _workspaceRebuildLocation; private string _lastViewedFolderBeforeReload; private SplitPanel _split; - private Panel _treeOnlyPanel; + private TreeViewPanel _treeOnlyPanel; private ContainerControl _treePanelRoot; private ContainerControl _treeHeaderPanel; private Panel _contentItemsSearchPanel; @@ -186,7 +186,7 @@ namespace FlaxEditor.Windows }; // Tree-only panel (used when showing all content in the tree) - _treeOnlyPanel = new Panel(ScrollBars.None) + _treeOnlyPanel = new TreeViewPanel { AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0, 0, _toolStrip.Bottom, 0), @@ -237,6 +237,7 @@ namespace FlaxEditor.Windows Parent = _contentTreePanel, }; _tree.SelectedChanged += OnTreeSelectionChanged; + _treeOnlyPanel.ContentTree = _tree; // Content items searching query input box and filters selector _contentItemsSearchPanel = new Panel @@ -632,11 +633,7 @@ namespace FlaxEditor.Windows var area = node.TextRect; const float minRenameWidth = 220.0f; if (area.Width < minRenameWidth) - { - float expand = minRenameWidth - area.Width; - area.X -= expand * 0.5f; area.Width = minRenameWidth; - } area.Y -= 2; area.Height += 4.0f; popup = RenamePopup.Show(node, area, item.ShortName, true); @@ -889,6 +886,7 @@ namespace FlaxEditor.Windows // Refresh this folder now and try to find duplicated item Editor.ContentDatabase.RefreshFolder(item.ParentFolder, true); RefreshView(); + RefreshTreeItems(); var targetItem = item.ParentFolder.FindChild(targetPath); // Start renaming it From fe90f4954e2631dc072bf716fcbccbe4e9b0d589 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 17 Apr 2026 16:17:55 -0500 Subject: [PATCH 2/3] Use list overloads --- Source/Editor/Content/Tree/TreeViewPanel.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Content/Tree/TreeViewPanel.cs b/Source/Editor/Content/Tree/TreeViewPanel.cs index 90666b3b6..854bafc7f 100644 --- a/Source/Editor/Content/Tree/TreeViewPanel.cs +++ b/Source/Editor/Content/Tree/TreeViewPanel.cs @@ -62,13 +62,16 @@ public class TreeViewPanel : Panel var selection = ContentTree.Selection; if (selection.Count > 0) { + var items = new List(); foreach (var node in selection) { if (node is ContentItemTreeNode contentNode) { - Editor.Instance.Windows.ContentWin.Delete(contentNode.Item); + items.Add(contentNode.Item); } } + + Editor.Instance.Windows.ContentWin.Delete(items); } } @@ -80,13 +83,16 @@ public class TreeViewPanel : Panel var selection = ContentTree.Selection; if (selection.Count > 0) { + var items = new List(); foreach (var node in selection) { if (node is ContentItemTreeNode contentNode) { - Editor.Instance.Windows.ContentWin.Duplicate(contentNode.Item); + items.Add(contentNode.Item); } } + + Editor.Instance.Windows.ContentWin.Duplicate(items); } } From 6c8c1aed457b3807c62f9b037f9c6c7b9565a1d6 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 17 Apr 2026 16:31:49 -0500 Subject: [PATCH 3/3] Fix context menu. --- Source/Editor/Content/Tree/TreeViewPanel.cs | 30 ++++++++++--- .../Windows/ContentWindow.ContextMenu.cs | 43 +++++++++++++++---- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Content/Tree/TreeViewPanel.cs b/Source/Editor/Content/Tree/TreeViewPanel.cs index 854bafc7f..44bbfa878 100644 --- a/Source/Editor/Content/Tree/TreeViewPanel.cs +++ b/Source/Editor/Content/Tree/TreeViewPanel.cs @@ -38,7 +38,10 @@ public class TreeViewPanel : Panel }); } - private void Rename() + /// + /// Renames the selected item. + /// + public void Rename() { if (ContentTree == null || !Visible) return; @@ -54,7 +57,10 @@ public class TreeViewPanel : Panel } } - private void Delete() + /// + /// Deletes the selected items. + /// + public void Delete() { if (ContentTree == null || !Visible) return; @@ -75,7 +81,10 @@ public class TreeViewPanel : Panel } } - private void Duplicate() + /// + /// Duplicates the selected items. + /// + public void Duplicate() { if (ContentTree == null || !Visible) return; @@ -96,7 +105,10 @@ public class TreeViewPanel : Panel } } - private void Copy() + /// + /// Copies the items. + /// + public void Copy() { if (ContentTree == null || !Visible) return; @@ -113,7 +125,10 @@ public class TreeViewPanel : Panel UpdateContentItemCut(false); } - private void Paste() + /// + /// Pastes the items. + /// + public void Paste() { if (ContentTree == null || !Visible) return; @@ -126,7 +141,10 @@ public class TreeViewPanel : Panel UpdateContentItemCut(false); } - private void Cut() + /// + /// Cuts the items. + /// + public void Cut() { if (ContentTree == null || !Visible) return; diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index ad5a8caf2..f4985eb7a 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -67,12 +67,28 @@ namespace FlaxEditor.Windows b = cm.AddButton("Open", () => Open(item)); b.Enabled = proxy != null || isFolder; - if (_view.SelectedCount > 1) - b = cm.AddButton("Open (all selected)", () => + if (_showAllContentInTree) + { + var selection = _tree.Selection; + if (selection.Count > 0) { - foreach (var e in _view.Selection) - Open(e); - }); + b = cm.AddButton("Open (all selected)", () => + { + foreach (var e in _tree.Selection) + if (e is ContentItemTreeNode contentNode) + Open(contentNode.Item); + }); + } + } + else + { + if (_view.SelectedCount > 1) + b = cm.AddButton("Open (all selected)", () => + { + foreach (var e in _view.Selection) + Open(e); + }); + } cm.AddButton(Utilities.Constants.ShowInExplorer, () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path))); @@ -137,12 +153,21 @@ namespace FlaxEditor.Windows { cm.AddButton("Delete", () => Delete(item)); cm.AddSeparator(); - cm.AddButton("Duplicate", _view.Duplicate); - cm.AddButton("Cut", _view.Cut); - cm.AddButton("Copy", _view.Copy); + if (_showAllContentInTree) + { + cm.AddButton("Duplicate", _treeOnlyPanel.Duplicate); + cm.AddButton("Cut", _treeOnlyPanel.Cut); + cm.AddButton("Copy", _treeOnlyPanel.Copy); + } + else + { + cm.AddButton("Duplicate", _view.Duplicate); + cm.AddButton("Cut", _view.Cut); + cm.AddButton("Copy", _view.Copy); + } } - b = cm.AddButton("Paste", _view.Paste); + b = _showAllContentInTree ? cm.AddButton("Paste", _treeOnlyPanel.Paste) : cm.AddButton("Paste", _view.Paste); b.Enabled = _view.CanPaste(); if (isFolder && folder.Node is MainContentFolderTreeNode)