2025-03-08 23:11:55 -05:00
|
|
|
|
using System;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
2025-10-05 11:45:32 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2025-06-30 16:37:34 +08:00
|
|
|
|
using DG.Tweening;
|
2026-05-02 22:10:19 +08:00
|
|
|
|
using Lean.Pool;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
using Ichni.RhythmGame;
|
2025-02-12 01:57:18 +08:00
|
|
|
|
using Unity.VisualScripting;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
using UnityEngine;
|
2025-02-10 15:56:10 +08:00
|
|
|
|
using UnityEngine.UI;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
|
|
|
|
|
|
namespace Ichni.Editor
|
|
|
|
|
|
{
|
|
|
|
|
|
public class Hierarchy : StaticWindow
|
|
|
|
|
|
{
|
|
|
|
|
|
public GameObject hierarchyTabPrefab;
|
|
|
|
|
|
public RectTransform tabContainer;
|
2025-03-08 23:11:55 -05:00
|
|
|
|
public Button addFolderButton;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
public List<HierarchyTab> tabList;
|
2025-06-30 16:37:34 +08:00
|
|
|
|
public Button expandButtom;
|
2025-07-17 16:44:38 +08:00
|
|
|
|
public bool NeedExecute = false;
|
2025-03-08 23:11:55 -05:00
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
tabList = new List<HierarchyTab>();
|
|
|
|
|
|
addFolderButton.onClick.AddListener(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ElementFolder.GenerateElement("New Folder", Guid.NewGuid(), new List<string>(), true, null);
|
|
|
|
|
|
});
|
2025-06-30 16:37:34 +08:00
|
|
|
|
expandButtom.onClick.AddListener(Expand);
|
|
|
|
|
|
rectTransform = this.GetComponent<RectTransform>();
|
2025-03-08 23:11:55 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
public HierarchyTab GenerateTab(GameElement targetElement, GameElement parentElement, bool animate = true)
|
2025-02-09 23:47:42 -05:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
if (targetElement.connectedTab != null) return targetElement.connectedTab;
|
|
|
|
|
|
|
|
|
|
|
|
HierarchyTab tab = LeanPool.Spawn(hierarchyTabPrefab, tabContainer).GetComponent<HierarchyTab>();
|
|
|
|
|
|
tab.SetTab(targetElement, parentElement, animate);
|
2025-02-21 19:36:03 +08:00
|
|
|
|
tabList.Add(tab);
|
2025-02-12 01:57:18 +08:00
|
|
|
|
return tab;
|
2025-02-09 23:47:42 -05:00
|
|
|
|
}
|
2025-10-05 11:45:32 +08:00
|
|
|
|
public async Task<HierarchyTab> GenerateTabAsync(GameElement targetElement, GameElement parentElement)
|
|
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
if (targetElement.connectedTab != null) return targetElement.connectedTab;
|
2025-10-05 11:45:32 +08:00
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
HierarchyTab tab = LeanPool.Spawn(hierarchyTabPrefab, tabContainer).GetComponent<HierarchyTab>();
|
2025-10-05 11:45:32 +08:00
|
|
|
|
tab.SetTab(targetElement, parentElement);
|
|
|
|
|
|
tabList.Add(tab);
|
2026-05-02 22:10:19 +08:00
|
|
|
|
await Task.Yield();
|
2025-10-05 11:45:32 +08:00
|
|
|
|
return tab;
|
|
|
|
|
|
}
|
2025-06-30 16:37:34 +08:00
|
|
|
|
public bool isExpand = false;
|
|
|
|
|
|
private RectTransform rectTransform;
|
|
|
|
|
|
public void Expand()
|
|
|
|
|
|
{
|
|
|
|
|
|
float originX = 225f;
|
|
|
|
|
|
isExpand = !isExpand;
|
|
|
|
|
|
// 用DOTween动画赋值
|
|
|
|
|
|
if (isExpand)
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.DOSizeDelta(
|
|
|
|
|
|
new Vector2(rectTransform.sizeDelta.x * 2, rectTransform.sizeDelta.y), 0.5f);
|
|
|
|
|
|
rectTransform.DOAnchorPos(
|
|
|
|
|
|
new Vector2(originX * 2, rectTransform.anchoredPosition.y), 0.5f);
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().DOAnchorPos(
|
|
|
|
|
|
new Vector2(
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().anchoredPosition.x * 3,
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().anchoredPosition.y
|
|
|
|
|
|
), 0.5f);
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().DOAnchorPos(
|
|
|
|
|
|
new Vector2(
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().anchoredPosition.x * 2,
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().anchoredPosition.y
|
|
|
|
|
|
), 0.5f);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
rectTransform.DOSizeDelta(
|
|
|
|
|
|
new Vector2(rectTransform.sizeDelta.x / 2, rectTransform.sizeDelta.y), 0.5f);
|
|
|
|
|
|
rectTransform.DOAnchorPos(
|
|
|
|
|
|
new Vector2(originX, rectTransform.anchoredPosition.y), 0.5f);
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().DOAnchorPos(
|
|
|
|
|
|
new Vector2(
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().anchoredPosition.x / 3,
|
|
|
|
|
|
enableButtonGroup.disableButton.GetComponent<RectTransform>().anchoredPosition.y
|
|
|
|
|
|
), 0.5f);
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().DOAnchorPos(
|
|
|
|
|
|
new Vector2(
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().anchoredPosition.x / 2,
|
|
|
|
|
|
expandButtom.GetComponent<RectTransform>().anchoredPosition.y
|
|
|
|
|
|
), 0.5f);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-03-16 00:42:27 +08:00
|
|
|
|
|
2025-05-11 14:12:47 +08:00
|
|
|
|
public ScrollRect scrollRect;
|
|
|
|
|
|
public Vector2 vector2;
|
2025-06-29 21:28:49 +08:00
|
|
|
|
public void FindTab(GameElement targetElement)
|
2025-02-21 19:36:03 +08:00
|
|
|
|
{
|
2025-07-09 17:02:47 +08:00
|
|
|
|
if (targetElement.connectedTab != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
targetElement.connectedTab.SelectGameElement();
|
|
|
|
|
|
getTabPos(targetElement.connectedTab);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-06-29 21:28:49 +08:00
|
|
|
|
targetElement.ScanAndAddEnableTypes();
|
|
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
|
|
|
|
|
|
StartCoroutine(TryGetTab(targetElement));
|
2025-06-29 21:28:49 +08:00
|
|
|
|
|
2025-06-14 20:47:45 +08:00
|
|
|
|
}
|
2026-05-02 22:10:19 +08:00
|
|
|
|
|
2025-06-14 20:47:45 +08:00
|
|
|
|
public IEnumerator TryGetTab(GameElement targetElement)
|
|
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
// 1. 向上收集所有没有Tab的祖先
|
2025-06-14 20:47:45 +08:00
|
|
|
|
Stack<GameElement> stack = new Stack<GameElement>();
|
|
|
|
|
|
GameElement current = targetElement;
|
|
|
|
|
|
while (current != null && current.connectedTab == null)
|
2025-02-21 19:36:03 +08:00
|
|
|
|
{
|
2025-06-14 20:47:45 +08:00
|
|
|
|
stack.Push(current);
|
|
|
|
|
|
current = current.parentElement;
|
2025-02-21 19:36:03 +08:00
|
|
|
|
}
|
2025-06-14 20:47:45 +08:00
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
// 2. 从顶层祖先开始,逐层同步展开
|
2025-06-14 20:47:45 +08:00
|
|
|
|
while (stack.Count > 0)
|
2025-02-21 19:36:03 +08:00
|
|
|
|
{
|
2025-06-14 20:47:45 +08:00
|
|
|
|
var elem = stack.Pop();
|
2026-05-02 22:10:19 +08:00
|
|
|
|
var parentTab = elem.parentElement?.connectedTab;
|
|
|
|
|
|
|
|
|
|
|
|
if (parentTab != null)
|
2025-05-24 14:34:12 +08:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
if (!parentTab.isExpanded)
|
2025-06-29 19:52:19 +08:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
StartCoroutine(parentTab.ExpandSyncBatched());
|
|
|
|
|
|
yield return new WaitUntil(() => parentTab.isExpandDone);
|
2025-06-29 19:52:19 +08:00
|
|
|
|
}
|
2026-05-02 22:10:19 +08:00
|
|
|
|
else if (elem.connectedTab == null)
|
2025-06-29 19:52:19 +08:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
// 父节点已展开但当前元素缺失Tab(刚添加或曾被折叠),直接补生成
|
|
|
|
|
|
GenerateTab(elem, elem.parentElement, false);
|
2025-06-29 19:52:19 +08:00
|
|
|
|
}
|
2025-05-24 14:34:12 +08:00
|
|
|
|
}
|
2026-05-02 22:10:19 +08:00
|
|
|
|
else
|
2025-05-24 14:34:12 +08:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
if (elem.connectedTab == null)
|
|
|
|
|
|
GenerateTab(elem, null, false);
|
2025-05-24 14:34:12 +08:00
|
|
|
|
}
|
2026-05-02 22:10:19 +08:00
|
|
|
|
yield return null;
|
2025-06-14 20:47:45 +08:00
|
|
|
|
}
|
2025-05-24 14:34:12 +08:00
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
// 3. 最终确保目标Tab存在
|
|
|
|
|
|
if (targetElement.connectedTab == null)
|
2025-06-14 20:47:45 +08:00
|
|
|
|
{
|
2026-05-02 22:10:19 +08:00
|
|
|
|
GenerateTab(targetElement, targetElement.parentElement, false);
|
2025-06-14 20:47:45 +08:00
|
|
|
|
yield return null;
|
2025-02-21 19:36:03 +08:00
|
|
|
|
}
|
2025-07-09 17:02:47 +08:00
|
|
|
|
|
2026-05-02 22:10:19 +08:00
|
|
|
|
yield return null;
|
|
|
|
|
|
getTabPos(targetElement.connectedTab);
|
2025-07-09 17:02:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
void getTabPos(HierarchyTab finalTab)
|
|
|
|
|
|
{
|
2025-06-29 19:52:19 +08:00
|
|
|
|
// 修正定位算法
|
2025-07-09 17:02:47 +08:00
|
|
|
|
|
2025-06-29 19:52:19 +08:00
|
|
|
|
RectTransform tabRect = finalTab.GetComponent<RectTransform>();
|
|
|
|
|
|
RectTransform containerRect = tabContainer;
|
|
|
|
|
|
RectTransform viewportRect = scrollRect.viewport != null ? scrollRect.viewport : scrollRect.GetComponent<RectTransform>();
|
|
|
|
|
|
|
|
|
|
|
|
// Tab相对于内容顶部的距离(正值为下,负值为上)
|
|
|
|
|
|
float tabTop = -tabRect.anchoredPosition.y;
|
|
|
|
|
|
float tabHeight = tabRect.rect.height;
|
|
|
|
|
|
float contentHeight = containerRect.rect.height;
|
|
|
|
|
|
float viewportHeight = viewportRect.rect.height;
|
|
|
|
|
|
|
|
|
|
|
|
// 目标:让Tab居中(或尽量居中)
|
|
|
|
|
|
float targetCenter = tabTop + tabHeight / 2f;
|
|
|
|
|
|
float viewportCenter = viewportHeight / 2f;
|
|
|
|
|
|
float scrollOffset = targetCenter - viewportCenter;
|
|
|
|
|
|
|
|
|
|
|
|
// normalizedPosition = 1 - (scrollOffset / (contentHeight - viewportHeight))
|
|
|
|
|
|
float denominator = Mathf.Max(1f, contentHeight - viewportHeight);
|
|
|
|
|
|
float normalized = 1f - Mathf.Clamp01(scrollOffset / denominator);
|
|
|
|
|
|
|
|
|
|
|
|
scrollRect.verticalNormalizedPosition = normalized;
|
2025-06-14 20:47:45 +08:00
|
|
|
|
finalTab.SelectGameElement();
|
2025-06-29 21:28:49 +08:00
|
|
|
|
|
2025-07-09 17:02:47 +08:00
|
|
|
|
}
|
2025-07-17 16:44:38 +08:00
|
|
|
|
public GameElement upLoadElement = null;
|
|
|
|
|
|
public IEnumerator TryGetElement()
|
|
|
|
|
|
{
|
|
|
|
|
|
NeedExecute = true;
|
|
|
|
|
|
// 等待直到upLoadElement被赋值
|
|
|
|
|
|
if (upLoadElement == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return new WaitUntil(() => upLoadElement != null);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NeedExecute = false;
|
|
|
|
|
|
upLoadElement = null;
|
|
|
|
|
|
}
|
2025-02-09 23:47:42 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|