#nullable enable using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// UTKComponentList를 래핑하여 윈도우 형태로 제공하는 컴포넌트입니다. /// /// 개요: /// /// UTKComponentListWindow는 UTKComponentList를 내부에 포함하고 헤더(타이틀, 닫기 버튼)를 추가한 /// 윈도우 형태의 컴포넌트입니다. 모든 트리 관련 기능은 내부 UTKComponentList에 위임됩니다. /// /// /// UXML 사용 예시: /// /// /// /// /// ]]> /// /// C# 사용 예시: /// ("component-window"); /// /// // 2. 윈도우 제목 및 닫기 버튼 설정 /// componentWindow.Title = "모델 리스트"; /// componentWindow.ShowCloseButton = true; /// /// // 3. 데이터 구성 - 카테고리와 아이템 /// var data = new List /// { /// new UTKComponentListCategoryData /// { /// name = "캐릭터", /// isExpanded = true, /// children = new List /// { /// new UTKComponentListItemData /// { /// name = "플레이어", /// ExternalKey = "player_001", /// IsVisible = true /// }, /// new UTKComponentListItemData /// { /// name = "NPC", /// ExternalKey = "npc_001", /// IsVisible = true /// } /// } /// } /// }; /// componentWindow.SetData(data); /// /// // 4. 선택 이벤트 구독 /// componentWindow.OnItemSelected = (selectedItems) => /// { /// foreach (var item in selectedItems) /// { /// Debug.Log($"선택됨: {item.name}"); /// // 3D 뷰에서 해당 모델 하이라이트 /// HighlightModel(item.ExternalKey); /// } /// }; /// /// // 5. 선택 해제 이벤트 /// componentWindow.OnItemDeselected = (deselectedItems) => /// { /// foreach (var item in deselectedItems) /// { /// UnhighlightModel(item.ExternalKey); /// } /// }; /// /// // 6. 가시성 변경 이벤트 (눈 아이콘) /// componentWindow.OnItemVisibilityChanged += (item, isVisible) => /// { /// var gameObject = FindGameObjectByKey(item.ExternalKey); /// if (gameObject != null) /// { /// gameObject.SetActive(isVisible); /// } /// }; /// /// // 7. 삭제 이벤트 (Delete/Backspace 키) /// componentWindow.EnabledDeleteItem = true; /// componentWindow.OnItemDeleted = (item) => /// { /// Debug.Log($"삭제 요청: {item.name}"); /// componentWindow.DeleteItem(item); /// }; /// /// // 8. 더블클릭 이벤트 /// componentWindow.OnItemDoubleClicked = (item) => /// { /// // 카메라 포커스 /// FocusCameraOn(item.ExternalKey); /// }; /// /// // 9. 윈도우 닫힘 이벤트 /// componentWindow.OnClosed += () => /// { /// Debug.Log("윈도우가 닫혔습니다."); /// }; /// /// // 10. 프로그래밍 방식 선택 /// componentWindow.SelectItem("플레이어", notify: true); /// componentWindow.DeselectItem("플레이어", notify: false); /// componentWindow.ClearSelection(); /// componentWindow.SelectByItemId(itemId); /// /// // 11. 아이템 추가/삭제 /// var newItem = new UTKComponentListItemData { name = "새 캐릭터" }; /// componentWindow.AddItem(newItem); /// componentWindow.AddItem(categoryData, newItem); /// componentWindow.DeleteItem(newItem); /// componentWindow.SetItemName(newItem, "수정된 이름"); /// /// // 12. 윈도우 표시 /// componentWindow.Show(); /// /// // 13. 리소스 해제 (OnDestroy에서 호출) /// componentWindow.Dispose(); /// ]]> /// [UxmlElement] public partial class UTKComponentListWindow : VisualElement, IDisposable { #region IDisposable private bool _disposed = false; #endregion #region 상수 (Constants) /// 메인 UXML 파일 경로 (Resources 폴더 기준) private const string UXML_PATH = "UIToolkit/Window/UTKComponentListWindow"; /// USS 파일 경로 (Resources 폴더 기준) private const string USS_PATH = "UIToolkit/Window/UTKComponentListWindowUss"; #endregion #region UI 컴포넌트 참조 (UI Component References) /// 내부 UTKComponentList 컴포넌트 private UTKComponentList? _componentList; /// 트리 리스트 닫기 버튼 (UTKButton) private UTKButton? _closeButton; /// 윈도우 제목 라벨 private Label? _titleLabel; #endregion #region 공개 속성 (Public Properties) /// /// 항목 삭제 기능 활성화 여부입니다. /// true일 때만 Delete/Backspace 키로 항목 삭제 이벤트가 발생합니다. /// 기본값은 false입니다. /// public bool EnabledDeleteItem { get => _componentList?.EnabledDeleteItem ?? false; set { if (_componentList != null) _componentList.EnabledDeleteItem = value; } } /// 윈도우 제목을 가져오거나 설정합니다. public string Title { get => _titleLabel?.text ?? string.Empty; set { if (_titleLabel != null) _titleLabel.text = value; } } /// 닫기 버튼 표시 여부를 설정합니다. public bool ShowCloseButton { get => _closeButton?.style.display == DisplayStyle.Flex; set { if (_closeButton != null) _closeButton.style.display = value ? DisplayStyle.Flex : DisplayStyle.None; } } #endregion #region 외부 이벤트 (Public Events) /// /// 메인/검색 리스트에서 항목이 선택될 때 발생합니다. /// public Action>? OnItemSelected { get => _componentList?.OnItemSelected; set { if (_componentList != null) _componentList.OnItemSelected = value; } } /// /// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다. /// public Action>? OnItemDeselected { get => _componentList?.OnItemDeselected; set { if (_componentList != null) _componentList.OnItemDeselected = value; } } /// /// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다. /// public event Action? OnItemVisibilityChanged { add { if (_componentList != null) _componentList.OnItemVisibilityChanged += value; } remove { if (_componentList != null) _componentList.OnItemVisibilityChanged -= value; } } /// /// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키). /// public Action? OnItemDeleted { get => _componentList?.OnItemDeleted; set { if (_componentList != null) _componentList.OnItemDeleted = value; } } /// /// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다. /// public Action? OnItemDoubleClicked { get => _componentList?.OnItemDoubleClicked; set { if (_componentList != null) _componentList.OnItemDoubleClicked = value; } } /// /// 아이콘을 클릭할 때 발생합니다. /// public Action? OnItemIconClicked { get => _componentList?.OnItemIconClicked; set { if (_componentList != null) _componentList.OnItemIconClicked = value; } } /// /// 트리 리스트가 닫힐 때(숨겨질 때) 발생합니다. /// 닫기 버튼 클릭 시 트리거됩니다. /// public event Action? OnClosed; #endregion #region 생성자 (Constructor) /// /// UTKComponentListWindow 컴포넌트를 초기화합니다. /// UXML 템플릿을 로드하고 내부 UTKComponentList를 설정합니다. /// public UTKComponentListWindow() { // 1. 메인 UXML 로드 및 복제 var visualTree = Resources.Load(UXML_PATH); if (visualTree == null) { Debug.LogError($"[UTKComponentListWindow] UXML not found at: {UXML_PATH}"); return; } visualTree.CloneTree(this); // 2. 내부 UTKComponentList 찾기 (UXML의 ui:Instance로 생성된 컴포넌트) _componentList = this.Q(); if (_componentList == null) { Debug.LogError("[UTKComponentListWindow] UTKComponentList not found in UXML"); return; } // 3. 테마 적용 및 변경 구독 UTKThemeManager.Instance.ApplyThemeToElement(this); SubscribeToThemeChanges(); // USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨) var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } // 4. 헤더 요소 참조 _titleLabel = this.Q