Files
2026-09-21 12:15:57 +03:00

144 lines
4.6 KiB
C#

using System;
using System.Collections.Generic;
using Reflex.Logging;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
#if UNITY_6000_2_OR_NEWER
using TreeViewItem = UnityEditor.IMGUI.Controls.TreeViewItem<int>;
using TreeViewState = UnityEditor.IMGUI.Controls.TreeViewState<int>;
using TreeView = UnityEditor.IMGUI.Controls.TreeView<int>;
#endif
namespace Reflex.Editor.DebuggingWindow
{
internal class TreeViewWithTreeModel<T> : TreeView where T : TreeElement
{
private TreeModel<T> _treeModel;
private readonly List<TreeViewItem> _rows = new List<TreeViewItem>(100);
public TreeViewWithTreeModel(TreeViewState state, MultiColumnHeader multiColumnHeader, TreeModel<T> model) : base(state, multiColumnHeader)
{
Init(model);
}
private void Init(TreeModel<T> model)
{
_treeModel = model;
}
public T Find(int id)
{
return _treeModel.Find(id);
}
protected override TreeViewItem BuildRoot()
{
int depthForHiddenRoot = -1;
return new TreeViewItem<T>(_treeModel.Root.Id, depthForHiddenRoot, _treeModel.Root.Name, _treeModel.Root);
}
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
{
if (_treeModel.Root == null)
{
ReflexLogger.Log("tree model root is null. did you call SetData()?", LogLevel.Error);
}
_rows.Clear();
if (!string.IsNullOrEmpty(searchString))
{
Search(_treeModel.Root, searchString, _rows);
}
else
{
if (_treeModel.Root.HasChildren)
{
AddChildrenRecursive(_treeModel.Root, 0, _rows);
}
}
// We still need to setup the child parent information for the rows since this
// information is used by the TreeView internal logic (navigation, dragging etc)
SetupParentsAndChildrenFromDepths(root, _rows);
return _rows;
}
private void AddChildrenRecursive(T parent, int depth, IList<TreeViewItem> newRows)
{
foreach (T child in parent.Children)
{
var item = new TreeViewItem<T>(child.Id, depth, child.Name, child);
newRows.Add(item);
if (child.HasChildren)
{
if (IsExpanded(child.Id))
{
AddChildrenRecursive(child, depth + 1, newRows);
}
else
{
item.children = CreateChildListForCollapsedParent();
}
}
}
}
private static void Search(T searchFromThis, string search, List<TreeViewItem> result)
{
if (string.IsNullOrEmpty(search))
{
throw new ArgumentException("Invalid search: cannot be null or empty", nameof(search));
}
if (searchFromThis == null || searchFromThis.Children == null)
{
return;
}
const int itemDepth = 0; // tree is flattened when searching
Stack<T> stack = new Stack<T>();
foreach (var element in searchFromThis.Children)
{
stack.Push((T) element);
}
while (stack.Count > 0)
{
T current = stack.Pop();
// Matches search?
if (current.Name.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
{
result.Add(new TreeViewItem<T>(current.Id, itemDepth, current.Name, current));
}
if (current.Children != null && current.Children.Count > 0)
{
foreach (var element in current.Children)
{
stack.Push((T) element);
}
}
}
SortSearchResult(result);
}
private static void SortSearchResult(List<TreeViewItem> rows)
{
rows.Sort((x, y) => EditorUtility.NaturalCompare(x.displayName, y.displayName));
}
protected override IList<int> GetAncestors(int id)
{
return _treeModel.GetAncestors(id);
}
protected override IList<int> GetDescendantsThatHaveChildren(int id)
{
return _treeModel.GetDescendantsThatHaveChildren(id);
}
}
}