Signed-off-by: TRADER_FOER <lhf190@outlook.com>

This commit is contained in:
2026-05-02 22:10:19 +08:00
parent 91bc3a269b
commit 648269d509
12 changed files with 236 additions and 165 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using DG.Tweening;
using Lean.Pool;
using Ichni.RhythmGame;
using Unity.VisualScripting;
using UnityEngine;
@@ -29,26 +30,23 @@ namespace Ichni.Editor
rectTransform = this.GetComponent<RectTransform>();
}
public HierarchyTab GenerateTab(GameElement targetElement, GameElement parentElement)
public HierarchyTab GenerateTab(GameElement targetElement, GameElement parentElement, bool animate = true)
{
HierarchyTab tab = Instantiate(hierarchyTabPrefab, tabContainer).GetComponent<HierarchyTab>();
tab.SetTab(targetElement, parentElement);
if (targetElement.connectedTab != null) return targetElement.connectedTab;
HierarchyTab tab = LeanPool.Spawn(hierarchyTabPrefab, tabContainer).GetComponent<HierarchyTab>();
tab.SetTab(targetElement, parentElement, animate);
tabList.Add(tab);
return tab;
}
public async Task<HierarchyTab> GenerateTabAsync(GameElement targetElement, GameElement parentElement)
{
var request = InstantiateAsync(hierarchyTabPrefab, tabContainer);
if (targetElement.connectedTab != null) return targetElement.connectedTab;
// 等待实例化过程完成
while (!request.isDone)
{
await Task.Yield(); // 异步地等待下一帧
}
HierarchyTab tab = request.Result[0].GetComponent<HierarchyTab>();
HierarchyTab tab = LeanPool.Spawn(hierarchyTabPrefab, tabContainer).GetComponent<HierarchyTab>();
tab.SetTab(targetElement, parentElement);
tabList.Add(tab);
// 返回实例化的游戏对象
await Task.Yield();
return tab;
}
public bool isExpand = false;
@@ -100,29 +98,20 @@ namespace Ichni.Editor
{
if (targetElement.connectedTab != null)
{
// 如果已经有Tab了直接选中
targetElement.connectedTab.SelectGameElement();
getTabPos(targetElement.connectedTab);
return;
}
targetElement.ScanAndAddEnableTypes();
if (!EditorManager.instance.ExpandWhileClick)
{
var tab = EditorManager.instance.uiManager.hierarchy.GenerateTab(targetElement, null);
tab.SelectGameElement();
Destroy(tab.gameObject);
EditorManager.instance.uiManager.hierarchy.tabList.Remove(tab);
}
else
{
StartCoroutine(TryGetTab(targetElement));
}
StartCoroutine(TryGetTab(targetElement));
}
public IEnumerator TryGetTab(GameElement targetElement)
{
// 1. 向上找到最近的有Tab的祖先
// 1. 向上收集所有没有Tab的祖先
Stack<GameElement> stack = new Stack<GameElement>();
GameElement current = targetElement;
while (current != null && current.connectedTab == null)
@@ -131,47 +120,42 @@ namespace Ichni.Editor
current = current.parentElement;
}
// 2. 如果有Tab的祖先存在依次展开回目标
HierarchyTab parentTab = current != null ? current.connectedTab : null;
// 2. 从顶层祖先开始,逐层同步展开
while (stack.Count > 0)
{
var elem = stack.Pop();
// 只展开父Tab不直接生成Tab
if (elem.parentElement != null && elem.parentElement.connectedTab != null)
{
if (!elem.parentElement.connectedTab.isExpanded)
{
elem.parentElement.connectedTab.ExpandOrFold(true);
}
else if (!elem.parentElement.connectedTab.isExpandDone)
{
elem.parentElement.connectedTab.ExpandOrFold();
elem.parentElement.connectedTab.ExpandOrFold(true);//合上再展开,这思路也是没谁了
}
else
{
//他就在展开了,等下就好了
}
yield return null;
}
// 等待当前elem的Tab生成
while (elem.connectedTab == null)
{
yield return null;
}
parentTab = elem.connectedTab;
}
// 3. 等待目标Tab实例化
while (targetElement.connectedTab is null)
{
var parentTab = elem.parentElement?.connectedTab;
if (parentTab != null)
{
if (!parentTab.isExpanded)
{
StartCoroutine(parentTab.ExpandSyncBatched());
yield return new WaitUntil(() => parentTab.isExpandDone);
}
else if (elem.connectedTab == null)
{
// 父节点已展开但当前元素缺失Tab刚添加或曾被折叠直接补生成
GenerateTab(elem, elem.parentElement, false);
}
}
else
{
if (elem.connectedTab == null)
GenerateTab(elem, null, false);
}
yield return null;
}
yield return null; // 等待一帧确保UI更新
// 3. 最终确保目标Tab存在
if (targetElement.connectedTab == null)
{
GenerateTab(targetElement, targetElement.parentElement, false);
yield return null;
}
yield return null;
getTabPos(targetElement.connectedTab);
}
void getTabPos(HierarchyTab finalTab)
{

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DG.Tweening;
using Lean.Pool;
using Ichni.RhythmGame;
using Michsky.MUIP;
using Sirenix.Utilities;
@@ -35,10 +36,20 @@ namespace Ichni.Editor
public DoubleCheckButton deleteButton;
public TMP_Text tabButtonText;
public void SetTab(GameElement targetElement, GameElement parentElement)
private List<GameObject> indentationLines = new List<GameObject>();
public void SetTab(GameElement targetElement, GameElement parentElement, bool animate = true)
{
transform.localScale = Vector3.one;
transform.DOScale(Vector3.one, 0.2f).SetEase(Ease.OutCirc).From(new Vector3(1, 0, 1));
if (animate)
{
transform.localScale = Vector3.one;
transform.DOScale(Vector3.one, 0.2f).SetEase(Ease.OutCirc).From(new Vector3(1, 0, 1));
}
else
{
transform.DOKill();
transform.localScale = Vector3.one;
}
connectedGameElement = targetElement;
tabButtonText.text = targetElement.elementName;
targetElement.connectedTab = this;
@@ -46,6 +57,14 @@ namespace Ichni.Editor
this.isSelected = false;
this.childTabList = new List<HierarchyTab>();
// 清除旧的缩进线
for (int i = indentationLines.Count - 1; i >= 0; i--)
{
if (indentationLines[i] != null)
Destroy(indentationLines[i]);
}
indentationLines.Clear();
if (parentElement == null)
{
this.tabLayer = 0;
@@ -62,7 +81,10 @@ namespace Ichni.Editor
if (!this.parentTab.isExpanded)
{
this.isExpanded = false;
var pt = this.parentTab;
SetExpansion(false);
pt.SetStatus();
return;
}
for (int i = 1; i <= this.tabLayer; i++)
@@ -70,14 +92,17 @@ namespace Ichni.Editor
float lineX = 30 * i - 15;
var d = Instantiate(indentationLinePrefab, tabRect);
d.GetComponent<RectTransform>().anchoredPosition = new Vector2(lineX, 0);
indentationLines.Add(d);
}
parentTab.SetStatus();
parentTab?.SetStatus();
}
float posX = (30 * tabLayer);
tabMainRect.anchoredPosition = new Vector2(posX, tabMainRect.anchoredPosition.y);
tabButton.onClick.RemoveAllListeners();
tabButton.onClick.AddListener(SelectGameElement);
expandButton.onClick.RemoveAllListeners();
expandButton.onClick.AddListener(ExpandOrFold);
deleteButton.onConfirm = () => EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElement(connectedGameElement);
@@ -87,6 +112,13 @@ namespace Ichni.Editor
private void OnDestroy()
{
transform.DOKill();
// 清除缩进线
for (int i = indentationLines.Count - 1; i >= 0; i--)
{
if (indentationLines[i] != null)
Destroy(indentationLines[i]);
}
indentationLines.Clear();
}
public void SetStatus()
@@ -293,9 +325,6 @@ namespace Ichni.Editor
connectedGameElement.ScanAndAddEnableTypes();
List<GameElement> FixedList = !forceAllExPand ? connectedGameElement.GetChildrenByTypes() : connectedGameElement.childElementList;
// float startTime = Time.realtimeSinceStartup;
// connectedGameElement.childElementList.Sort();//TODO: 后续可以让玩家手动快速排序
Debug.Log(FixedList.Count);
ExpandAsync(FixedList);
}
@@ -313,19 +342,57 @@ namespace Ichni.Editor
{
if (!expand && isExpanded)
{
for (int i = childTabList.Count; i > 0; i--)
for (int i = childTabList.Count - 1; i >= 0; i--)
{
childTabList[i - 1].SetExpansion(expand); //false
if (childTabList[i] != null)
childTabList[i].SetExpansion(expand);
}
}
if (!expand)
{
parentTab.childTabList.Remove(this);
Destroy(gameObject);
EditorManager.instance.uiManager.hierarchy.tabList.Remove(this);
CleanupAndDespawn();
}
}
private void CleanupAndDespawn()
{
if (connectedGameElement != null)
{
// 如果该元素还在选中列表中,先移除(须在 connectedTab=null 之前执行)
if (isSelected)
{
var sel = EditorManager.instance.operationManager.currentSelectedElements;
sel.Remove(connectedGameElement);
isSelected = false;
}
connectedGameElement.connectedTab = null;
}
parentTab?.childTabList.Remove(this);
EditorManager.instance.uiManager.hierarchy.tabList.Remove(this);
transform.DOKill();
tabButton.onClick.RemoveAllListeners();
expandButton.onClick.RemoveAllListeners();
for (int i = indentationLines.Count - 1; i >= 0; i--)
{
if (indentationLines[i] != null)
Destroy(indentationLines[i]);
}
indentationLines.Clear();
connectedGameElement = null;
parentTab = null;
childTabList?.Clear();
isExpanded = false;
isExpandDone = true;
tabLayer = 0;
LeanPool.Despawn(gameObject);
}
private void ExpandAnim()
{
expandButton.transform.DORotate(new Vector3(0, 0, !isExpanded ? 0f : 180f), 0.2f);
@@ -341,12 +408,36 @@ namespace Ichni.Editor
{
var childElement = FixedList[index];
EditorManager.instance.uiManager.hierarchy.GenerateTab(childElement, connectedGameElement);
Debug.Log($"生成子Tab{childElement.elementName},索引:{index},总数:{FixedList.Count}");
// 如果处理时间超过帧时间限制,直接继续执行而不等待
// 移除了原来的 yield return null 等待逻辑
}
}
public IEnumerator ExpandSyncBatched()
{
connectedGameElement.ScanAndAddEnableTypes();
isExpanded = true;
isExpandDone = false;
ExpandAnim();
var children = connectedGameElement.childElementList;
int batchCount = 0;
for (int i = 0; i < children.Count; i++)
{
if (!isExpanded) break;
if (children[i].connectedTab == null)
{
EditorManager.instance.uiManager.hierarchy.GenerateTab(children[i], connectedGameElement, false);
batchCount++;
if (batchCount >= 15)
{
batchCount = 0;
yield return null;
if (!isExpanded) break;
}
}
}
isExpandDone = true;
}
async void ExpandAsync(List<GameElement> FixedList)
{
isExpandDone = false;
@@ -355,7 +446,6 @@ namespace Ichni.Editor
var childElement = FixedList[index];
await EditorManager.instance.uiManager.hierarchy.GenerateTabAsync(childElement, connectedGameElement);
Debug.Log($"生成子Tab{childElement.elementName},索引:{index},总数:{FixedList.Count}");
if (!isExpanded) break;
}

View File

@@ -94,7 +94,7 @@ namespace Ichni.Editor
var item = LeanPool.Spawn(searchResultItemPrefab, searchResultsRoot);
var itemText = item.GetComponentInChildren<TMP_Text>();
if (itemText != null)
itemText.text = element.elementName + " " + element.GetType().ToString();
itemText.text = element.elementName + " | " + element.GetType().ToString();
var button = item.GetComponentInChildren<Button>();
if (button != null)