#nullable enable using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// UTKImageList를 래핑하여 윈도우 형태로 제공하는 컴포넌트입니다. /// LibraryWindow의 UIToolkit 버전입니다. /// /// 개요: /// /// UTKImageListWindow는 UTKImageList를 내부에 포함하고 헤더(타이틀, 닫기 버튼)를 추가한 /// 윈도우 형태의 컴포넌트입니다. 모든 리스트 관련 기능은 내부 UTKImageList에 위임됩니다. /// /// /// 주요 기능: /// /// 윈도우 형태의 UI 제공 (헤더 + 닫기 버튼) /// 내부 UTKImageList의 모든 기능 위임 /// 드래그 앤 드롭 이벤트 전달 /// /// /// UXML 사용 예시: /// /// /// /// /// ]]> /// /// C# 사용 예시: /// ("library-window"); /// /// // 2. 윈도우 제목 및 닫기 버튼 설정 /// imageWindow.Title = "프리팹 라이브러리"; /// imageWindow.ShowCloseButton = true; /// /// // 3. 데이터 구성 - 이미지와 이름을 가진 아이템들 /// var data = new List /// { /// new UTKImageListItemData /// { /// itemName = "의자", /// imagePath = "Prefabs/Thumbnails/chair", /// objectPrefabPath = "Prefabs/Furniture/Chair" /// }, /// new UTKImageListItemData /// { /// itemName = "책상", /// imagePath = "Prefabs/Thumbnails/desk", /// objectPrefabPath = "Prefabs/Furniture/Desk" /// } /// }; /// imageWindow.SetData(data); /// /// // 4. 아이템 클릭 이벤트 구독 /// imageWindow.OnItemClick = (item) => /// { /// Debug.Log($"클릭된 아이템: {item.itemName}"); /// }; /// /// // 5. 드래그 시작 이벤트 /// imageWindow.OnItemBeginDrag = (item, screenPos) => /// { /// Debug.Log($"드래그 시작: {item.itemName}"); /// }; /// /// // 6. 드래그 중 이벤트 /// imageWindow.OnItemDrag = (item, screenPos) => /// { /// // 드래그 중 위치 업데이트 /// }; /// /// // 7. 드래그 종료 이벤트 /// imageWindow.OnItemEndDrag = (item, screenPos) => /// { /// Debug.Log($"드래그 종료: {item.itemName}"); /// }; /// /// // 8. 드롭 이벤트 (프리팹 인스턴스화) /// imageWindow.OnItemDrop = (item) => /// { /// if (!string.IsNullOrEmpty(item.objectPrefabPath)) /// { /// var prefab = Resources.Load(item.objectPrefabPath); /// Instantiate(prefab, dropPosition, Quaternion.identity); /// } /// }; /// /// // 9. 리스트 영역 밖으로 드래그 시 3D 미리보기 표시 /// imageWindow.OnDragExitList = (item, screenPos) => /// { /// // 리스트 밖 = 3D 씬 영역 /// Show3DPreview(item.objectPrefabPath, screenPos); /// }; /// /// // 10. 리스트 영역으로 다시 들어오면 미리보기 숨김 /// imageWindow.OnDragEnterList = (item, screenPos) => /// { /// Hide3DPreview(); /// }; /// /// // 11. 윈도우 닫힘 이벤트 /// imageWindow.OnClosed += () => /// { /// Debug.Log("윈도우가 닫혔습니다."); /// }; /// /// // 12. 드래그 고스트 이미지 설정 /// imageWindow.DragImageFollowCursor = true; /// /// // 13. 검색 실행 /// imageWindow.ApplySearch("의자"); /// /// // 14. 아이템 추가/제거 /// var newItem = new UTKImageListItemData { itemName = "새 아이템" }; /// imageWindow.AddItem(newItem); /// imageWindow.RemoveItem(newItem); /// imageWindow.Clear(); /// /// // 15. 아이템 개수 확인 /// int count = imageWindow.ItemCount; /// /// // 16. 윈도우 표시/닫기 /// imageWindow.Show(); /// imageWindow.Close(); /// /// // 17. 리소스 해제 (OnDestroy에서 호출) /// imageWindow.Dispose(); /// ]]> /// /// 관련 리소스: /// /// Resources/UIToolkit/Window/UTKImageListWindow.uxml - 윈도우 레이아웃 /// Resources/UIToolkit/Window/UTKImageListWindow.uss - 윈도우 스타일 /// /// [UxmlElement] public partial class UTKImageListWindow : VisualElement, IDisposable { #region 상수 (Constants) /// 메인 UXML 파일 경로 (Resources 폴더 기준) private const string UXML_PATH = "UIToolkit/Window/UTKImageListWindow"; /// USS 파일 경로 (Resources 폴더 기준) private const string USS_PATH = "UIToolkit/Window/UTKImageListWindowUss"; #endregion #region UI 컴포넌트 참조 (UI Component References) /// 내부 UTKImageList 컴포넌트 private UTKImageList? _imageList; /// 윈도우 닫기 버튼 (UTKButton) private UTKButton? _closeButton; /// 윈도우 제목 라벨 private Label? _titleLabel; #endregion #region IDisposable private bool _disposed; #endregion #region 공개 속성 (Public Properties) /// /// 드래그 시 이미지가 커서를 따라다니도록 설정합니다. /// 내부 UTKImageList에 위임됩니다. /// public bool DragImageFollowCursor { get => _imageList?.DragImageFollowCursor ?? true; set { if (_imageList != null) _imageList.DragImageFollowCursor = value; } } /// /// 드래그 영역 체크에 사용할 요소를 설정합니다. /// 기본값은 윈도우 자체입니다. /// public VisualElement? DragBoundsElement { get => _imageList?.DragBoundsElement; set { if (_imageList != null) _imageList.DragBoundsElement = 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; } } /// /// 현재 표시 중인 아이템 수를 반환합니다. /// public int ItemCount => _imageList?.ItemCount ?? 0; #endregion #region 이벤트 위임 (Event Delegation) /// /// 아이템 클릭 이벤트입니다. /// 내부 UTKImageList에서 발생한 이벤트를 전달합니다. /// public Action? OnItemClick { get => _imageList?.OnItemClick; set { if (_imageList != null) _imageList.OnItemClick = value; } } /// /// 아이템 드롭 이벤트입니다. /// public Action? OnItemDrop { get => _imageList?.OnItemDrop; set { if (_imageList != null) _imageList.OnItemDrop = value; } } /// /// 드래그 시작 이벤트입니다. /// (아이템 데이터, 화면 좌표) /// public Action? OnItemBeginDrag { get => _imageList?.OnItemBeginDrag; set { if (_imageList != null) _imageList.OnItemBeginDrag = value; } } /// /// 드래그 중 이벤트입니다. /// (아이템 데이터, 화면 좌표) /// public Action? OnItemDrag { get => _imageList?.OnItemDrag; set { if (_imageList != null) _imageList.OnItemDrag = value; } } /// /// 드래그 종료 이벤트입니다. /// (아이템 데이터, 화면 좌표) /// public Action? OnItemEndDrag { get => _imageList?.OnItemEndDrag; set { if (_imageList != null) _imageList.OnItemEndDrag = value; } } /// /// 드래그 중 리스트 영역을 벗어났을 때 발생하는 이벤트입니다. /// (아이템 데이터, 화면 좌표) /// 3D 프리팹 미리보기를 표시하는데 사용합니다. /// public Action? OnDragExitList { get => _imageList?.OnDragExitList; set { if (_imageList != null) _imageList.OnDragExitList = value; } } /// /// 드래그 중 리스트 영역에 다시 진입했을 때 발생하는 이벤트입니다. /// (아이템 데이터, 화면 좌표) /// 3D 프리팹 미리보기를 숨기는데 사용합니다. /// public Action? OnDragEnterList { get => _imageList?.OnDragEnterList; set { if (_imageList != null) _imageList.OnDragEnterList = value; } } /// /// 윈도우가 닫힐 때(숨겨질 때) 발생합니다. /// 닫기 버튼 클릭 또는 Close() 메서드 호출 시 트리거됩니다. /// public event Action? OnClosed; #endregion #region 생성자 (Constructor) /// /// UTKImageListWindow 컴포넌트를 초기화합니다. /// UXML 템플릿을 로드하고 내부 UTKImageList를 설정합니다. /// public UTKImageListWindow() { // 1. 메인 UXML 로드 및 복제 var visualTree = Resources.Load(UXML_PATH); if (visualTree == null) { Debug.LogError($"[UTKImageListWindow] UXML not found at: {UXML_PATH}"); return; } visualTree.CloneTree(this); // 2. 테마 적용 및 변경 구독 UTKThemeManager.Instance.ApplyThemeToElement(this); SubscribeToThemeChanges(); // USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨) var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } // 3. 내부 UTKImageList 찾기 (UXML에서 생성된 컴포넌트) _imageList = this.Q(); if (_imageList == null) { Debug.LogError("[UTKImageListWindow] UTKImageList not found in UXML"); } else { // 드래그 영역 체크에 윈도우 전체 영역을 사용하도록 설정 _imageList.DragBoundsElement = this; } // 4. 헤더 요소 참조 _titleLabel = this.Q