#nullable enable
using DG.Tweening;
using Gpm.Ui;
using UnityEngine;
using UnityEngine.UI;
namespace UVC.UI.List.ComponentList
{
///
/// InfiniteScroll에 표시될 개별 아이템을 정의하는 클래스입니다.
/// 이 아이템은 '카테고리' 또는 '일반 항목' 두 가지 상태를 가질 수 있으며,
/// ComponentListItemData의 isCategory 값에 따라 UI가 동적으로 변경됩니다.
///
public class ComponentListItem : InfiniteScrollItem
{
// ComponentList 참조
private ComponentList? _componentList;
// [카테고리 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;
private void Awake()
{
// ComponentList 참조 캐싱
if (_componentList == null)
{
_componentList = GetComponentInParent();
}
}
///
/// InfiniteScroll에 의해 데이터가 이 아이템에 할당될 때 호출되는 핵심 메서드입니다.
/// 스크롤 시 아이템이 재활용될 때마다 새로운 데이터로 이 메서드가 호출되어 UI를 갱신합니다.
///
/// 이 아이템에 표시할 데이터입니다. `InfiniteScrollData`를 상속받은 `ComponentListItemData` 객체입니다.
///
///
/// // 1. 표시할 데이터를 생성합니다.
/// var categoryData = new ComponentListItemData
/// {
/// isCategory = true,
/// categoryName = "전기 설비",
/// categoryBadgeCount = 10
/// };
///
/// var generalData = new ComponentListItemData
/// {
/// isCategory = false,
/// generalName = "분전반",
/// generalOption = "옵션 A",
/// Id = "some-unique-id"
/// };
///
/// // 2. 생성한 데이터를 InfiniteScroll에 추가합니다.
/// infiniteScroll.InsertData(categoryData);
/// infiniteScroll.InsertData(generalData);
///
///
public override void UpdateData(InfiniteScrollData scrollData)
{
// 1. 부모 클래스의 UpdateData를 호출하여 기본 초기화 작업을 수행합니다.
// 이 과정에서 this.scrollData에 매개변수로 받은 scrollData가 할당됩니다.
base.UpdateData(scrollData);
// InfiniteScroll 아이템 재활용 시 애니메이션 상태 리셋
isAnimating = false;
// 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();
// 애니메이션을 위해 현재 각도에서 목표 각도로 회전시킵니다.
categoryExtendButton.transform.DOKill(); // 이전 애니메이션이 있으면 종료
categoryExtendButton.transform.DORotate(new Vector3(0, 0, itemData.IsExpanded ? 0 : 90), 0.0f);
}
else
{
// 일반 아이템일 경우
generalText.text = itemData.Name;
generalOptionText.text = itemData.Option;
// 버튼 상태는 외부 이벤트 핸들러에서 처리
// 기본값: 숨기기 버튼 표시 (객체가 보이는 상태로 가정)
showButton.gameObject.SetActive(false);
hideButton.gameObject.SetActive(true);
}
}
public virtual void OnClickItem()
{
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleClickItem(itemData);
}
}
public virtual void OnRightClickItem()
{
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleRightClickItem(itemData);
}
}
///
/// 카테고리 아이템의 '확장/축소' 버튼을 클릭했을 때 호출됩니다.
///
public virtual void OnExpendButtonClick()
{
// 애니메이션이 진행 중이면 중복 호출을 방지합니다.
if (isAnimating) return;
isAnimating = true;
ComponentListItemData itemData = (ComponentListItemData)scrollData;
// 토글 후의 상태를 기준으로 목표 각도 계산
// 현재 isExpanded=true(펼쳐진 상태)이면 접을 것이므로 90도로 (화살표 우측 방향)
// 현재 isExpanded=false(접힌 상태)이면 펼칠 것이므로 0도로 (화살표 아래 방향)
float targetAngle = itemData.IsExpanded ? 90 : 0;
// 이전 애니메이션이 있으면 종료
categoryExtendButton.transform.DOKill();
categoryExtendButton.transform.DORotate(new Vector3(0, 0, targetAngle), 0.3f)
.SetUpdate(true) // TimeScale 영향 받지 않도록 설정
.OnComplete(() =>
{
isAnimating = false;
});
// ComponentList의 이벤트를 통해 확장/축소 처리
if (_componentList != null)
{
_componentList.HandleCategoryExpand(itemData);
}
}
///
/// 카테고리 아이템의 '설정' 버튼을 클릭했을 때 호출됩니다.
///
public virtual void OnSettingButtonClick()
{
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleSettingButtonClick(itemData);
}
}
///
/// 일반 아이템의 '보이기' 버튼을 클릭했을 때 호출됩니다.
///
public virtual void OnShowButtonClick()
{
showButton.gameObject.SetActive(false);
hideButton.gameObject.SetActive(true);
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleShowButtonClick(itemData);
}
}
///
/// 일반 아이템의 '숨기기' 버튼을 클릭했을 때 호출됩니다.
///
public virtual void OnHideButtonClick()
{
showButton.gameObject.SetActive(true);
hideButton.gameObject.SetActive(false);
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleHideButtonClick(itemData);
}
}
///
/// 일반 아이템의 '찾기/이동' 버튼을 클릭했을 때 호출됩니다.
///
public virtual void OnSearchButtonClick()
{
ComponentListItemData itemData = (ComponentListItemData)scrollData;
if (_componentList != null)
{
_componentList.HandleSearchButtonClick(itemData);
}
}
protected virtual void OnDestroy()
{
// 애니메이션 정리
if (categoryExtendButton != null)
{
categoryExtendButton.transform.DOKill();
}
// 참조 정리
_componentList = null;
// UI 참조 정리
categoryRoot = null!;
categoryText = null!;
categoryBageText = null!;
categoryExtendButton = null!;
generalRoot = null!;
generalText = null!;
generalOptionText = null!;
showButton = null!;
hideButton = null!;
}
}
}