Files
EnglewoodLAB/Assets/Scripts/Studio/Window/UTKTreeListWindowController.cs

441 lines
16 KiB
C#

#nullable enable
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UVC.Core;
using UVC.Studio.Manager;
using UVC.UIToolkit;
namespace UVC.Studio.Window
{
/// <summary>
/// UTKTreeListWindow와 StageObjectManager, SelectionManager를 연결하는 컨트롤러입니다.
/// </summary>
public class UTKTreeListWindowController : IDisposable
{
/// <summary>연결된 UTKTreeListWindow</summary>
private readonly UTKTreeListWindow _window;
/// <summary>StageObject ID와 UTKTreeListItemData 간의 매핑</summary>
private readonly Dictionary<string, UTKTreeListItemData> _stageObjectToItem = new();
/// <summary>StageObjectManager 참조</summary>
private StageObjectManager? _stageObjectManager;
/// <summary>SelectionManager 참조</summary>
private SelectionManager? _selectionManager;
/// <summary>선택 이벤트 처리 중 무한 루프 방지 플래그</summary>
private bool _isProcessingSelection = false;
private bool _isInitialized = false;
private bool _disposed = false;
/// <summary>
/// UTKTreeListItemData를 초기화하고 이벤트를 연결합니다.
/// </summary>
/// <param name="window">연결할 UTKTreeListWindow</param>
public UTKTreeListWindowController(UTKTreeListWindow window)
{
_window = window;
_window.OnItemSelected = OnItemSelectedHandler;
_window.OnItemDeselected = OnItemDeselectedHandler;
_window.OnItemVisibilityChanged += OnItemVisibilityChangedHandler;
_window.OnItemDeleted = OnItemDeletedHandler;
_window.OnItemDoubleClicked = OnItemDoubleClickedHandler;
}
/// <summary>
/// 비동기 초기화: InjectorSceneContext 완료 후 StageObjectManager, SelectionManager를 가져옵니다.
/// </summary>
public async UniTaskVoid InitializeAsync()
{
await InjectorSceneContext.Instance.WaitForInitializationAsync();
// StageObjectManager 가져오기
_stageObjectManager = InjectorAppContext.Instance.Get<StageObjectManager>();
if (_stageObjectManager != null)
{
_stageObjectManager.OnObjectAdded += OnStageObjectAdded;
_stageObjectManager.OnObjectRemoved += OnStageObjectRemoved;
// 이미 등록된 객체들 반영
foreach (var kvp in _stageObjectManager.Objects)
{
AddItem(kvp.Value);
}
}
else
{
Debug.LogWarning("[UTKTreeListItemData] StageObjectManager not found.");
}
// SelectionManager 가져오기
_selectionManager = InjectorAppContext.Instance.Get<SelectionManager>();
if (_selectionManager != null)
{
_selectionManager.OnSelectionChanged += OnSelectionManagerSelectionChanged;
}
else
{
Debug.LogWarning("[UTKTreeListItemData] SelectionManager not found.");
}
_isInitialized = true;
}
/// <summary>
/// UTKTreeListWindow에서 아이템이 선택되었을 때 호출됩니다.
/// </summary>
private void OnItemSelectedHandler(List<UTKTreeListItemData> items)
{
if (_isProcessingSelection) return;
if (_selectionManager == null) return;
_isProcessingSelection = true;
try
{
foreach (var item in items)
{
Debug.Log($"[UTKTreeListItemData] Item selected: {item.name} (ExternalKey: {item.ExternalKey})");
// ExternalKey가 있으면 루트 StageObject 선택
if (!string.IsNullOrEmpty(item.ExternalKey))
{
_selectionManager.SelectById(item.ExternalKey, addToSelection: false, clearHierarchySelection: false);
}
// Tag에 Transform이 있으면 자식 항목 - PropertyWindow에 표시
else if (item.Tag is Transform childTransform)
{
_selectionManager.DisplayChildTransformProperties(childTransform, item.name);
}
}
}
finally
{
_isProcessingSelection = false;
}
}
/// <summary>
/// UTKTreeListWindow에서 아이템 선택이 해제되었을 때 호출됩니다.
/// </summary>
private void OnItemDeselectedHandler(List<UTKTreeListItemData> items)
{
if (_isProcessingSelection) return;
if (_selectionManager == null) return;
_isProcessingSelection = true;
try
{
foreach (var item in items)
{
Debug.Log($"[UTKTreeListItemData] Item deselected: {item.name} (ExternalKey: {item.ExternalKey})");
// ExternalKey가 있으면 루트 StageObject 선택 해제
if (!string.IsNullOrEmpty(item.ExternalKey))
{
_selectionManager.DeselectById(item.ExternalKey);
}
// Tag에 Transform이 있으면 자식 Transform 표시 초기화
else if (item.Tag is Transform)
{
_selectionManager.ClearChildTransformDisplay();
}
}
}
finally
{
_isProcessingSelection = false;
}
}
/// <summary>
/// 아이템 가시성이 변경되었을 때 호출됩니다.
/// </summary>
private void OnItemVisibilityChangedHandler(UTKTreeListItemData item, bool isVisible)
{
Debug.Log($"[UTKTreeListItemData] Item visibility changed: {item.name}, IsVisible: {isVisible}");
// ExternalKey가 있으면 루트 StageObject 가시성 설정
if (!string.IsNullOrEmpty(item.ExternalKey))
{
_selectionManager?.SetVisibilityById(item.ExternalKey, isVisible);
}
// Tag에 Transform이 있으면 자식 항목 - 직접 GameObject 가시성 설정
else if (item.Tag is Transform childTransform)
{
if (childTransform != null && childTransform.gameObject != null)
{
childTransform.gameObject.SetActive(isVisible);
Debug.Log($"[UTKTreeListItemData] Set child GameObject active: {childTransform.name} = {isVisible}");
}
}
}
/// <summary>
/// 아이템이 삭제되었을 때 호출됩니다 (Delete 키).
/// </summary>
private void OnItemDeletedHandler(UTKTreeListItemData item)
{
Debug.Log($"[UTKTreeListItemData] Item deleted: {item.name} (ExternalKey: {item.ExternalKey})");
// ExternalKey가 있으면 루트 StageObject 삭제
if (!string.IsNullOrEmpty(item.ExternalKey))
{
_stageObjectToItem.Remove(item.ExternalKey);
_selectionManager?.DeselectById(item.ExternalKey);
_stageObjectManager?.Unregister(item.ExternalKey);
}
// Tag에 Transform이 있으면 자식 항목 - GameObject 삭제
else if (item.Tag is Transform childTransform)
{
_selectionManager?.ClearChildTransformDisplay();
if (childTransform != null && childTransform.gameObject != null)
{
Debug.Log($"[UTKTreeListItemData] Destroying child GameObject: {childTransform.name}");
UnityEngine.Object.Destroy(childTransform.gameObject);
}
}
}
/// <summary>
/// 아이템이 더블클릭되었을 때 호출됩니다.
/// </summary>
private void OnItemDoubleClickedHandler(UTKTreeListItemData item)
{
Debug.Log($"[UTKTreeListItemData] Item double clicked: {item.name} (ExternalKey: {item.ExternalKey})");
if (_stageObjectManager == null) return;
// ExternalKey가 있으면 ID로 포커스
if (!string.IsNullOrEmpty(item.ExternalKey))
{
_stageObjectManager.Focus(item.ExternalKey);
}
// Tag에 Transform이 있으면 자식 항목 - Transform의 GameObject로 포커스
else if (item.Tag is Transform childTransform && childTransform.gameObject != null)
{
_stageObjectManager.Focus(childTransform.gameObject);
}
}
/// <summary>
/// SelectionManager에서 선택이 변경되었을 때 호출됩니다. (화면에서 객체 클릭 시)
/// Window의 선택 상태를 동기화합니다.
/// </summary>
private void OnSelectionManagerSelectionChanged(StageObjectManager.StageObject stageObject, bool isSelected)
{
if (_isProcessingSelection) return;
if (string.IsNullOrEmpty(stageObject.Id)) return;
if (!_stageObjectToItem.TryGetValue(stageObject.Id, out var item)) return;
_isProcessingSelection = true;
try
{
if (isSelected)
{
_window.SelectItem(item.name);
Debug.Log($"[UTKTreeListItemData] Synced selection to window: {item.name}");
}
else
{
_window.DeselectItem(item.name);
Debug.Log($"[UTKTreeListItemData] Synced deselection to window: {item.name}");
}
}
finally
{
_isProcessingSelection = false;
}
}
/// <summary>
/// StageObject가 추가될 때 호출됩니다.
/// </summary>
private void OnStageObjectAdded(StageObjectManager.StageObject stageObject)
{
AddItem(stageObject);
}
/// <summary>
/// StageObject가 제거될 때 호출됩니다.
/// </summary>
private void OnStageObjectRemoved(StageObjectManager.StageObject stageObject)
{
RemoveItem(stageObject);
}
/// <summary>
/// StageObject로부터 UTKTreeListItemData를 생성하여 Window에 추가합니다.
/// </summary>
private void AddItem(StageObjectManager.StageObject stageObject)
{
if (_stageObjectToItem.ContainsKey(stageObject.Id))
{
Debug.LogWarning($"[UTKTreeListItemData] Item already exists for StageObject: {stageObject.Id}");
return;
}
var item = CreateItemRecursive(
stageObject.GameObject != null ? stageObject.GameObject.transform : null,
stageObject.GameObject != null ? stageObject.GameObject.name : stageObject.Equipment.id,
stageObject.Id
);
_stageObjectToItem[stageObject.Id] = item;
_window.AddItem(item);
Debug.Log($"[UTKTreeListItemData] Added item: {item.name}");
}
/// <summary>
/// Transform의 자식들을 재귀적으로 탐색하여 UTKTreeListItemData를 생성
/// </summary>
/// <param name="transform">탐색할 Transform (null이면 이름만 사용)</param>
/// <param name="name">표시 이름</param>
/// <param name="externalKey">외부 키 (루트 노드에만 설정)</param>
/// <returns>생성된 UTKTreeListItemData</returns>
private UTKTreeListItemData CreateItemRecursive(Transform? transform, string name, string? externalKey = null)
{
var treeItem = new UTKTreeListItemData(){name = name};
if (!string.IsNullOrEmpty(externalKey))
{
treeItem.ExternalKey = externalKey;
}
// Transform을 Tag에 저장 (자식 Transform 접근용)
treeItem.Tag = transform;
// Transform이 있으면 자식들을 재귀적으로 추가
if (transform != null)
{
foreach (Transform child in transform)
{
var childItem = CreateItemRecursive(child, child.name);
treeItem.Add(childItem);
}
}
return treeItem;
}
/// <summary>
/// UTKTreeListItemData를 Window에서 제거합니다.
/// </summary>
private void RemoveItem(StageObjectManager.StageObject stageObject)
{
if (_stageObjectToItem.TryGetValue(stageObject.Id, out var item))
{
_window.DeleteItem(item);
_stageObjectToItem.Remove(stageObject.Id);
Debug.Log($"[UTKTreeListItemData] Removed item: {item.name}");
}
}
/// <summary>
/// Window의 선택을 해제합니다.
/// </summary>
public void ClearSelection()
{
_window.ClearSelection();
}
/// <summary>
/// StageObject의 표시 이름을 변경합니다.
/// </summary>
/// <param name="stageObjectId">변경할 StageObject의 ID</param>
/// <param name="newName">새 이름</param>
public void UpdateItemName(string stageObjectId, string newName)
{
if (!_stageObjectToItem.TryGetValue(stageObjectId, out var item)) return;
_window.SetItemName(item, newName);
Debug.Log($"[UTKTreeListItemData] Updated item name: {stageObjectId} -> {newName}");
}
/// <summary>
/// SelectionManager의 현재 선택 상태를 Window에 동기화합니다.
/// </summary>
public async void SyncSelectionFromSelectionManager()
{
if (!_isInitialized)
{
await UniTask.WaitUntil(() => _isInitialized).TimeoutWithoutException(new TimeSpan(0, 0, 1));
}
if (_selectionManager == null) return;
var selectedObjects = _selectionManager.SelectedObjects;
if (selectedObjects.Count == 0) return;
_isProcessingSelection = true;
try
{
foreach (var stageObject in selectedObjects)
{
if (string.IsNullOrEmpty(stageObject.Id)) continue;
if (_stageObjectToItem.TryGetValue(stageObject.Id, out var item))
{
_window.SelectItem(item.name);
Debug.Log($"[UTKTreeListItemData] Synced existing selection: {item.name}");
}
}
}
finally
{
_isProcessingSelection = false;
}
}
/// <summary>
/// StageObject ID로 UTKTreeListItemData를 찾습니다.
/// </summary>
public UTKTreeListItemData? GetItem(string stageObjectId)
{
return _stageObjectToItem.TryGetValue(stageObjectId, out var item) ? item : null;
}
/// <summary>
/// 리소스를 해제하고 이벤트 구독을 정리합니다.
/// </summary>
public void Dispose()
{
if (_disposed) return;
_disposed = true;
// Window 이벤트 해제
_window.OnItemSelected = null;
_window.OnItemDeselected = null;
_window.OnItemVisibilityChanged -= OnItemVisibilityChangedHandler;
_window.OnItemDeleted = null;
_window.OnItemDoubleClicked = null;
// StageObjectManager 이벤트 해제
if (_stageObjectManager != null)
{
_stageObjectManager.OnObjectAdded -= OnStageObjectAdded;
_stageObjectManager.OnObjectRemoved -= OnStageObjectRemoved;
_stageObjectManager = null;
}
// SelectionManager 이벤트 해제
if (_selectionManager != null)
{
_selectionManager.OnSelectionChanged -= OnSelectionManagerSelectionChanged;
_selectionManager = null;
}
// 매핑 정리
_stageObjectToItem.Clear();
}
}
}