Files
Loong/Assets/Plugins/ThirdParty/FindReference2/Editor/Script/FR2_TreeUI2.cs
2025-07-01 14:54:02 +08:00

417 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace vietlabs.fr2
{
public class FR2_TreeUI2
{
internal Drawer drawer;
private Vector2 position;
private TreeItem rootItem;
internal Rect visibleRect;
public float itemPaddingRight = 0f;
public FR2_TreeUI2(Drawer drawer)
{
this.drawer = drawer;
}
public void Reset(params string[] root)
{
position = Vector2.zero;
rootItem = new TreeItem
{
tree = this,
id = "$root",
height = 0,
depth = -1,
_isOpen = true,
highlight = false,
childCount = root.Length
};
rootItem.RefreshChildren(root);
rootItem.DeepOpen();
}
public void Draw(Rect rect)
{
if (rect.width > 0)
{
visibleRect = rect;
}
var contentRect = new Rect(0f, 0f, 1f, rootItem.childrenHeight);
bool noScroll = contentRect.height < visibleRect.height;
if (noScroll)
{
position = Vector2.zero;
}
var minY = (int)position.y;
var maxY = (int)(position.y + visibleRect.height);
contentRect.x -= FR2_Setting.TreeIndent;
TreeItem.DrawCall = 0;
TreeItem.DrawRender = 0;
position = GUI.BeginScrollView(visibleRect, position, contentRect);
{
var r = new Rect(0, 0, rect.width - (noScroll ? 4f : 18f) - itemPaddingRight, 16f);
var index = 0;
rootItem.Draw(ref index, ref r, minY, maxY);
}
GUI.EndScrollView();
}
public void DrawLayout()
{
EventType evtType = Event.current.type;
Rect r = GUILayoutUtility.GetRect(1f, Screen.width, 16f, Screen.height);
if (evtType != EventType.Layout)
{
visibleRect = r;
}
Draw(visibleRect);
}
public bool NoScroll()
{
return rootItem.childrenHeight < visibleRect.height;
}
// ------------------------ DELEGATE --------------
public class Drawer
{
public virtual int GetHeight(string id)
{
return 16;
}
public virtual int GetChildCount(string id)
{
return 0;
}
public virtual string[] GetChildren(string id)
{
return null;
}
public virtual void Draw(Rect r, TreeItem item)
{
GUI.Label(r, item.id);
}
}
public class GroupDrawer : Drawer
{
public Action<Rect, string, int> drawGroup;
public Action<Rect, string> drawItem;
private Dictionary<string, List<string>> groupDict;
internal FR2_TreeUI2 tree;
public GroupDrawer(Action<Rect, string, int> drawGroup, Action<Rect, string> drawItem)
{
this.drawItem = drawItem;
this.drawGroup = drawGroup;
}
// ----------------- TREE WRAPPER ------------------
public bool TreeNoScroll()
{
return tree.NoScroll();
}
public bool hideGroupIfPossible;
public void Reset<T>(List<T> items, Func<T, string> idFunc, Func<T, string> groupFunc,
Action<List<string>> customGroupSort = null)
{
groupDict = new Dictionary<string, List<string>>();
for (var i = 0; i < items.Count; i++)
{
List<string> list;
string groupName = groupFunc(items[i]);
if (groupName == null) continue; // do not exclude groupName string.Empty
string itemId = idFunc(items[i]);
if (string.IsNullOrEmpty(itemId)) continue; // ignore items without id
if (!groupDict.TryGetValue(groupName, out list))
{
list = new List<string>();
groupDict.Add(groupName, list);
}
list.Add(itemId);
}
if (tree == null)
{
tree = new FR2_TreeUI2(this);
}
List<string> groups = groupDict.Keys.ToList();
if (hideGroupIfPossible && groups.Count == 1) //single group : Flat list
{
var v = groupDict[groups[0]];
tree.Reset(v.ToArray());
groupDict.Clear();
}
else
{ //multiple groups
if (customGroupSort != null)
{
customGroupSort(groups);
}
else
{
groups.Sort();
}
tree.Reset(groups.ToArray());
}
}
public void Draw(Rect r)
{
if (tree != null) tree.Draw(r);
}
public bool hasChildren
{
get
{
return tree != null && tree.rootItem.childCount > 0;
}
}
public bool hasValidTree
{
get { return groupDict != null && tree != null; }
}
public void DrawLayout()
{
if (tree != null) tree.DrawLayout();
}
// ----------------- DRAWER WRAPPER ------------------
public override int GetChildCount(string id)
{
if (string.IsNullOrEmpty(id)) return 0;
List<string> group;
if (groupDict.TryGetValue(id, out group))
{
return group.Count;
}
return 0;
}
public override string[] GetChildren(string id)
{
List<string> group;
if (groupDict.TryGetValue(id, out group))
{
return group.ToArray();
}
return null;
}
public override void Draw(Rect r, TreeItem item)
{
List<string> group;
if (groupDict.TryGetValue(item.id, out group))
{
drawGroup(r, item.id, item.childCount);
return;
}
drawItem(r, item.id);
}
}
// ------------------------ TreeItem2 --------------
public class TreeItem
{
public static int DrawCall;
public static int DrawRender;
internal bool _isOpen;
public int childCount;
public List<TreeItem> children;
public int childrenHeight;
public int depth; // item depth
public int height;
public bool highlight;
public string id; // item id
internal TreeItem parent;
//static Color COLOR = new Color(0f, 0f, 0f, 0.05f);
internal FR2_TreeUI2 tree;
public bool IsOpen
{
get { return _isOpen; }
set
{
if (_isOpen == value || childCount == 0)
{
return;
}
_isOpen = value;
if (_isOpen)
{
if (children == null)
{
RefreshChildren(tree.drawer.GetChildren(id));
}
//Update height for all parents
TreeItem p = parent;
while (p != null)
{
p.childrenHeight += childrenHeight;
p = p.parent;
}
}
else
{
//Update height for all parents
TreeItem p = parent;
while (p != null)
{
p.childrenHeight -= childrenHeight;
p = p.parent;
}
}
}
}
internal void DeepOpen()
{
IsOpen = true;
if (children == null)
{
return;
}
for (var i = 0; i < children.Count; i++)
{
children[i].DeepOpen();
}
}
internal void Draw(ref int index, ref Rect rect, int minY, int maxY)
{
DrawCall++;
// if (DrawCall < 10)
// {
// Debug.Log(index + ":" + rect + ":" + minY + ":" + maxY + ":" + height + ":" + childrenHeight);
// }
//var skipDraw = (rect.y >= maxY) || (height <=0);
float min = rect.y;
float max = rect.y + height;
bool interMin = min >= minY && min <= maxY;
bool interMax = max >= minY && max <= maxY;
if (height > 0 && (interMin || interMax))
{
DrawRender++;
rect.height = height;
if (index % 2 == 1 && FR2_Setting.AlternateRowColor)
{
Color o = GUI.color;
GUI.color = FR2_Setting.RowColor;
// GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
GUI.DrawTexture(new Rect(rect.x - FR2_Setting.TreeIndent, rect.y, rect.width, rect.height),
EditorGUIUtility.whiteTexture);
GUI.color = o;
}
float x = (depth + 1) * 16f;
tree.drawer.Draw(new Rect(x, rect.y, rect.width - x, rect.height), this);
if (childCount > 0)
{
IsOpen = GUI.Toggle(new Rect(rect.x + x - 16f, rect.y, 16f, 16f), IsOpen, string.Empty,
EditorStyles.foldout);
}
index++;
rect.y += height;
}
else
{
rect.y += height;
}
if (_isOpen && rect.y <= maxY) //draw children
{
for (var i = 0; i < children.Count; i++)
{
children[i].Draw(ref index, ref rect, minY, maxY);
if (rect.y > maxY)
{
break;
}
}
}
}
internal void RefreshChildren(string[] childrenIDs)
{
childCount = childrenIDs.Length;
childrenHeight = 0;
children = new List<TreeItem>();
for (var i = 0; i < childCount; i++)
{
string itemId = childrenIDs[i];
var item = new TreeItem
{
tree = tree,
parent = this,
id = itemId,
depth = depth + 1,
highlight = false,
height = tree.drawer.GetHeight(itemId),
childCount = tree.drawer.GetChildCount(itemId)
};
childrenHeight += item.height;
children.Add(item);
}
}
}
}
}