#nullable enable using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UIElements; using UVC.UIToolkit.List; namespace UVC.UIToolkit.Window { /// /// 계층적 트리 구조를 표시하는 커스텀 UI Toolkit 컴포넌트입니다. /// /// 개요: /// /// TreeListWindow는 Unity UI Toolkit의 TreeView를 래핑하여 검색, 가시성 토글, /// 닫기 기능 등을 제공하는 재사용 가능한 컴포넌트입니다. /// UXML 파일(TreeListWindow.uxml, TreeListItem.uxml)과 함께 사용됩니다. /// /// /// 주요 기능: /// /// 계층적 트리 구조 표시 (펼치기/접기 지원) /// 실시간 검색 필터링 /// 항목별 가시성(눈 아이콘) 토글 /// 선택 이벤트 처리 /// /// /// UXML에서 사용: /// /// /// /// /// 코드에서 사용: /// /// var treeList = root.Q(); /// treeList.OnSelectionChanged += (item) => Debug.Log($"선택: {item.name}"); /// treeList.OnVisibilityChanged += (item) => model.SetActive(item.id, item.IsVisible); /// treeList.SetData(treeItems); /// /// /// 관련 리소스: /// /// Resources/UIToolkit/Window/UTKTreeListWindow.uxml - 메인 레이아웃 /// Resources/UIToolkit/List/UTKTreeListItem.uxml - 개별 항목 템플릿 /// /// /// 선택 해제 방지: /// /// 빈 영역 클릭 시 선택이 해제되지 않도록 하려면 EventSystem의 InputSystemUIInputModule 컴포넌트에서 /// Deselect On Background Click 옵션을 해제해야 합니다. /// /// [UxmlElement] public partial class UTKTreeListWindow : VisualElement, IDisposable { #region IDisposable private bool _disposed = false; #endregion #region 상수 (Constants) /// 메인 UXML 파일 경로 (Resources 폴더 기준) private const string UXML_PATH = "UIToolkit/Window/UTKTreeListWindow"; #endregion #region UI 컴포넌트 참조 (UI Component References) /// 검색어 입력 필드 private TextField? _searchField; /// Unity UI Toolkit의 TreeView 컴포넌트 private TreeView? _treeView; /// 트리 리스트 닫기 버튼 private Button? _closeButton; /// 검색어 지우기 버튼 private Button? _clearButton; #endregion #region 내부 데이터 (Internal Data) /// /// 원본 루트 데이터입니다. /// 검색 필터 해제 시 원래 데이터로 복원하는 데 사용됩니다. /// private List _originalRoots = new(); /// /// TreeView에 바인딩되는 데이터 소스입니다. /// TreeViewItemData는 Unity의 TreeView가 요구하는 래퍼 타입입니다. /// private List>? _rootData; /// /// 항목 ID 자동 생성을 위한 시드 값입니다. /// SetData() 호출 시 id가 0인 항목에 순차적으로 ID를 할당합니다. /// private int _idSeed = 1; /// /// 이전에 선택된 항목들입니다. /// 선택 해제 이벤트 발송에 사용됩니다. /// private List _previouslySelectedItems = new(); /// /// 선택 이벤트 발송을 일시적으로 억제하는 플래그입니다. /// 프로그래밍 방식으로 선택 시 이벤트를 발송하지 않으려면 true로 설정합니다. /// private bool _suppressSelectionEvent = false; /// /// 펼침/접힘 이벤트 처리를 일시적으로 억제하는 플래그입니다. /// ExpandByData() 실행 중 이벤트로 인한 데이터 덮어쓰기를 방지합니다. /// private bool _suppressExpandEvent = false; #endregion #region 공개 속성 (Public Properties) /// /// 항목 삭제 기능 활성화 여부입니다. /// true일 때만 Delete/Backspace 키로 항목 삭제 이벤트가 발생합니다. /// 기본값은 true입니다. /// public bool EnabledDeleteItem { get; set; } = true; #endregion #region 외부 이벤트 (Public Events) /// /// 메인/검색 리스트에서 항목이 선택될 때 발생합니다. /// public Action>? OnItemSelected; /// /// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다. /// public Action>? OnItemDeselected; /// /// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다. /// 3D 모델의 GameObject 활성화/비활성화에 연동합니다. /// public event Action? OnItemVisibilityChanged; /// /// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키). /// public Action? OnItemDeleted; /// /// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다. /// public Action? OnItemDoubleClicked; /// /// 트리 리스트가 닫힐 때(숨겨질 때) 발생합니다. /// 닫기 버튼 클릭 시 트리거됩니다. /// public event Action? OnClosed; #endregion #region 생성자 (Constructor) /// /// TreeListWindow 컴포넌트를 초기화합니다. /// UXML 템플릿을 로드하고 내부 컴포넌트를 설정합니다. /// public UTKTreeListWindow() { // 1. 메인 UXML 로드 및 복제 // CloneTree(this)로 UXML 내용이 이 클래스의 자식으로 추가됨 var visualTree = Resources.Load(UXML_PATH); if (visualTree == null) { Debug.LogError($"[TreeMenu] UXML not found at: {UXML_PATH}"); return; } visualTree!.CloneTree(this); // 2. 자식 요소 참조 획득 (UXML의 name 속성으로 찾음) _searchField = this.Q("search-field"); _treeView = this.Q("main-tree-view"); _closeButton = this.Q