441 lines
16 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|