305 lines
11 KiB
C#
305 lines
11 KiB
C#
#nullable enable
|
|
using Gpm.Ui;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UVC.Extention;
|
|
using UVC.Locale;
|
|
using UVC.UI.Modal;
|
|
|
|
namespace UVC.UI.List
|
|
{
|
|
public class PrefabGrid : MonoBehaviour
|
|
{
|
|
[Tooltip("데이터를 표시할 InfiniteScroll 컴포넌트입니다.")]
|
|
[SerializeField]
|
|
protected InfiniteScroll? scrollList = null;
|
|
|
|
[Tooltip("검색어 입력을 위한 TMP_InputField 컴포넌트입니다.")]
|
|
[SerializeField]
|
|
protected TMP_InputField? inputField = null;
|
|
|
|
[SerializeField]
|
|
protected TextMeshProUGUI searchResultText;
|
|
|
|
[Tooltip("드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.")]
|
|
[SerializeField]
|
|
public bool dragImageFollowCursor = true;
|
|
|
|
/// <summary>
|
|
/// 드래그 시 그리드 아이템 이미지가 커서를 따라다니도록 설정합니다.
|
|
/// </summary>
|
|
public bool DragImageFollowCursor => dragImageFollowCursor;
|
|
|
|
// InfiniteScroll에 표시될 원본 데이터 리스트입니다.
|
|
protected List<PrefabGridItemData>? data;
|
|
protected List<PrefabGridItemData>? filteredData;
|
|
|
|
private Vector2 originalScrollPosition;
|
|
|
|
#region 이벤트 (Events)
|
|
|
|
/// <summary>
|
|
/// 아이템 클릭 이벤트.
|
|
/// </summary>
|
|
public Action<PrefabGridItemData>? OnItemClick;
|
|
|
|
/// <summary>
|
|
/// 아이템 드롭 이벤트.
|
|
/// </summary>
|
|
public Action<PrefabGridItemData>? OnItemDrop;
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 시작 이벤트.
|
|
/// (itemData, screenPosition)
|
|
/// </summary>
|
|
public Action<PrefabGridItemData, Vector2>? OnItemBeginDrag;
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 중 이벤트.
|
|
/// (itemData, screenPosition)
|
|
/// </summary>
|
|
public Action<PrefabGridItemData, Vector2>? OnItemDrag;
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 종료 이벤트.
|
|
/// (itemData, screenPosition)
|
|
/// </summary>
|
|
public Action<PrefabGridItemData, Vector2>? OnItemEndDrag;
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// SingletonScene 초기화 과정에서 호출됩니다.
|
|
/// 필요한 컴포넌트를 찾고, 이벤트 리스너를 등록합니다.
|
|
/// </summary>
|
|
protected void Start()
|
|
{
|
|
// scrollList가 인스펙터에서 할당되지 않았을 경우, 자식에서 찾아봅니다.
|
|
if (scrollList == null)
|
|
{
|
|
scrollList = GetComponentInChildren<InfiniteScroll>();
|
|
}
|
|
|
|
if (scrollList == null)
|
|
{
|
|
Debug.LogError("InfiniteScroll component is not assigned or found in Children.");
|
|
return;
|
|
}
|
|
|
|
if (scrollList != null)
|
|
{
|
|
var rect = scrollList.GetComponent<RectTransform>();
|
|
originalScrollPosition = scrollList.GetComponent<RectTransform>().anchoredPosition;
|
|
}
|
|
|
|
// inputField가 인스펙터에서 할당되지 않았을 경우, 자식에서 찾아봅니다.
|
|
if (inputField == null)
|
|
{
|
|
inputField = GetComponentInChildren<TMP_InputField>();
|
|
}
|
|
if (inputField != null)
|
|
{
|
|
// 사용자가 검색어를 입력하고 Enter 키를 누르거나 입력을 완료했을 때 OnInputFieldChanged 메서드가 호출되도록 이벤트를 등록합니다.
|
|
//inputField.onEndEdit.AddListener(OnInputFieldChanged);
|
|
inputField.onSubmit.AddListener(OnInputFieldChanged);
|
|
}
|
|
}
|
|
|
|
protected void OnDestroy()
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
// Init에서 등록한 리스너를 제거합니다.
|
|
//inputField.onEndEdit.RemoveListener(OnInputFieldChanged);
|
|
inputField.onSubmit.RemoveListener(OnInputFieldChanged);
|
|
}
|
|
|
|
// 이벤트 핸들러 정리
|
|
OnItemClick = null;
|
|
OnItemDrop = null;
|
|
OnItemBeginDrag = null;
|
|
OnItemDrag = null;
|
|
OnItemEndDrag = null;
|
|
|
|
// 데이터 참조 정리
|
|
data?.Clear();
|
|
data = null;
|
|
filteredData?.Clear();
|
|
filteredData = null;
|
|
}
|
|
|
|
#region 내부 이벤트 핸들러 (Internal Event Handlers)
|
|
|
|
/// <summary>
|
|
/// 아이템 클릭 처리 (PrefabGridItem에서 호출)
|
|
/// </summary>
|
|
internal void HandleItemClick(PrefabGridItemData itemData)
|
|
{
|
|
OnItemClick?.Invoke(itemData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 아이템 드롭 처리 (PrefabGridItem에서 호출)
|
|
/// </summary>
|
|
internal void HandleItemDrop(PrefabGridItemData itemData)
|
|
{
|
|
OnItemDrop?.Invoke(itemData);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 시작 처리 (PrefabGridItem에서 호출)
|
|
/// </summary>
|
|
internal void HandleItemBeginDrag(PrefabGridItemData itemData, Vector2 screenPosition)
|
|
{
|
|
OnItemBeginDrag?.Invoke(itemData, screenPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 중 처리 (PrefabGridItem에서 호출)
|
|
/// </summary>
|
|
internal void HandleItemDrag(PrefabGridItemData itemData, Vector2 screenPosition)
|
|
{
|
|
OnItemDrag?.Invoke(itemData, screenPosition);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 아이템 드래그 종료 처리 (PrefabGridItem에서 호출)
|
|
/// </summary>
|
|
internal void HandleItemEndDrag(PrefabGridItemData itemData, Vector2 screenPosition)
|
|
{
|
|
OnItemEndDrag?.Invoke(itemData, screenPosition);
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// FactoryObjectManager에서 데이터를 가져와 스크롤 리스트를 설정합니다.
|
|
/// 데이터를 카테고리별로 그룹화하고, 각 카테고리와 해당 항목들을 리스트에 추가합니다.
|
|
/// </summary>
|
|
public virtual void SetupData(List<PrefabGridItemData> objectsData)
|
|
{
|
|
if (scrollList == null)
|
|
{
|
|
Debug.LogError("InfiniteScroll component is not assigned.");
|
|
return;
|
|
}
|
|
|
|
if (inputField != null) inputField.text = string.Empty;
|
|
if (searchResultText != null) searchResultText.gameObject.SetActive(false);
|
|
|
|
scrollList.ClearData(); // 스크롤 리스트의 기존 데이터를 모두 비웁니다.
|
|
|
|
if (data == null) data = new List<PrefabGridItemData>();
|
|
data?.Clear();
|
|
data?.AddRange(objectsData);
|
|
|
|
// 3. 완성된 데이터 리스트를 InfiniteScroll에 한 번에 삽입하여 UI를 업데이트합니다.
|
|
scrollList.InsertData(data!.ToArray());
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 검색 입력 필드의 내용이 변경되고 제출(Enter)되었을 때 호출됩니다.
|
|
/// 입력된 텍스트를 기반으로 scrollList의 내용을 필터링합니다.
|
|
/// </summary>
|
|
/// <param name="text">사용자가 입력한 검색어</param>
|
|
protected virtual void OnInputFieldChanged(string text)
|
|
{
|
|
Debug.Log($"Searching1 for: {text}");
|
|
if (scrollList == null || inputField == null) return;
|
|
// 검색어가 비어있으면 필터링을 수행하지 않습니다.
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
// 검색어가 너무 짧으면 사용자에게 알림을 표시하고 입력을 초기화합니다.
|
|
if (text.Length < 3)
|
|
{
|
|
inputField.text = string.Empty; // 입력 필드 초기화
|
|
inputField.ActivateInputField(); // 입력 필드에 다시 포커스 설정
|
|
Toast.Show("검색어는 3글자 이상 입력해주세요.", 2f);
|
|
return;
|
|
}
|
|
|
|
Debug.Log($"Searching2 for: {text}");
|
|
|
|
// 원본 데이터(data)를 기반으로 필터링된 새로운 리스트를 생성합니다.
|
|
List<PrefabGridItemData> filteredItems = data.Where(itemData =>
|
|
{
|
|
return itemData.ItemName.Contains(text, StringComparison.OrdinalIgnoreCase);
|
|
}).ToList();
|
|
|
|
if (filteredData == null)
|
|
{
|
|
filteredData = new List<PrefabGridItemData>();
|
|
}
|
|
else
|
|
{
|
|
filteredData.Clear();
|
|
}
|
|
string resultText = LocalizationManager.Instance.GetString("검색결과");
|
|
|
|
if (searchResultText != null)
|
|
{
|
|
searchResultText.gameObject.SetActive(true);
|
|
searchResultText.text = $"{resultText}: {filteredItems.Count}건";
|
|
}
|
|
|
|
if (scrollList != null)
|
|
{
|
|
var rect = scrollList.GetComponent<RectTransform>();
|
|
rect.anchoredPosition = new Vector2(originalScrollPosition.x, originalScrollPosition.y - 24);
|
|
}
|
|
|
|
// 필터링된 아이템들을 결과 리스트에 추가합니다.
|
|
filteredData.AddRange(filteredItems);
|
|
|
|
// 스크롤 리스트를 비우고 필터링된 결과로 새로 채웁니다.
|
|
scrollList?.Clear();
|
|
scrollList?.InsertData(filteredData.ToArray(), true);
|
|
}
|
|
|
|
public void SetSearchText(string text)
|
|
{
|
|
if (inputField != null)
|
|
{
|
|
inputField.text = $"@{text} ";
|
|
inputField.SetCaretToEndAsync().Forget();
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 검색창의 'X' 버튼 클릭 시 호출됩니다.
|
|
/// 입력 필드를 비우고, 적용되었던 모든 필터를 제거합니다.
|
|
/// </summary>
|
|
public virtual void OnClickClearText()
|
|
{
|
|
if (inputField != null && scrollList != null && data != null)
|
|
{
|
|
|
|
if (scrollList != null && !inputField.text.IsNullOrEmpty())
|
|
{
|
|
var rect = scrollList.GetComponent<RectTransform>();
|
|
rect.anchoredPosition = originalScrollPosition;
|
|
}
|
|
|
|
if (searchResultText != null) searchResultText.gameObject.SetActive(false);
|
|
|
|
inputField.text = string.Empty; // 입력 필드 초기화
|
|
inputField.ActivateInputField(); // 입력 필드에 다시 포커스 설정
|
|
if (filteredData != null)
|
|
{
|
|
filteredData.Clear(); // 필터링된 데이터 리스트를 비웁니다.
|
|
filteredData = null;
|
|
}
|
|
// scrollList에 설정된 필터를 제거(null)하여 모든 항목이 보이도록 합니다.
|
|
scrollList?.SetFilter(null);
|
|
scrollList?.ClearData(); // 스크롤 리스트의 내용을 비웁니다.
|
|
scrollList?.InsertData(data.ToArray());
|
|
}
|
|
}
|
|
}
|
|
}
|