Files
FlaxEngine/Source/Editor/Content/Tree/ContentTreeNode.cs
ExMatics HydrogenC 47a25c7828 Add font fallback
Note: All the `First()` in the code are temperary workarounds to make it work and require refractoring
2023-11-28 07:17:46 +08:00

325 lines
9.9 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Drag;
using FlaxEditor.GUI.Tree;
using FlaxEditor.Utilities;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.Content
{
/// <summary>
/// Content folder tree node.
/// </summary>
/// <seealso cref="TreeNode" />
public class ContentTreeNode : TreeNode
{
private DragItems _dragOverItems;
private List<Rectangle> _highlights;
/// <summary>
/// The folder.
/// </summary>
protected ContentFolder _folder;
/// <summary>
/// Gets the content folder item.
/// </summary>
public ContentFolder Folder => _folder;
/// <summary>
/// Gets the type of the folder.
/// </summary>
public ContentFolderType FolderType => _folder.FolderType;
/// <summary>
/// Returns true if that folder can import/manage scripts.
/// </summary>
public bool CanHaveScripts => _folder.CanHaveScripts;
/// <summary>
/// Returns true if that folder can import/manage assets.
/// </summary>
/// <returns>True if can contain assets for project, otherwise false</returns>
public bool CanHaveAssets => _folder.CanHaveAssets;
/// <summary>
/// Gets the parent node.
/// </summary>
public ContentTreeNode ParentNode => Parent as ContentTreeNode;
/// <summary>
/// Gets the folder path.
/// </summary>
public string Path => _folder.Path;
/// <summary>
/// Gets the navigation button label.
/// </summary>
public virtual string NavButtonLabel => _folder.ShortName;
/// <summary>
/// Initializes a new instance of the <see cref="ContentTreeNode"/> class.
/// </summary>
/// <param name="parent">The parent node.</param>
/// <param name="path">The folder path.</param>
public ContentTreeNode(ContentTreeNode parent, string path)
: this(parent, parent?.FolderType ?? ContentFolderType.Other, path)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ContentTreeNode"/> class.
/// </summary>
/// <param name="parent">The parent node.</param>
/// <param name="type">The folder type.</param>
/// <param name="path">The folder path.</param>
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;
}
/// <summary>
/// Shows the rename popup for the item.
/// </summary>
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); };
}
/// <summary>
/// Updates the query search filter.
/// </summary>
/// <param name="filterText">The filter text.</param>
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<Rectangle>(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.First().GetCharPosition(text, ranges[i].StartIndex);
var end = font.First().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;
}
/// <inheritdoc />
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);
}
}
/// <inheritdoc />
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);
}
/// <inheritdoc />
protected override DragDropEffect OnDragEnterHeader(DragData data)
{
if (_dragOverItems == null)
_dragOverItems = new DragItems(ValidateDragItem);
_dragOverItems.OnDragEnter(data);
return GetDragEffect(data);
}
/// <inheritdoc />
protected override DragDropEffect OnDragMoveHeader(DragData data)
{
return GetDragEffect(data);
}
/// <inheritdoc />
protected override void OnDragLeaveHeader()
{
_dragOverItems.OnDragLeave();
base.OnDragLeaveHeader();
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
protected override void DoDragDrop()
{
DoDragDrop(DragItems.GetDragData(_folder));
}
/// <inheritdoc />
protected override void OnLongPress()
{
Select();
StartRenaming();
}
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (IsFocused)
{
switch (key)
{
case KeyboardKeys.F2:
StartRenaming();
return true;
case KeyboardKeys.Delete:
if (Folder.Exists)
Editor.Instance.Windows.ContentWin.Delete(Folder);
return true;
}
if (RootWindow.GetKey(KeyboardKeys.Control))
{
switch (key)
{
case KeyboardKeys.D:
if (Folder.Exists)
Editor.Instance.Windows.ContentWin.Duplicate(Folder);
return true;
}
}
}
return base.OnKeyDown(key);
}
}
}