Asset Library 기능 추가
This commit is contained in:
@@ -72,7 +72,7 @@ namespace UVC.UI.List.ComponentList
|
||||
/// isCategory = false,
|
||||
/// generalName = "분전반",
|
||||
/// generalOption = "옵션 A",
|
||||
/// factoryObjectInfo = new FactoryObjectInfo { Id = "some-unique-id", Name = "분전반" }
|
||||
/// factoryObjectInfo = new FactoryObjectInfo { Id = "some-unique-Id", Name = "분전반" }
|
||||
/// };
|
||||
///
|
||||
/// // 2. 생성한 데이터를 InfiniteScroll에 추가합니다.
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace UVC.UI.List.Draggable
|
||||
/// public class MyItemData : ListItemData
|
||||
/// {
|
||||
/// public string Title;
|
||||
/// public MyItemData(string title, string id, string displayName, Sprite? icon = null) : base(id, displayName, icon)
|
||||
/// public MyItemData(string title, string Id, string displayName, Sprite? icon = null) : base(Id, displayName, icon)
|
||||
/// {
|
||||
/// Title = title;
|
||||
/// }
|
||||
|
||||
206
Assets/Scripts/UVC/UI/List/PrefabGrid.cs
Normal file
206
Assets/Scripts/UVC/UI/List/PrefabGrid.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
#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;
|
||||
|
||||
// InfiniteScroll에 표시될 원본 데이터 리스트입니다.
|
||||
protected List<PrefabGridItemData>? data;
|
||||
protected List<PrefabGridItemData>? filteredData;
|
||||
|
||||
private Vector2 originalScrollPosition;
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/List/PrefabGrid.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/List/PrefabGrid.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b906924cc1a9b94489e83b6bc123be4a
|
||||
78
Assets/Scripts/UVC/UI/List/PrefabGridItem.cs
Normal file
78
Assets/Scripts/UVC/UI/List/PrefabGridItem.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using Gpm.Ui;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UVC.Util;
|
||||
|
||||
namespace UVC.UI.List
|
||||
{
|
||||
public class PrefabGridItem : InfiniteScrollItem, IPointerEnterHandler, IPointerExitHandler
|
||||
{
|
||||
[SerializeField]
|
||||
private Image image;
|
||||
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI text;
|
||||
|
||||
/// <summary>
|
||||
/// InfiniteScroll에 의해 데이터가 이 아이템에 할당될 때 호출되는 핵심 메서드입니다.
|
||||
/// 스크롤 시 아이템이 재활용될 때마다 새로운 데이터로 이 메서드가 호출되어 UI를 갱신합니다.
|
||||
/// </summary>
|
||||
/// <param name="scrollData">이 아이템에 표시할 데이터입니다. `InfiniteScrollData`를 상속받은 `ComponentListItemData` 객체입니다.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 1. 표시할 데이터를 생성합니다.
|
||||
/// var categoryData = new ComponentListItemData
|
||||
/// {
|
||||
/// isCategory = true,
|
||||
/// categoryName = "전기 설비",
|
||||
/// categoryBadgeCount = 10
|
||||
/// };
|
||||
///
|
||||
/// var generalData = new ComponentListItemData
|
||||
/// {
|
||||
/// isCategory = false,
|
||||
/// generalName = "분전반",
|
||||
/// generalOption = "옵션 A",
|
||||
/// factoryObjectInfo = new FactoryObjectInfo { Id = "some-unique-Id", Name = "분전반" }
|
||||
/// };
|
||||
///
|
||||
/// // 2. 생성한 데이터를 InfiniteScroll에 추가합니다.
|
||||
/// // infiniteScroll은 InfiniteScroll 컴포넌트의 인스턴스입니다.
|
||||
/// infiniteScroll.InsertData(categoryData);
|
||||
/// infiniteScroll.InsertData(generalData);
|
||||
///
|
||||
/// // 위 코드가 실행되면, InfiniteScroll은 각 데이터에 맞는 ComponentListItem을 생성(또는 재활용)하고,
|
||||
/// // 이 UpdateData 메서드를 호출하여 UI를 데이터에 맞게 설정합니다.
|
||||
/// </code>
|
||||
/// </example>
|
||||
public override async void UpdateData(InfiniteScrollData scrollData)
|
||||
{
|
||||
// 1. 부모 클래스의 UpdateData를 호출하여 기본 초기화 작업을 수행합니다.
|
||||
// 이 과정에서 this.scrollData에 매개변수로 받은 scrollData가 할당됩니다.
|
||||
base.UpdateData(scrollData);
|
||||
|
||||
PrefabGridItemData data = (PrefabGridItemData)scrollData;
|
||||
|
||||
text.text = data.ItemName;
|
||||
image.sprite = await ResourceManager.LoadOnlyAsync<Sprite>(data.ImagePrefabPath);
|
||||
}
|
||||
|
||||
public void OnClick()
|
||||
{
|
||||
PrefabGridItemData data = (PrefabGridItemData)scrollData;
|
||||
data.OnClickAction?.Invoke(data);
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
CursorManager.Instance.SetCursor(CursorType.HandPoint);
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
CursorManager.Instance.SetCursor(CursorType.Default);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/List/PrefabGridItem.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/List/PrefabGridItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ae3315290cb6cc45b65306627d6a04b
|
||||
33
Assets/Scripts/UVC/UI/List/PrefabGridItemData.cs
Normal file
33
Assets/Scripts/UVC/UI/List/PrefabGridItemData.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
#nullable enable
|
||||
using Gpm.Ui;
|
||||
using System;
|
||||
|
||||
namespace UVC.UI.List
|
||||
{
|
||||
public class PrefabGridItemData : InfiniteScrollData
|
||||
{
|
||||
|
||||
public string Id = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 설명
|
||||
/// </summary>
|
||||
public string ItemName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 이미지 프리팹 경로
|
||||
/// </summary>
|
||||
public string ImagePrefabPath = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 객체 프리팹 경로
|
||||
/// </summary>
|
||||
public string ObjectPrefabPath = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 클릭 시 호출될 액션입니다.
|
||||
/// </summary>
|
||||
public Action<PrefabGridItemData>? OnClickAction;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UVC/UI/List/PrefabGridItemData.cs.meta
Normal file
2
Assets/Scripts/UVC/UI/List/PrefabGridItemData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51b8cb5656c93fd428f36697fe2a60d8
|
||||
@@ -1,4 +1,4 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UVC.Locale; // ButtonText의 기본값을 위해 추가
|
||||
using UVC.Log; // ULog 사용 예시를 위해 추가 (필요에 따라)
|
||||
|
||||
@@ -27,17 +27,17 @@ namespace UVC.UI.Modal
|
||||
/// }
|
||||
///
|
||||
/// // 아이템 삭제 확인
|
||||
/// public async void TryDeleteItem(string itemName)
|
||||
/// public async void TryDeleteItem(string ItemName)
|
||||
/// {
|
||||
/// string title = "아이템 삭제";
|
||||
/// string message = $"정말로 '{itemName}' 아이템을 삭제하시겠습니까? 이 행동은 되돌릴 수 없습니다.";
|
||||
/// string message = $"정말로 '{ItemName}' 아이템을 삭제하시겠습니까? 이 행동은 되돌릴 수 없습니다.";
|
||||
///
|
||||
/// // 기본 버튼 텍스트 사용 (ModalContent에서 설정된 "확인", "취소")
|
||||
/// bool confirmed = await Confirm.Show(title, message);
|
||||
///
|
||||
/// if (confirmed)
|
||||
/// {
|
||||
/// ULog.Debug($"'{itemName}' 아이템 삭제를 진행합니다.");
|
||||
/// ULog.Debug($"'{ItemName}' 아이템 삭제를 진행합니다.");
|
||||
/// // (아이템 삭제 로직...)
|
||||
/// }
|
||||
/// }
|
||||
@@ -64,21 +64,21 @@ namespace UVC.UI.Modal
|
||||
/// <code>
|
||||
/// public class ShopManager : MonoBehaviour
|
||||
/// {
|
||||
/// public async void OnPurchaseItem(string itemName, int price)
|
||||
/// public async void OnPurchaseItem(string ItemName, int price)
|
||||
/// {
|
||||
/// string purchaseTitle = "구매 확인";
|
||||
/// string purchaseMessage = $"{itemName} 아이템을 {price} 골드에 구매하시겠습니까?";
|
||||
/// string purchaseMessage = $"{ItemName} 아이템을 {price} 골드에 구매하시겠습니까?";
|
||||
///
|
||||
/// bool confirmed = await Confirm.Show(purchaseTitle, purchaseMessage, "구매", "나중에");
|
||||
///
|
||||
/// if (confirmed)
|
||||
/// {
|
||||
/// ULog.Debug($"{itemName} 구매를 진행합니다.");
|
||||
/// ULog.Debug($"{ItemName} 구매를 진행합니다.");
|
||||
/// // (구매 처리 로직...)
|
||||
/// }
|
||||
/// else
|
||||
/// {
|
||||
/// ULog.Debug($"{itemName} 구매를 취소했습니다.");
|
||||
/// ULog.Debug($"{ItemName} 구매를 취소했습니다.");
|
||||
/// }
|
||||
///
|
||||
/// // 예시: 설정 초기화 전에 다국어로 확인을 받습니다.
|
||||
|
||||
Reference in New Issue
Block a user