1차 완료
This commit is contained in:
8
Assets/Scripts/UVC/UI/List/ComponentList.meta
Normal file
8
Assets/Scripts/UVC/UI/List/ComponentList.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b65468caabd07e542b9de8377a34315e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
367
Assets/Scripts/UVC/UI/List/ComponentList/ComponentList.cs
Normal file
367
Assets/Scripts/UVC/UI/List/ComponentList/ComponentList.cs
Normal file
@@ -0,0 +1,367 @@
|
||||
#nullable enable
|
||||
using Gpm.Ui;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UVC.Core;
|
||||
using UVC.Extention;
|
||||
using UVC.Factory;
|
||||
using UVC.Factory.Component;
|
||||
using UVC.Locale;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.UI.Menu;
|
||||
using UVC.UI.Modal;
|
||||
|
||||
namespace UVC.UI.List.ComponentList
|
||||
{
|
||||
/// <summary>
|
||||
/// 컴포넌트 목록 UI를 관리하는 싱글톤 클래스입니다.
|
||||
/// InfiniteScroll을 사용하여 대량의 데이터를 효율적으로 표시하고,
|
||||
/// 검색 및 필터링 기능을 제공합니다.
|
||||
/// IPointerEnterHandler, IPointerExitHandler를 구현하여
|
||||
/// 마우스가 UI 영역에 있을 때 3D 뷰의 카메라 조작을 비활성화합니다.
|
||||
/// </summary>
|
||||
public class ComponentList : SingletonScene<ComponentList>, IPointerEnterHandler, IPointerExitHandler
|
||||
{
|
||||
[Tooltip("데이터를 표시할 InfiniteScroll 컴포넌트입니다.")]
|
||||
[SerializeField]
|
||||
protected InfiniteScroll? scrollList = null;
|
||||
|
||||
[Tooltip("검색어 입력을 위한 TMP_InputField 컴포넌트입니다.")]
|
||||
[SerializeField]
|
||||
protected TMP_InputField? inputField = null;
|
||||
|
||||
// InfiniteScroll에 표시될 원본 데이터 리스트입니다.
|
||||
protected List<ComponentListItemData>? data;
|
||||
protected List<ComponentListItemData>? filteredData;
|
||||
|
||||
/// <summary>
|
||||
/// SingletonScene 초기화 과정에서 호출됩니다.
|
||||
/// 필요한 컴포넌트를 찾고, 이벤트 리스너를 등록합니다.
|
||||
/// </summary>
|
||||
protected override void Init()
|
||||
{
|
||||
// scrollList가 인스펙터에서 할당되지 않았을 경우, 자식에서 찾아봅니다.
|
||||
if (scrollList == null)
|
||||
{
|
||||
scrollList = GetComponentInChildren<InfiniteScroll>();
|
||||
}
|
||||
|
||||
if (scrollList == null)
|
||||
{
|
||||
Debug.LogError("InfiniteScroll component is not assigned or found in children.");
|
||||
return;
|
||||
}
|
||||
|
||||
// inputField가 인스펙터에서 할당되지 않았을 경우, 자식에서 찾아봅니다.
|
||||
if (inputField == null)
|
||||
{
|
||||
inputField = GetComponentInChildren<TMP_InputField>();
|
||||
}
|
||||
if (inputField != null)
|
||||
{
|
||||
// 사용자가 검색어를 입력하고 Enter 키를 누르거나 입력을 완료했을 때 OnInputFieldChanged 메서드가 호출되도록 이벤트를 등록합니다.
|
||||
//inputField.onEndEdit.AddListener(OnInputFieldChanged);
|
||||
inputField.onSubmit.AddListener(OnInputFieldChanged);
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
if (inputField != null)
|
||||
{
|
||||
// Init에서 등록한 리스너를 제거합니다.
|
||||
//inputField.onEndEdit.RemoveListener(OnInputFieldChanged);
|
||||
inputField.onSubmit.RemoveListener(OnInputFieldChanged);
|
||||
}
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FactoryObjectManager에서 데이터를 가져와 스크롤 리스트를 설정합니다.
|
||||
/// 데이터를 카테고리별로 그룹화하고, 각 카테고리와 해당 항목들을 리스트에 추가합니다.
|
||||
/// </summary>
|
||||
public virtual void SetupData()
|
||||
{
|
||||
if (scrollList == null)
|
||||
{
|
||||
Debug.LogError("InfiniteScroll component is not assigned.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(inputField != null) inputField.text = string.Empty;
|
||||
|
||||
//transform.SetAsFirstSibling();
|
||||
scrollList.ClearData(); // 스크롤 리스트의 기존 데이터를 모두 비웁니다.
|
||||
|
||||
data?.Clear();
|
||||
data = new List<ComponentListItemData>();
|
||||
|
||||
// 1. FactoryObjectManager에서 카테고리별로 그룹화된 객체 정보를 가져옵니다.
|
||||
var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
|
||||
|
||||
// 2. 각 카테고리에 대해 루프를 돕니다.
|
||||
foreach (var info in infos)
|
||||
{
|
||||
// 2-1. 카테고리 아이템 데이터를 추가합니다.
|
||||
data.Add(new ComponentListItemData
|
||||
{
|
||||
isCategory = true,
|
||||
categoryName = info.Key, // 카테고리 이름 (예: "전기 설비")
|
||||
categoryBadgeCount = info.Value.Count, // 해당 카테고리의 항목 수
|
||||
OnCategoryExpendAction = ToggleCategory // 액션 할당
|
||||
});
|
||||
|
||||
// 2-2. 해당 카테고리에 속한 모든 일반 항목 데이터를 추가합니다.
|
||||
foreach (var item in info.Value)
|
||||
{
|
||||
data.Add(new ComponentListItemData
|
||||
{
|
||||
isCategory = false,
|
||||
generalName = item.Name, // 항목 이름 (예: "분전반")
|
||||
generalOption = item.Floor, // 항목 옵션 (예: "1F")
|
||||
factoryObjectInfo = item // 이 항목이 속한 카테고리 이름 (필터링을 위해 필요)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 완성된 데이터 리스트를 InfiniteScroll에 한 번에 삽입하여 UI를 업데이트합니다.
|
||||
scrollList.InsertData(data.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리의 확장/축소 상태를 토글하고, 그에 따라 자식 아이템들의 표시 여부를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <param name="categoryData">상태를 변경할 카테고리 데이터</param>
|
||||
public virtual void ToggleCategory(ComponentListItemData categoryData)
|
||||
{
|
||||
if (scrollList == null || data == null) return;
|
||||
|
||||
// 1. 현재 스크롤 리스트에서 토글할 카테고리의 인덱스를 찾습니다.
|
||||
int categoryIndex = -1;
|
||||
for (int i = 0; i < scrollList.GetDataCount(); i++)
|
||||
{
|
||||
if (scrollList.GetData(i) == categoryData)
|
||||
{
|
||||
categoryIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (categoryIndex == -1) return; // 카테고리를 찾지 못하면 중단
|
||||
|
||||
List<ComponentListItemData> list = filteredData != null ? filteredData : data;
|
||||
|
||||
// 2. 카테고리의 확장 상태를 변경합니다.
|
||||
categoryData.isExpanded = !categoryData.isExpanded;
|
||||
|
||||
// 3. 확장/축소 상태에 따라 자식 아이템을 추가하거나 제거합니다.
|
||||
if (categoryData.isExpanded) // 카테고리 확장
|
||||
{
|
||||
// 원본 데이터 리스트에서 이 카테고리에 속한 자식들을 찾습니다.
|
||||
int originalDataIndex = list.IndexOf(categoryData);
|
||||
if (originalDataIndex != -1)
|
||||
{
|
||||
var childrenToAdd = list.Skip(originalDataIndex + 1)
|
||||
.Take(categoryData.categoryBadgeCount)
|
||||
.ToArray();
|
||||
|
||||
// 카테고리 바로 다음 위치에 자식 아이템들을 삽입합니다.
|
||||
if (childrenToAdd.Length > 0)
|
||||
{
|
||||
scrollList.InsertData(childrenToAdd, categoryIndex + 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // 카테고리 축소
|
||||
{
|
||||
// 카테고리 바로 다음에 있는 자식 아이템들을 제거합니다.
|
||||
// 한 번에 하나씩 제거하므로, 항상 categoryIndex + 1 위치의 아이템을 제거합니다.
|
||||
for (int i = 0; i < categoryData.categoryBadgeCount; i++)
|
||||
{
|
||||
// 제거할 아이템이 리스트 범위 내에 있는지 확인
|
||||
if (categoryIndex + 1 < scrollList.GetDataCount())
|
||||
{
|
||||
scrollList.RemoveData(categoryIndex + 1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // 범위를 벗어나면 중단
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <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}");
|
||||
|
||||
// 특수 필터 키워드가 있는지 확인합니다.
|
||||
// 예: "@Category 전기" -> '전기' 카테고리 필터
|
||||
bool hasCategoryFilter = text.StartsWith("@Category ");
|
||||
bool hasAreaFilter = text.StartsWith("@Area ");
|
||||
bool hasFloorFilter = text.StartsWith("@Floor ");
|
||||
|
||||
// 원본 데이터(data)를 기반으로 필터링된 새로운 리스트를 생성합니다.
|
||||
List<ComponentListItemData> filteredItems = data.Where(itemData =>
|
||||
{
|
||||
if (itemData.isCategory) return false; // 카테고리 항목은 항상 제외
|
||||
|
||||
if (hasCategoryFilter)
|
||||
{
|
||||
string categoryText = text.Substring("@Category ".Length).Trim();
|
||||
return itemData.categoryName.Contains(categoryText, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else if (hasAreaFilter)
|
||||
{
|
||||
string areaText = text.Substring("@Area ".Length).Trim();
|
||||
return itemData.factoryObjectInfo?.Area.Contains(areaText, StringComparison.OrdinalIgnoreCase) == true;
|
||||
}
|
||||
else if (hasFloorFilter)
|
||||
{
|
||||
string floorText = text.Substring("@Floor ".Length).Trim();
|
||||
return itemData.factoryObjectInfo?.Floor.Contains(floorText, StringComparison.OrdinalIgnoreCase) == true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return itemData.generalName.Contains(text, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
if (filteredData == null)
|
||||
{
|
||||
filteredData = new List<ComponentListItemData>();
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredData.Clear();
|
||||
}
|
||||
string resultText = LocalizationManager.Instance.GetString("검색결과");
|
||||
|
||||
// 검색 결과 카테고리 아이템을 추가합니다.
|
||||
filteredData.Add(new ComponentListItemData
|
||||
{
|
||||
isCategory = true,
|
||||
categoryName = $"{resultText}: {filteredItems.Count}건",
|
||||
categoryBadgeCount = filteredItems.Count,
|
||||
isExpanded = true, // 검색 결과는 항상 펼쳐진 상태로 시작
|
||||
OnCategoryExpendAction = ToggleCategory // 액션 할당
|
||||
});
|
||||
|
||||
// 필터링된 아이템들을 결과 리스트에 추가합니다.
|
||||
filteredData.AddRange(filteredItems);
|
||||
|
||||
// 스크롤 리스트를 비우고 필터링된 결과로 새로 채웁니다.
|
||||
scrollList.Clear();
|
||||
scrollList.InsertData(filteredData.ToArray(), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 필터 버튼 클릭 시 호출됩니다.
|
||||
/// 검색에 사용할 수 있는 특수 필터 키워드를 보여주는 컨텍스트 메뉴를 표시합니다.
|
||||
/// </summary>
|
||||
public virtual void OnClickFilter()
|
||||
{
|
||||
Debug.Log("Filter button clicked.");
|
||||
|
||||
// 컨텍스트 메뉴에 표시할 항목들을 정의합니다.
|
||||
var menuItems = new List<ContextMenuItemData>
|
||||
{
|
||||
// "카테고리" 메뉴: 클릭 시 검색창에 "@Category "를 자동으로 입력해줍니다.
|
||||
// 생성자: (itemId, displayName, command, commandParameter)
|
||||
new ContextMenuItemData("Menu1", "카테고리", new ActionCommand(()=>{ if(inputField != null) { inputField.text = "@Category "; inputField.SetCaretToEndAsync().Forget();} })),
|
||||
new ContextMenuItemData(isSeparator: true), // 구분선 추가
|
||||
new ContextMenuItemData("Menu2", "구역", new ActionCommand(()=>{ if(inputField != null) { inputField.text = "@Area "; inputField.SetCaretToEndAsync().Forget();} })),
|
||||
new ContextMenuItemData(isSeparator: true), // 구분선 추가
|
||||
new ContextMenuItemData("Menu3", "층", new ActionCommand(()=>{ if(inputField != null) { inputField.text = "@Floor "; inputField.SetCaretToEndAsync().Forget(); } })),
|
||||
};
|
||||
|
||||
// ContextMenuManager를 통해 마우스 위치에 메뉴를 표시합니다.
|
||||
ContextMenuManager.Instance.ShowMenu(menuItems, Input.mousePosition);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새로고침 버튼 클릭 시 호출됩니다.
|
||||
/// 리스트의 모든 데이터를 지우고 SetupData를 다시 호출하여 목록을 갱신합니다.
|
||||
/// </summary>
|
||||
public virtual void OnClickRefresh()
|
||||
{
|
||||
if (scrollList != null && data != null)
|
||||
{
|
||||
scrollList.Clear(); // 스크롤 리스트의 내용을 비웁니다.
|
||||
SetupData(); // 데이터를 다시 설정합니다.
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 닫기 버튼 클릭 시 호출됩니다.
|
||||
/// 컴포넌트 목록 UI를 비활성화하고, 카메라 컨트롤을 다시 활성화합니다.
|
||||
/// </summary>
|
||||
public virtual void OnClickClose()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
FactoryCameraController.Instance.Enable = true; // 카메라 컨트롤러 활성화
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 검색창의 'X' 버튼 클릭 시 호출됩니다.
|
||||
/// 입력 필드를 비우고, 적용되었던 모든 필터를 제거합니다.
|
||||
/// </summary>
|
||||
public virtual void OnClickClearText()
|
||||
{
|
||||
if (inputField != null && scrollList != null && data != null)
|
||||
{
|
||||
inputField.text = string.Empty; // 입력 필드 초기화
|
||||
inputField.ActivateInputField(); // 입력 필드에 다시 포커스 설정
|
||||
// scrollList에 설정된 필터를 제거(null)하여 모든 항목이 보이도록 합니다.
|
||||
scrollList.SetFilter(null);
|
||||
scrollList.ClearData(); // 스크롤 리스트의 내용을 비웁니다.
|
||||
scrollList.InsertData(data.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 포인터가 이 UI 요소의 영역 안으로 들어왔을 때 호출됩니다.
|
||||
/// UI와 상호작용하는 동안 3D 뷰의 카메라가 움직이지 않도록 컨트롤러를 비활성화합니다.
|
||||
/// </summary>
|
||||
public virtual void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
FactoryCameraController.Instance.Enable = false; // 카메라 컨트롤러 비활성화
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 마우스 포인터가 이 UI 요소의 영역 밖으로 나갔을 때 호출됩니다.
|
||||
/// 카메라 컨트롤을 다시 활성화하여 3D 뷰를 조작할 수 있도록 합니다.
|
||||
/// </summary>
|
||||
public virtual void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
FactoryCameraController.Instance.Enable = true; // 카메라 컨트롤러 활성화
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c34cb81f79b2b644c8d42ef8d64a0753
|
||||
250
Assets/Scripts/UVC/UI/List/ComponentList/ComponentListItem.cs
Normal file
250
Assets/Scripts/UVC/UI/List/ComponentList/ComponentListItem.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
#nullable enable
|
||||
using DG.Tweening;
|
||||
using Gpm.Ui;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UVC.Factory;
|
||||
using UVC.Factory.Component;
|
||||
using UVC.Locale;
|
||||
using UVC.UI.Modal;
|
||||
|
||||
|
||||
namespace UVC.UI.List.ComponentList
|
||||
{
|
||||
/// <summary>
|
||||
/// InfiniteScroll에 표시될 개별 아이템을 정의하는 클래스입니다.
|
||||
/// 이 아이템은 '카테고리' 또는 '일반 항목' 두 가지 상태를 가질 수 있으며,
|
||||
/// ComponentListItemData의 isCategory 값에 따라 UI가 동적으로 변경됩니다.
|
||||
/// </summary>
|
||||
public class ComponentListItem : InfiniteScrollItem
|
||||
{
|
||||
// [카테고리 UI 요소]
|
||||
[Header("Category UI")]
|
||||
[SerializeField]
|
||||
protected GameObject categoryRoot; // 카테리 UI의 최상위 GameObject
|
||||
|
||||
[SerializeField]
|
||||
protected TMPro.TextMeshProUGUI categoryText; // 카테고리 이름을 표시할 텍스트
|
||||
|
||||
[SerializeField]
|
||||
protected TMPro.TextMeshProUGUI categoryBageText; // 카테고리에 속한 항목 수를 표시할 텍스트
|
||||
|
||||
[SerializeField]
|
||||
protected Button categoryExtendButton; // 카테고리 확장/축소 버튼
|
||||
|
||||
// [일반 항목 UI 요소]
|
||||
[Header("General Item UI")]
|
||||
[SerializeField]
|
||||
protected GameObject generalRoot; // 일반 항목 UI의 최상위 GameObject
|
||||
|
||||
[SerializeField]
|
||||
protected TMPro.TMP_InputField generalText; // 일반 항목의 이름을 표시할 입력 필드
|
||||
|
||||
[SerializeField]
|
||||
protected TMPro.TextMeshProUGUI generalOptionText; // 일반 항목의 옵션을 표시할 텍스트
|
||||
|
||||
[SerializeField]
|
||||
protected Button showButton; // 항목을 씬에 표시하는 버튼
|
||||
|
||||
[SerializeField]
|
||||
protected Button hideButton; // 항목을 씬에서 숨기는 버튼
|
||||
|
||||
// 애니메이션 중복 실행을 방지하기 위한 플래그
|
||||
protected bool isAnimating = false;
|
||||
|
||||
/// <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 void UpdateData(InfiniteScrollData scrollData)
|
||||
{
|
||||
// 1. 부모 클래스의 UpdateData를 호출하여 기본 초기화 작업을 수행합니다.
|
||||
// 이 과정에서 this.scrollData에 매개변수로 받은 scrollData가 할당됩니다.
|
||||
base.UpdateData(scrollData);
|
||||
|
||||
// 2. 매개변수로 받은 scrollData를 실제 사용할 데이터 타입인 ComponentListItemData로 형변환(casting)합니다.
|
||||
// 이렇게 해야 ComponentListItemData에만 정의된 속성(isCategory, categoryName 등)에 접근할 수 있습니다.
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
|
||||
// 3. 데이터의 isCategory 값에 따라 카테고리 UI 또는 일반 항목 UI를 활성화/비활성화합니다.
|
||||
categoryRoot.SetActive(itemData.isCategory);
|
||||
generalRoot.SetActive(!itemData.isCategory);
|
||||
|
||||
// 4. 활성화된 UI에 데이터를 채워 넣습니다.
|
||||
if (itemData.isCategory)
|
||||
{
|
||||
// 카테고리 아이템일 경우
|
||||
categoryText.text = itemData.categoryName;
|
||||
categoryBageText.text = itemData.categoryBadgeCount.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일반 아이템일 경우
|
||||
generalText.text = itemData.generalName;
|
||||
generalOptionText.text = itemData.generalOption;
|
||||
|
||||
// 이 아이템과 연결된 실제 씬(Scene) 상의 FactoryObject가 있는지 확인합니다.
|
||||
if (itemData.factoryObjectInfo != null)
|
||||
{
|
||||
// FactoryObjectManager를 통해 ID로 객체를 찾습니다.
|
||||
FactoryObject? obj = FactoryObjectManager.Instance.FindById(itemData.factoryObjectInfo.Id);
|
||||
if (obj != null)
|
||||
{
|
||||
// 찾은 객체의 활성화 상태에 따라 '보이기'/'숨기기' 버튼의 상태를 결정합니다.
|
||||
showButton.gameObject.SetActive(!obj.gameObject.activeSelf);
|
||||
hideButton.gameObject.SetActive(obj.gameObject.activeSelf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 아이템의 '확장/축소' 버튼을 클릭했을 때 호출됩니다.
|
||||
/// </summary>
|
||||
public virtual void OnExpendButtonClick()
|
||||
{
|
||||
// 애니메이션이 진행 중이면 중복 호출을 방지합니다.
|
||||
if (isAnimating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isAnimating = true;
|
||||
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
|
||||
// 애니메이션을 위해 현재 각도에서 목표 각도로 회전시킵니다.
|
||||
float targetAngle = itemData.isExpanded ? 90 : 0;
|
||||
categoryExtendButton.transform.DORotate(new Vector3(0, 0, targetAngle), 0.3f)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
// 애니메이션이 완료되면 플래그를 초기화합니다.
|
||||
isAnimating = false;
|
||||
});
|
||||
|
||||
// 데이터에 할당된 Action을 호출하여 상태 변경을 상위 리스트에 알립니다.
|
||||
itemData.OnCategoryExpendAction?.Invoke(itemData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 아이템의 '설정' 버튼을 클릭했을 때 호출됩니다.
|
||||
/// </summary>
|
||||
public virtual async void OnSettingButtonClick()
|
||||
{
|
||||
// 1. 이 아이템에 바인딩된 데이터를 가져옵니다.
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
|
||||
// 2. 모달이 열려있는 동안 카메라 조작을 막기 위해 컨트롤러를 비활성화합니다.
|
||||
FactoryCameraController.Instance.Enable = false; // 카메라 컨트롤러 비활성화
|
||||
|
||||
// 3. 모달에 표시할 콘텐츠를 설정합니다.
|
||||
var modalContent = new ModalContent("Prefabs/UI/Modal/SettingModal")
|
||||
{
|
||||
Title = "설정 카테고리",
|
||||
Message = $"DisplaySetting>{itemData.categoryName}"
|
||||
};
|
||||
|
||||
// 4. 설정 모달을 엽니다. await 키워드를 사용하여 모달이 닫힐 때까지 기다립니다.
|
||||
await UVC.UI.Modal.Modal.Open<object>(modalContent);
|
||||
|
||||
// 5. 모달이 닫히면 카메라 컨트롤러를 다시 활성화합니다.
|
||||
FactoryCameraController.Instance.Enable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 일반 아이템의 '보이기' 버튼을 클릭했을 때 호출됩니다.
|
||||
/// </summary>
|
||||
public virtual void OnShowButtonClick()
|
||||
{
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
if (itemData.factoryObjectInfo != null)
|
||||
{
|
||||
// ID를 사용하여 씬에서 실제 객체를 찾습니다.
|
||||
FactoryObject? obj = FactoryObjectManager.Instance.FindById(itemData.factoryObjectInfo.Id);
|
||||
if (obj != null)
|
||||
{
|
||||
// 객체를 활성화(보이게)합니다.
|
||||
obj.gameObject.SetActive(true);
|
||||
// '보이기' 버튼은 숨기고 '숨기기' 버튼을 표시합니다.
|
||||
showButton.gameObject.SetActive(false);
|
||||
hideButton.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 일반 아이템의 '숨기기' 버튼을 클릭했을 때 호출됩니다.
|
||||
/// </summary>
|
||||
public virtual void OnHideButtonClick()
|
||||
{
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
if (itemData.factoryObjectInfo != null)
|
||||
{
|
||||
// ID를 사용하여 씬에서 실제 객체를 찾습니다.
|
||||
FactoryObject? obj = FactoryObjectManager.Instance.FindById(itemData.factoryObjectInfo.Id);
|
||||
if (obj != null)
|
||||
{
|
||||
// 객체를 비활성화(숨기기)합니다.
|
||||
obj.gameObject.SetActive(false);
|
||||
|
||||
// '숨기기' 버튼은 숨기고 '보이기' 버튼을 표시합니다.
|
||||
showButton.gameObject.SetActive(true);
|
||||
hideButton.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 일반 아이템의 '찾기/이동' 버튼을 클릭했을 때 호출됩니다.
|
||||
/// </summary>
|
||||
public virtual void OnSearchButtonClick()
|
||||
{
|
||||
ComponentListItemData itemData = (ComponentListItemData)scrollData;
|
||||
if (itemData.factoryObjectInfo != null)
|
||||
{
|
||||
FactoryObject? obj = FactoryObjectManager.Instance.FindById(itemData.factoryObjectInfo.Id);
|
||||
if(obj == null)
|
||||
{
|
||||
// 객체를 찾지 못한 경우 사용자에게 알림 메시지를 표시합니다.
|
||||
Toast.Show(LocalizationManager.Instance.GetString($"{itemData.factoryObjectInfo.Name} 객체를 찾을 수 없습니다."), 2f);
|
||||
return;
|
||||
}
|
||||
|
||||
// 객체가 존재하고 활성화 상태일 때만 카메라를 이동시킵니다.
|
||||
if (obj != null && obj.gameObject.activeSelf)
|
||||
{
|
||||
// FactoryCameraController를 사용하여 해당 객체의 위치로 카메라를 부드럽게 이동시킵니다.
|
||||
FactoryCameraController.Instance.FocusOnTarget(obj.transform.position, 10f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5e7a4e53cd10a24cb35d9223f48636a
|
||||
@@ -0,0 +1,49 @@
|
||||
#nullable enable
|
||||
using Gpm.Ui;
|
||||
using System;
|
||||
using UVC.Factory.Component;
|
||||
|
||||
namespace UVC.UI.List.ComponentList
|
||||
{
|
||||
public class ComponentListItemData : InfiniteScrollData
|
||||
{
|
||||
/// <summary>
|
||||
/// 카테고리 아이템인지 여부
|
||||
/// </summary>
|
||||
public bool isCategory = false;
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 이름
|
||||
/// </summary>
|
||||
public string categoryName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 배지 개수
|
||||
/// </summary>
|
||||
public int categoryBadgeCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 일반 아이템 이름
|
||||
/// </summary>
|
||||
public string generalName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 일반 아이템 옵션
|
||||
/// </summary>
|
||||
public string generalOption = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 팩토리 객체 정보
|
||||
/// </summary>
|
||||
public FactoryObjectInfo? factoryObjectInfo = null;
|
||||
|
||||
|
||||
internal bool isExpanded = true; // 카테고리가 확장되었는지 여부
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 확장/축소 버튼 클릭 시 호출될 액션입니다.
|
||||
/// ComponentList에서 이 액션에 ToggleCategory 메서드를 할당합니다.
|
||||
/// </summary>
|
||||
public Action<ComponentListItemData>? OnCategoryExpendAction;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60c669d57267f8c45a0427a3b0d6c70e
|
||||
@@ -3,7 +3,6 @@ using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using static RTG.GizmoTransform;
|
||||
|
||||
namespace UVC.UI.List.Draggable
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user