#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 { /// /// UTKTreeListWindow와 StageObjectManager, SelectionManager를 연결하는 컨트롤러입니다. /// public class UTKTreeListWindowController : IDisposable { /// 연결된 UTKTreeListWindow private readonly UTKTreeListWindow _window; /// StageObject ID와 UTKTreeListItemData 간의 매핑 private readonly Dictionary _stageObjectToItem = new(); /// StageObjectManager 참조 private StageObjectManager? _stageObjectManager; /// SelectionManager 참조 private SelectionManager? _selectionManager; /// 선택 이벤트 처리 중 무한 루프 방지 플래그 private bool _isProcessingSelection = false; private bool _isInitialized = false; private bool _disposed = false; /// /// UTKTreeListItemData를 초기화하고 이벤트를 연결합니다. /// /// 연결할 UTKTreeListWindow public UTKTreeListWindowController(UTKTreeListWindow window) { _window = window; _window.OnItemSelected = OnItemSelectedHandler; _window.OnItemDeselected = OnItemDeselectedHandler; _window.OnItemVisibilityChanged += OnItemVisibilityChangedHandler; _window.OnItemDeleted = OnItemDeletedHandler; _window.OnItemDoubleClicked = OnItemDoubleClickedHandler; } /// /// 비동기 초기화: InjectorSceneContext 완료 후 StageObjectManager, SelectionManager를 가져옵니다. /// public async UniTaskVoid InitializeAsync() { await InjectorSceneContext.Instance.WaitForInitializationAsync(); // StageObjectManager 가져오기 _stageObjectManager = InjectorAppContext.Instance.Get(); 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(); if (_selectionManager != null) { _selectionManager.OnSelectionChanged += OnSelectionManagerSelectionChanged; } else { Debug.LogWarning("[UTKTreeListItemData] SelectionManager not found."); } _isInitialized = true; } /// /// UTKTreeListWindow에서 아이템이 선택되었을 때 호출됩니다. /// private void OnItemSelectedHandler(List 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; } } /// /// UTKTreeListWindow에서 아이템 선택이 해제되었을 때 호출됩니다. /// private void OnItemDeselectedHandler(List 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; } } /// /// 아이템 가시성이 변경되었을 때 호출됩니다. /// 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}"); } } } /// /// 아이템이 삭제되었을 때 호출됩니다 (Delete 키). /// 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); } } } /// /// 아이템이 더블클릭되었을 때 호출됩니다. /// 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); } } /// /// SelectionManager에서 선택이 변경되었을 때 호출됩니다. (화면에서 객체 클릭 시) /// Window의 선택 상태를 동기화합니다. /// 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; } } /// /// StageObject가 추가될 때 호출됩니다. /// private void OnStageObjectAdded(StageObjectManager.StageObject stageObject) { AddItem(stageObject); } /// /// StageObject가 제거될 때 호출됩니다. /// private void OnStageObjectRemoved(StageObjectManager.StageObject stageObject) { RemoveItem(stageObject); } /// /// StageObject로부터 UTKTreeListItemData를 생성하여 Window에 추가합니다. /// 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}"); } /// /// Transform의 자식들을 재귀적으로 탐색하여 UTKTreeListItemData를 생성 /// /// 탐색할 Transform (null이면 이름만 사용) /// 표시 이름 /// 외부 키 (루트 노드에만 설정) /// 생성된 UTKTreeListItemData 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; } /// /// UTKTreeListItemData를 Window에서 제거합니다. /// 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}"); } } /// /// Window의 선택을 해제합니다. /// public void ClearSelection() { _window.ClearSelection(); } /// /// StageObject의 표시 이름을 변경합니다. /// /// 변경할 StageObject의 ID /// 새 이름 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}"); } /// /// SelectionManager의 현재 선택 상태를 Window에 동기화합니다. /// 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; } } /// /// StageObject ID로 UTKTreeListItemData를 찾습니다. /// public UTKTreeListItemData? GetItem(string stageObjectId) { return _stageObjectToItem.TryGetValue(stageObjectId, out var item) ? item : null; } /// /// 리소스를 해제하고 이벤트 구독을 정리합니다. /// 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(); } } }