UTKImageListWindow 개발 중

This commit is contained in:
logonkhi
2025-12-31 20:02:36 +09:00
parent 91ca3c0468
commit 430fb27a10
27 changed files with 3358 additions and 22 deletions

View File

@@ -0,0 +1,310 @@
#nullable enable
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UVC.UIToolkit.List;
using UVC.UIToolkit.Window;
/// <summary>
/// UTKImageListWindow의 기능을 테스트하기 위한 샘플 MonoBehaviour입니다.
/// 이미지 리스트 데이터를 생성하고 다양한 이벤트 핸들러를 등록하여 동작을 확인합니다.
///
/// <para><b>테스트 기능:</b></para>
/// <list type="bullet">
/// <item>이미지+텍스트 아이템 표시</item>
/// <item>검색 필터링 (3글자 이상)</item>
/// <item>아이템 클릭/선택 이벤트</item>
/// <item>드래그 앤 드롭 이벤트</item>
/// </list>
/// </summary>
public class UTKImageListWindowSample : MonoBehaviour
{
#region (Fields)
[SerializeField]
[Tooltip("UI를 표시할 UIDocument 컴포넌트")]
public UIDocument? uiDocument;
[SerializeField]
[Tooltip("드래그 시 이미지가 커서를 따라다니도록 설정")]
private bool dragImageFollowCursor = true;
/// <summary>UTKImageListWindow 인스턴스</summary>
private UTKImageListWindow? _imageListWindow;
/// <summary>드롭 위치 (스크린 좌표)</summary>
private Vector2 _lastDropScreenPosition;
/// <summary>메인 카메라 참조</summary>
private Camera? _mainCamera;
#endregion
#region Unity
private void Start()
{
// UIDocument 참조 확인
uiDocument ??= GetComponent<UIDocument>();
if (uiDocument == null)
{
Debug.LogError("[UTKImageListWindowSample] UIDocument가 할당되지 않았습니다.");
return;
}
// UTKImageListWindow 인스턴스 생성 및 추가
_imageListWindow = new UTKImageListWindow();
_imageListWindow.DragImageFollowCursor = dragImageFollowCursor;
_imageListWindow.Title = "LIBRARY";
_imageListWindow.ShowCloseButton = true;
uiDocument.rootVisualElement.Add(_imageListWindow);
// 테스트 데이터 생성
CreateTestData();
// 이벤트 핸들러 등록
RegisterEventHandlers();
// 윈도우 표시
_imageListWindow.Show();
Debug.Log("[UTKImageListWindowSample] 초기화 완료");
}
private void OnDestroy()
{
// 리소스 정리
_imageListWindow?.Dispose();
_imageListWindow = null;
}
#endregion
#region
/// <summary>
/// 테스트용 이미지 리스트 데이터를 생성합니다.
/// imagePath와 prefabPath 리스트를 사용하여 데이터를 생성합니다.
/// </summary>
private void CreateTestData()
{
if (_imageListWindow == null) return;
// 이미지 경로 리스트
List<string> imagePaths = new()
{
"Simulator/Images/lib_forklift_400x300",
"Simulator/Images/lib_pallet_400x300",
"Simulator/Images/lib_worker_400x300",
};
// 프리팹 경로 리스트
List<string> prefabPaths = new()
{
"Simulator/FreeForkLift/Prefabs/Forklift",
"Simulator/FreeForkLift/Prefabs/PalletEmpty",
"Simulator/CharCrafter Free Preset Characters Pack (Vol. 1)/Prefabs/Male Young Guy",
};
// 아이템 이름 리스트
string[] itemNames = { "지게차", "팔레트", "작업자" };
var data = new List<UTKImageListItemData>();
// imagePath, prefabPath 리스트를 사용하여 데이터 생성
for (int i = 0; i < 19; i++)
{
var itemData = new UTKImageListItemData
{
externalId = $"item-{i:D4}",
itemName = itemNames[i % itemNames.Length],
imagePath = imagePaths[i % imagePaths.Count],
objectPrefabPath = prefabPaths[i % prefabPaths.Count],
tag = "시뮬레이터"
};
data.Add(itemData);
}
// 데이터 설정
_imageListWindow.SetData(data);
Debug.Log($"[UTKImageListWindowSample] 테스트 데이터 생성 완료: {data.Count}개 아이템");
}
#endregion
#region
/// <summary>
/// UTKImageListWindow의 이벤트 핸들러들을 등록합니다.
/// 클릭, 드래그 앤 드롭, 윈도우 닫기 이벤트를 처리합니다.
/// </summary>
private void RegisterEventHandlers()
{
if (_imageListWindow == null) return;
// 아이템 클릭 이벤트
// 사용자가 아이템을 클릭할 때 발생
_imageListWindow.OnItemClick += (UTKImageListItemData item) =>
{
Debug.Log($"[클릭] {item.itemName} (ID: {item.externalId}, Tag: {item.tag})");
};
// 드래그 시작 이벤트
// 사용자가 아이템을 드래그하기 시작할 때 발생
_imageListWindow.OnItemBeginDrag += (UTKImageListItemData item, Vector2 position) =>
{
Debug.Log($"[드래그 시작] {item.itemName} at {position}");
};
// 드래그 중 이벤트
// 아이템을 드래그하는 동안 지속적으로 발생
_imageListWindow.OnItemDrag += (UTKImageListItemData item, Vector2 position) =>
{
// 너무 많은 로그를 방지하기 위해 주석 처리
// Debug.Log($"[드래그 중] {item.itemName} at {position}");
};
// 드래그 종료 이벤트
// 드래그가 끝났을 때 발생 (드롭 직전)
_imageListWindow.OnItemEndDrag += (UTKImageListItemData item, Vector2 position) =>
{
Debug.Log($"[드래그 종료] {item.itemName} at {position}");
// 드롭 위치 저장 (스크린 좌표)
_lastDropScreenPosition = position;
};
// 아이템 드롭 이벤트
// 드래그가 완료되어 아이템이 드롭되었을 때 발생
_imageListWindow.OnItemDrop += (UTKImageListItemData item) =>
{
Debug.Log($"[드롭] {item.itemName} - ObjectPrefabPath: {item.objectPrefabPath}");
// 프리팹 로드
var prefab = Resources.Load<GameObject>(item.objectPrefabPath);
if (prefab == null)
{
Debug.LogWarning($"[UTKImageListWindowSample] 프리팹을 찾을 수 없습니다: {item.objectPrefabPath}");
return;
}
// 월드 좌표 계산
Vector3 worldPosition = ScreenToWorldPosition(_lastDropScreenPosition);
// 프리팹 인스턴스화
var instance = Instantiate(prefab, worldPosition, Quaternion.identity);
Debug.Log($"[UTKImageListWindowSample] 프리팹 생성됨: {instance.name} at {worldPosition}");
};
// 윈도우 닫기 이벤트
// 사용자가 닫기 버튼을 클릭하거나 Close() 호출 시 발생
_imageListWindow.OnClosed += () =>
{
Debug.Log("[윈도우 닫힘]");
};
Debug.Log("[UTKImageListWindowSample] 이벤트 핸들러 등록 완료");
}
#endregion
#region (Coordinate Conversion)
/// <summary>
/// 스크린 좌표를 월드 좌표로 변환합니다.
/// 바닥면(Y=0)에 레이캐스트하여 위치를 계산합니다.
/// </summary>
/// <param name="screenPosition">스크린 좌표</param>
/// <returns>월드 좌표 (레이캐스트 실패 시 카메라 전방 10m 위치)</returns>
private Vector3 ScreenToWorldPosition(Vector2 screenPosition)
{
// 메인 카메라 캐시
if (_mainCamera == null)
{
_mainCamera = Camera.main;
}
if (_mainCamera == null)
{
Debug.LogWarning("[UTKImageListWindowSample] 메인 카메라를 찾을 수 없습니다.");
return Vector3.zero;
}
// 스크린 좌표에서 레이 생성
Ray ray = _mainCamera.ScreenPointToRay(screenPosition);
// 바닥면(Y=0 평면)과의 교차점 계산
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
if (groundPlane.Raycast(ray, out float distance))
{
return ray.GetPoint(distance);
}
// 물리 레이캐스트로 콜라이더와 충돌 확인
if (Physics.Raycast(ray, out RaycastHit hit, 100f))
{
return hit.point;
}
// 실패 시 카메라 전방 10m 위치 반환
return ray.GetPoint(10f);
}
#endregion
#region
/// <summary>
/// 런타임에 아이템을 추가하는 테스트 메서드입니다.
/// Inspector에서 컨텍스트 메뉴로 호출할 수 있습니다.
/// </summary>
[ContextMenu("Add Test Item")]
public void AddTestItem()
{
if (_imageListWindow == null) return;
var newItem = new UTKImageListItemData
{
externalId = $"runtime-{System.Guid.NewGuid():N}",
itemName = $"런타임 아이템 {_imageListWindow.ItemCount + 1}",
imagePath = "Prefabs/Thumbnails/runtime_item",
objectPrefabPath = "Prefabs/Objects/runtime_item",
tag = "런타임"
};
_imageListWindow.AddItem(newItem);
Debug.Log($"[UTKImageListWindowSample] 아이템 추가됨: {newItem.itemName}");
}
/// <summary>
/// 모든 아이템을 제거하는 테스트 메서드입니다.
/// </summary>
[ContextMenu("Clear All Items")]
public void ClearAllItems()
{
_imageListWindow?.Clear();
Debug.Log("[UTKImageListWindowSample] 모든 아이템 제거됨");
}
/// <summary>
/// 검색을 테스트하는 메서드입니다.
/// </summary>
[ContextMenu("Test Search (전기)")]
public void TestSearch()
{
_imageListWindow?.ApplySearch("전기");
Debug.Log("[UTKImageListWindowSample] 검색 테스트: '전기'");
}
/// <summary>
/// 윈도우를 다시 표시하는 메서드입니다.
/// </summary>
[ContextMenu("Show Window")]
public void ShowWindow()
{
_imageListWindow?.Show();
}
#endregion
}