// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System.Collections.Generic; using FlaxEditor.GUI; using FlaxEditor.GUI.Drag; using FlaxEditor.GUI.Tree; using FlaxEditor.Utilities; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Content { /// /// Content folder tree node. /// /// public class ContentTreeNode : TreeNode { private DragItems _dragOverItems; private List _highlights; /// /// The folder. /// protected ContentFolder _folder; /// /// Whether this node can be deleted. /// public virtual bool CanDelete => true; /// /// Whether this node can be duplicated. /// public virtual bool CanDuplicate => true; /// /// Gets the content folder item. /// public ContentFolder Folder => _folder; /// /// Gets the type of the folder. /// public ContentFolderType FolderType => _folder.FolderType; /// /// Returns true if that folder can import/manage scripts. /// public bool CanHaveScripts => _folder.CanHaveScripts; /// /// Returns true if that folder can import/manage assets. /// /// True if can contain assets for project, otherwise false public bool CanHaveAssets => _folder.CanHaveAssets; /// /// Gets the parent node. /// public ContentTreeNode ParentNode => Parent as ContentTreeNode; /// /// Gets the folder path. /// public string Path => _folder.Path; /// /// Gets the navigation button label. /// public virtual string NavButtonLabel => _folder.ShortName; /// /// Initializes a new instance of the class. /// /// The parent node. /// The folder path. public ContentTreeNode(ContentTreeNode parent, string path) : this(parent, parent?.FolderType ?? ContentFolderType.Other, path) { } /// /// Initializes a new instance of the class. /// /// The parent node. /// The folder type. /// The folder path. protected ContentTreeNode(ContentTreeNode parent, ContentFolderType type, string path) : base(false, Editor.Instance.Icons.FolderClosed32, Editor.Instance.Icons.FolderOpen32) { _folder = new ContentFolder(type, path, this); Text = _folder.ShortName; if (parent != null) { Folder.ParentFolder = parent.Folder; Parent = parent; } IconColor = Style.Current.Foreground; } /// /// Shows the rename popup for the item. /// public void StartRenaming() { if (!_folder.CanRename) return; // Start renaming the folder Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(false); var dialog = RenamePopup.Show(this, TextRect, _folder.ShortName, false); dialog.Tag = _folder; dialog.Renamed += popup => { Editor.Instance.Windows.ContentWin.Rename((ContentFolder)popup.Tag, popup.Text); Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(true); }; dialog.Closed += popup => { Editor.Instance.Windows.ContentWin.ScrollingOnTreeView(true); }; } /// /// Updates the query search filter. /// /// The filter text. public void UpdateFilter(string filterText) { bool noFilter = string.IsNullOrWhiteSpace(filterText); // Update itself bool isThisVisible; if (noFilter) { // Clear filter _highlights?.Clear(); isThisVisible = true; } else { var text = Text; if (QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges)) { // Update highlights if (_highlights == null) _highlights = new List(ranges.Length); else _highlights.Clear(); var style = Style.Current; var font = style.FontSmall; var textRect = TextRect; for (int i = 0; i < ranges.Length; i++) { var start = font.GetCharPosition(text, ranges[i].StartIndex); var end = font.GetCharPosition(text, ranges[i].EndIndex); _highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } isThisVisible = true; } else { // Hide _highlights?.Clear(); isThisVisible = false; } } // Update children bool isAnyChildVisible = false; for (int i = 0; i < _children.Count; i++) { if (_children[i] is ContentTreeNode child) { child.UpdateFilter(filterText); isAnyChildVisible |= child.Visible; } } bool isExpanded = isAnyChildVisible; if (isExpanded) { Expand(true); } else { Collapse(true); } Visible = isThisVisible | isAnyChildVisible; } /// public override void Draw() { base.Draw(); // Draw all highlights if (_highlights != null) { var style = Style.Current; var color = style.ProgressNormal * 0.6f; for (int i = 0; i < _highlights.Count; i++) Render2D.FillRectangle(_highlights[i], color); } } /// public override void OnDestroy() { // Delete folder item _folder.Dispose(); base.OnDestroy(); } private DragDropEffect GetDragEffect(DragData data) { if (data is DragDataFiles) { if (_folder.CanHaveAssets) return DragDropEffect.Copy; } else { if (_dragOverItems.HasValidDrag) return DragDropEffect.Move; } return DragDropEffect.None; } private bool ValidateDragItem(ContentItem item) { // Reject itself and any parent return item != _folder && !item.Find(_folder); } /// protected override DragDropEffect OnDragEnterHeader(DragData data) { if (_dragOverItems == null) _dragOverItems = new DragItems(ValidateDragItem); _dragOverItems.OnDragEnter(data); return GetDragEffect(data); } /// protected override DragDropEffect OnDragMoveHeader(DragData data) { return GetDragEffect(data); } /// protected override void OnDragLeaveHeader() { _dragOverItems.OnDragLeave(); base.OnDragLeaveHeader(); } /// protected override DragDropEffect OnDragDropHeader(DragData data) { var result = DragDropEffect.None; // Check if drop element or files if (data is DragDataFiles files) { // Import files Editor.Instance.ContentImporting.Import(files.Files, _folder); result = DragDropEffect.Copy; Expand(); } else if (_dragOverItems.HasValidDrag) { // Move items Editor.Instance.ContentDatabase.Move(_dragOverItems.Objects, _folder); result = DragDropEffect.Move; Expand(); } _dragOverItems.OnDragDrop(); return result; } /// protected override void DoDragDrop() { DoDragDrop(DragItems.GetDragData(_folder)); } /// protected override void OnLongPress() { Select(); StartRenaming(); } /// public override bool OnKeyDown(KeyboardKeys key) { if (IsFocused) { switch (key) { case KeyboardKeys.F2: StartRenaming(); return true; case KeyboardKeys.Delete: if (Folder.Exists && CanDelete) Editor.Instance.Windows.ContentWin.Delete(Folder); return true; } if (RootWindow.GetKey(KeyboardKeys.Control)) { switch (key) { case KeyboardKeys.D: if (Folder.Exists && CanDuplicate) Editor.Instance.Windows.ContentWin.Duplicate(Folder); return true; } } } return base.OnKeyDown(key); } } }