Files
XRLib/Assets/Sample/UIToolkit/UTKImageListWindowSample.cs
2025-12-31 20:02:36 +09:00

311 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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
}