733 lines
26 KiB
C#
733 lines
26 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
namespace UVC.UIToolkit
|
|
{
|
|
/// <summary>
|
|
/// UTKComponentList와 탭 기능을 결합하여 윈도우 형태로 제공하는 컴포넌트입니다.
|
|
///
|
|
/// <para><b>개요:</b></para>
|
|
/// <para>
|
|
/// UTKComponentTabListWindow는 UTKComponentList를 내부에 포함하고 헤더(타이틀, 닫기 버튼)와
|
|
/// 탭 버튼들을 추가한 윈도우 형태의 컴포넌트입니다. 탭을 통해 카테고리별 필터링이 가능합니다.
|
|
/// </para>
|
|
///
|
|
/// <para><b>UXML 사용 예시:</b></para>
|
|
/// <code><![CDATA[
|
|
/// <!-- UXML 파일에서 UTKComponentTabListWindow 사용 -->
|
|
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
|
/// <utk:UTKComponentTabListWindow name="tab-list-window" />
|
|
/// </ui:UXML>
|
|
/// ]]></code>
|
|
///
|
|
/// <para><b>C# 사용 예시:</b></para>
|
|
/// <code><![CDATA[
|
|
/// // 1. 윈도우 참조 획득
|
|
/// var tabWindow = root.Q<UTKComponentTabListWindow>("tab-list-window");
|
|
///
|
|
/// // 2. 윈도우 제목 및 닫기 버튼 설정
|
|
/// tabWindow.Title = "에셋 라이브러리";
|
|
/// tabWindow.ShowCloseButton = true;
|
|
///
|
|
/// // 3. 데이터 구성 - 카테고리별로 자동 탭 생성됨
|
|
/// var data = new List<UTKComponentListItemDataBase>
|
|
/// {
|
|
/// // 첫 번째 카테고리 → "캐릭터" 탭 자동 생성
|
|
/// new UTKComponentListCategoryData
|
|
/// {
|
|
/// name = "캐릭터",
|
|
/// isExpanded = true,
|
|
/// children = new List<UTKComponentListItemDataBase>
|
|
/// {
|
|
/// new UTKComponentListItemData { name = "플레이어", ExternalKey = "player" },
|
|
/// new UTKComponentListItemData { name = "NPC", ExternalKey = "npc" }
|
|
/// }
|
|
/// },
|
|
/// // 두 번째 카테고리 → "오브젝트" 탭 자동 생성
|
|
/// new UTKComponentListCategoryData
|
|
/// {
|
|
/// name = "오브젝트",
|
|
/// isExpanded = true,
|
|
/// children = new List<UTKComponentListItemDataBase>
|
|
/// {
|
|
/// new UTKComponentListItemData { name = "상자", ExternalKey = "box" },
|
|
/// new UTKComponentListItemData { name = "나무", ExternalKey = "tree" }
|
|
/// }
|
|
/// }
|
|
/// };
|
|
/// tabWindow.SetData(data);
|
|
/// // → "All" 탭과 "캐릭터", "오브젝트" 탭이 자동 생성됨
|
|
///
|
|
/// // 4. 탭 선택 (프로그래밍 방식)
|
|
/// tabWindow.SelectTab(-1); // -1 = "All" 전체 탭
|
|
/// tabWindow.SelectTab(0); // 0 = 첫 번째 카테고리 ("캐릭터")
|
|
/// tabWindow.SelectTab(1); // 1 = 두 번째 카테고리 ("오브젝트")
|
|
///
|
|
/// // 5. 선택 이벤트 구독
|
|
/// tabWindow.OnItemSelected = (selectedItems) =>
|
|
/// {
|
|
/// foreach (var item in selectedItems)
|
|
/// {
|
|
/// Debug.Log($"선택됨: {item.name}");
|
|
/// }
|
|
/// };
|
|
///
|
|
/// // 6. 선택 해제 이벤트
|
|
/// tabWindow.OnItemDeselected = (deselectedItems) =>
|
|
/// {
|
|
/// foreach (var item in deselectedItems)
|
|
/// {
|
|
/// Debug.Log($"선택 해제: {item.name}");
|
|
/// }
|
|
/// };
|
|
///
|
|
/// // 7. 가시성 변경 이벤트 (눈 아이콘)
|
|
/// tabWindow.OnItemVisibilityChanged += (item, isVisible) =>
|
|
/// {
|
|
/// var gameObject = FindGameObjectByKey(item.ExternalKey);
|
|
/// if (gameObject != null)
|
|
/// {
|
|
/// gameObject.SetActive(isVisible);
|
|
/// }
|
|
/// };
|
|
///
|
|
/// // 8. 삭제 이벤트 (Delete/Backspace 키)
|
|
/// tabWindow.EnabledDeleteItem = true;
|
|
/// tabWindow.OnItemDeleted = (item) =>
|
|
/// {
|
|
/// Debug.Log($"삭제 요청: {item.name}");
|
|
/// tabWindow.DeleteItem(item); // 탭 목록도 자동 갱신됨
|
|
/// };
|
|
///
|
|
/// // 9. 더블클릭 이벤트
|
|
/// tabWindow.OnItemDoubleClicked = (item) =>
|
|
/// {
|
|
/// FocusCameraOn(item.ExternalKey);
|
|
/// };
|
|
///
|
|
/// // 10. 윈도우 닫힘 이벤트
|
|
/// tabWindow.OnClosed += () =>
|
|
/// {
|
|
/// Debug.Log("윈도우가 닫혔습니다.");
|
|
/// };
|
|
///
|
|
/// // 11. 프로그래밍 방식 선택
|
|
/// tabWindow.SelectItem("플레이어", notify: true);
|
|
/// tabWindow.DeselectItem("플레이어", notify: false);
|
|
/// tabWindow.ClearSelection();
|
|
///
|
|
/// // 12. 아이템 추가/삭제 (탭 자동 갱신)
|
|
/// var newCategory = new UTKComponentListCategoryData { name = "이펙트" };
|
|
/// tabWindow.AddItem(newCategory); // "이펙트" 탭 자동 생성
|
|
///
|
|
/// var newItem = new UTKComponentListItemData { name = "폭발" };
|
|
/// tabWindow.AddItem(newCategory, newItem); // 카테고리에 아이템 추가
|
|
///
|
|
/// tabWindow.DeleteItem(newItem); // 아이템 삭제
|
|
///
|
|
/// // 13. 윈도우 표시
|
|
/// tabWindow.Show();
|
|
///
|
|
/// // 14. 리소스 해제 (OnDestroy에서 호출)
|
|
/// tabWindow.Dispose();
|
|
/// ]]></code>
|
|
///
|
|
/// <para><b>탭 동작 설명:</b></para>
|
|
/// <list type="bullet">
|
|
/// <item>"All" 탭: 모든 카테고리와 아이템을 표시합니다.</item>
|
|
/// <item>카테고리 탭: 해당 카테고리의 자식 아이템만 표시합니다 (부모 카테고리 없이).</item>
|
|
/// <item>탭별 검색어가 저장되어, 탭 전환 시 해당 탭의 검색어가 복원됩니다.</item>
|
|
/// </list>
|
|
/// </summary>
|
|
[UxmlElement]
|
|
public partial class UTKComponentTabListWindow : VisualElement, IDisposable
|
|
{
|
|
#region IDisposable
|
|
private bool _disposed = false;
|
|
#endregion
|
|
|
|
#region 상수 (Constants)
|
|
/// <summary>메인 UXML 파일 경로 (Resources 폴더 기준)</summary>
|
|
private const string UXML_PATH = "UIToolkit/Window/UTKComponentTabListWindow";
|
|
|
|
/// <summary>USS 파일 경로 (Resources 폴더 기준)</summary>
|
|
private const string USS_PATH = "UIToolkit/Window/UTKComponentListWindowUss";
|
|
|
|
/// <summary>"전체" 탭을 나타내는 인덱스</summary>
|
|
private const int ALL_TAB_INDEX = -1;
|
|
#endregion
|
|
|
|
#region UI 컴포넌트 참조 (UI Component References)
|
|
/// <summary>내부 UTKComponentList 컴포넌트</summary>
|
|
private UTKComponentList? _componentList;
|
|
|
|
/// <summary>탭 버튼 컨테이너</summary>
|
|
private VisualElement? _tabContainer;
|
|
|
|
/// <summary>트리 리스트 닫기 버튼 (UTKButton)</summary>
|
|
private UTKButton? _closeButton;
|
|
|
|
/// <summary>윈도우 제목 라벨</summary>
|
|
private Label? _titleLabel;
|
|
#endregion
|
|
|
|
#region 탭 관련 데이터 (Tab Data)
|
|
/// <summary>원본 전체 데이터</summary>
|
|
private List<UTKComponentListItemDataBase> _originalRoots = new();
|
|
|
|
/// <summary>현재 선택된 탭 인덱스 (-1: 전체)</summary>
|
|
private int _selectedTabIndex = ALL_TAB_INDEX;
|
|
|
|
/// <summary>탭 버튼들</summary>
|
|
private List<Button> _tabButtons = new();
|
|
|
|
/// <summary>탭별 검색어 저장 (key: 탭 인덱스, value: 검색어)</summary>
|
|
private Dictionary<int, string> _tabSearchQueries = new();
|
|
#endregion
|
|
|
|
#region 공개 속성 (Public Properties)
|
|
/// <summary>
|
|
/// 항목 삭제 기능 활성화 여부입니다.
|
|
/// true일 때만 Delete/Backspace 키로 항목 삭제 이벤트가 발생합니다.
|
|
/// 기본값은 false입니다.
|
|
/// </summary>
|
|
public bool EnabledDeleteItem
|
|
{
|
|
get => _componentList?.EnabledDeleteItem ?? false;
|
|
set { if (_componentList != null) _componentList.EnabledDeleteItem = value; }
|
|
}
|
|
|
|
/// <summary>윈도우 제목을 가져오거나 설정합니다.</summary>
|
|
public string Title
|
|
{
|
|
get => _titleLabel?.text ?? string.Empty;
|
|
set { if (_titleLabel != null) _titleLabel.text = value; }
|
|
}
|
|
|
|
/// <summary>닫기 버튼 표시 여부를 설정합니다.</summary>
|
|
public bool ShowCloseButton
|
|
{
|
|
get => _closeButton?.style.display == DisplayStyle.Flex;
|
|
set { if (_closeButton != null) _closeButton.style.display = value ? DisplayStyle.Flex : DisplayStyle.None; }
|
|
}
|
|
#endregion
|
|
|
|
#region 외부 이벤트 (Public Events)
|
|
/// <summary>
|
|
/// 메인/검색 리스트에서 항목이 선택될 때 발생합니다.
|
|
/// </summary>
|
|
public Action<List<UTKComponentListItemDataBase>>? OnItemSelected
|
|
{
|
|
get => _componentList?.OnItemSelected;
|
|
set { if (_componentList != null) _componentList.OnItemSelected = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다.
|
|
/// </summary>
|
|
public Action<List<UTKComponentListItemDataBase>>? OnItemDeselected
|
|
{
|
|
get => _componentList?.OnItemDeselected;
|
|
set { if (_componentList != null) _componentList.OnItemDeselected = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다.
|
|
/// </summary>
|
|
public event Action<UTKComponentListItemDataBase, bool>? OnItemVisibilityChanged
|
|
{
|
|
add { if (_componentList != null) _componentList.OnItemVisibilityChanged += value; }
|
|
remove { if (_componentList != null) _componentList.OnItemVisibilityChanged -= value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키).
|
|
/// </summary>
|
|
public Action<UTKComponentListItemDataBase>? OnItemDeleted
|
|
{
|
|
get => _componentList?.OnItemDeleted;
|
|
set { if (_componentList != null) _componentList.OnItemDeleted = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다.
|
|
/// </summary>
|
|
public Action<UTKComponentListItemDataBase>? OnItemDoubleClicked
|
|
{
|
|
get => _componentList?.OnItemDoubleClicked;
|
|
set { if (_componentList != null) _componentList.OnItemDoubleClicked = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 아이콘을 클릭할 때 발생합니다.
|
|
/// </summary>
|
|
public Action<string, UTKComponentListItemDataBase>? OnItemIconClicked
|
|
{
|
|
get => _componentList?.OnItemIconClicked;
|
|
set { if (_componentList != null) _componentList.OnItemIconClicked = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 트리 리스트가 닫힐 때(숨겨질 때) 발생합니다.
|
|
/// 닫기 버튼 클릭 시 트리거됩니다.
|
|
/// </summary>
|
|
public event Action? OnClosed;
|
|
#endregion
|
|
|
|
#region 생성자 (Constructor)
|
|
/// <summary>
|
|
/// UTKComponentTabListWindow 컴포넌트를 초기화합니다.
|
|
/// UXML 템플릿을 로드하고 내부 UTKComponentList와 탭을 설정합니다.
|
|
/// </summary>
|
|
public UTKComponentTabListWindow()
|
|
{
|
|
// 1. 메인 UXML 로드 및 복제
|
|
var visualTree = Resources.Load<VisualTreeAsset>(UXML_PATH);
|
|
if (visualTree == null)
|
|
{
|
|
Debug.LogError($"[UTKComponentTabListWindow] UXML not found at: {UXML_PATH}");
|
|
return;
|
|
}
|
|
visualTree.CloneTree(this);
|
|
|
|
// 2. 내부 UTKComponentList 찾기 (UXML의 커스텀 요소로 생성된 컴포넌트)
|
|
_componentList = this.Q<UTKComponentList>();
|
|
if (_componentList == null)
|
|
{
|
|
Debug.LogError("[UTKComponentTabListWindow] UTKComponentList not found in UXML");
|
|
return;
|
|
}
|
|
|
|
// 3. 테마 적용 및 변경 구독
|
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
|
SubscribeToThemeChanges();
|
|
|
|
// USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨)
|
|
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
|
if (uss != null)
|
|
{
|
|
styleSheets.Add(uss);
|
|
}
|
|
|
|
// 4. 탭 관련 요소 찾기
|
|
_tabContainer = this.Q<VisualElement>("tab-container");
|
|
|
|
// 5. 헤더 요소 참조
|
|
_titleLabel = this.Q<Label>("title");
|
|
_closeButton = this.Q<UTKButton>("close-btn");
|
|
|
|
// 6. 닫기 버튼 설정 및 이벤트 연결
|
|
if (_closeButton != null)
|
|
{
|
|
_closeButton.SetMaterialIcon(UTKMaterialIcons.Close, 16);
|
|
_closeButton.OnClicked += OnCloseButtonClicked;
|
|
}
|
|
}
|
|
|
|
/// <summary>닫기 버튼 클릭 이벤트 핸들러</summary>
|
|
private void OnCloseButtonClicked()
|
|
{
|
|
this.style.display = DisplayStyle.None;
|
|
OnClosed?.Invoke();
|
|
}
|
|
#endregion
|
|
|
|
#region 공개 메서드 (Public Methods)
|
|
/// <summary>
|
|
/// 트리 리스트를 화면에 표시합니다.
|
|
/// </summary>
|
|
public void Show()
|
|
{
|
|
this.style.display = DisplayStyle.Flex;
|
|
_componentList?.Show();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 트리 데이터를 설정합니다.
|
|
/// 데이터의 카테고리를 분석하여 탭을 자동 생성합니다.
|
|
/// </summary>
|
|
/// <param name="roots">루트 항목들의 리스트</param>
|
|
public void SetData(List<UTKComponentListItemDataBase> roots)
|
|
{
|
|
_originalRoots = roots ?? new List<UTKComponentListItemDataBase>();
|
|
|
|
// 탭 생성 (카테고리 기반)
|
|
CreateTabs();
|
|
|
|
// 전체 탭 선택 (기본값)
|
|
SelectTab(ALL_TAB_INDEX);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 탭을 선택하고 해당 데이터를 TreeView에 표시합니다.
|
|
/// 이전 탭의 검색어를 저장하고, 새 탭의 검색어를 복원합니다.
|
|
/// </summary>
|
|
/// <param name="tabIndex">선택할 탭 인덱스 (-1: 전체, 0~: 카테고리)</param>
|
|
public void SelectTab(int tabIndex)
|
|
{
|
|
// 1. 이전 탭의 검색어 저장
|
|
if (_componentList != null)
|
|
{
|
|
var currentQuery = _componentList.SearchQuery;
|
|
if (!string.IsNullOrEmpty(currentQuery))
|
|
{
|
|
_tabSearchQueries[_selectedTabIndex] = currentQuery;
|
|
}
|
|
else
|
|
{
|
|
_tabSearchQueries.Remove(_selectedTabIndex);
|
|
}
|
|
}
|
|
|
|
// 2. 탭 인덱스 변경 및 스타일 업데이트
|
|
_selectedTabIndex = tabIndex;
|
|
UpdateTabStyles();
|
|
|
|
// 3. 데이터 필터링 및 표시
|
|
var filteredData = FilterDataByTab(tabIndex);
|
|
_componentList?.SetData(filteredData);
|
|
|
|
// 4. 새 탭의 검색어 복원 및 적용
|
|
if (_componentList != null)
|
|
{
|
|
if (_tabSearchQueries.TryGetValue(tabIndex, out var savedQuery) && !string.IsNullOrEmpty(savedQuery))
|
|
{
|
|
_componentList.ApplySearch(savedQuery);
|
|
}
|
|
else
|
|
{
|
|
_componentList.ApplySearch(string.Empty);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정된 ID의 항목을 프로그래밍 방식으로 선택합니다.
|
|
/// </summary>
|
|
/// <param name="itemId">선택할 항목의 ID</param>
|
|
public void SelectByItemId(int itemId)
|
|
{
|
|
_componentList?.SelectByItemId(itemId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정된 이름 목록에 해당하는 항목만 표시하고 나머지는 숨깁니다.
|
|
/// </summary>
|
|
/// <param name="items">표시할 항목들의 이름 목록</param>
|
|
/// <param name="depth">검색 깊이</param>
|
|
public void ShowItems(List<string> items, int depth = 1)
|
|
{
|
|
_componentList?.ShowItems(items, depth);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 루트 레벨에 새 항목을 추가합니다.
|
|
/// </summary>
|
|
/// <param name="data">추가할 항목 데이터</param>
|
|
public void AddItem(UTKComponentListItemDataBase data)
|
|
{
|
|
_originalRoots.Add(data);
|
|
CreateTabs();
|
|
SelectTab(_selectedTabIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정된 부모 카테고리의 자식으로 새 항목을 추가합니다.
|
|
/// </summary>
|
|
/// <param name="parent">부모 카테고리</param>
|
|
/// <param name="data">추가할 항목 데이터</param>
|
|
public void AddItem(UTKComponentListCategoryData? parent, UTKComponentListItemDataBase data)
|
|
{
|
|
if (parent == null)
|
|
{
|
|
AddItem(data);
|
|
return;
|
|
}
|
|
|
|
parent.Add(data);
|
|
|
|
// 탭 및 표시 갱신
|
|
CreateTabs();
|
|
SelectTab(_selectedTabIndex);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 트리에서 항목을 완전히 삭제합니다.
|
|
/// </summary>
|
|
/// <param name="data">삭제할 항목 데이터</param>
|
|
public void DeleteItem(UTKComponentListItemDataBase data)
|
|
{
|
|
if (RemoveItemFromRoots(data))
|
|
{
|
|
CreateTabs();
|
|
SelectTab(_selectedTabIndex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 항목의 이름을 변경하고 UI를 갱신합니다.
|
|
/// </summary>
|
|
/// <param name="data">변경할 항목 데이터</param>
|
|
/// <param name="newName">새 이름</param>
|
|
public void SetItemName(UTKComponentListItemDataBase data, string newName)
|
|
{
|
|
_componentList?.SetItemName(data, newName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이름으로 항목을 찾아 선택합니다.
|
|
/// </summary>
|
|
/// <param name="name">선택할 항목의 이름</param>
|
|
/// <param name="notify">선택 이벤트 발송 여부</param>
|
|
public void SelectItem(string name, bool notify = true)
|
|
{
|
|
_componentList?.SelectItem(name, notify);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이름으로 항목을 찾아 선택을 해제합니다.
|
|
/// </summary>
|
|
/// <param name="name">선택 해제할 항목의 이름</param>
|
|
/// <param name="notify">선택 해제 이벤트 발송 여부</param>
|
|
public void DeselectItem(string name, bool notify = true)
|
|
{
|
|
_componentList?.DeselectItem(name, notify);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모든 항목의 선택을 해제합니다.
|
|
/// </summary>
|
|
public void ClearSelection()
|
|
{
|
|
_componentList?.ClearSelection();
|
|
}
|
|
#endregion
|
|
|
|
#region 탭 관리 (Tab Management)
|
|
/// <summary>
|
|
/// 데이터의 카테고리를 분석하여 탭 버튼을 생성합니다.
|
|
/// </summary>
|
|
private void CreateTabs()
|
|
{
|
|
if (_tabContainer == null) return;
|
|
|
|
// 기존 탭 제거
|
|
_tabContainer.Clear();
|
|
_tabButtons.Clear();
|
|
|
|
// "전체" 탭 생성
|
|
var allTab = CreateTabButton("All", ALL_TAB_INDEX);
|
|
_tabContainer.Add(allTab);
|
|
_tabButtons.Add(allTab);
|
|
|
|
// 카테고리별 탭 생성 (카테고리 타입인 항목들만 탭으로 표시)
|
|
var categories = _originalRoots
|
|
.Where(r => r.IsCategory && r.children.Count > 0)
|
|
.Select(r => r.name)
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
for (int i = 0; i < categories.Count; i++)
|
|
{
|
|
var categoryTab = CreateTabButton(categories[i], i);
|
|
_tabContainer.Add(categoryTab);
|
|
_tabButtons.Add(categoryTab);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 탭 버튼을 생성합니다.
|
|
/// </summary>
|
|
/// <param name="label">탭 레이블</param>
|
|
/// <param name="index">탭 인덱스</param>
|
|
/// <returns>생성된 버튼</returns>
|
|
private Button CreateTabButton(string label, int index)
|
|
{
|
|
var button = new Button(() => SelectTab(index))
|
|
{
|
|
text = label
|
|
};
|
|
button.AddToClassList("tab-button");
|
|
return button;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 선택된 탭의 스타일을 업데이트합니다.
|
|
/// </summary>
|
|
private void UpdateTabStyles()
|
|
{
|
|
for (int i = 0; i < _tabButtons.Count; i++)
|
|
{
|
|
var btn = _tabButtons[i];
|
|
// 인덱스 0 = "전체" 탭 (ALL_TAB_INDEX = -1)
|
|
// 인덱스 1~ = 카테고리 탭 (0~)
|
|
int tabIndex = i == 0 ? ALL_TAB_INDEX : i - 1;
|
|
|
|
if (tabIndex == _selectedTabIndex)
|
|
{
|
|
btn.AddToClassList("tab-button-selected");
|
|
}
|
|
else
|
|
{
|
|
btn.RemoveFromClassList("tab-button-selected");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 선택된 탭에 따라 데이터를 필터링합니다.
|
|
/// </summary>
|
|
/// <param name="tabIndex">탭 인덱스 (-1: 전체)</param>
|
|
/// <returns>필터링된 데이터</returns>
|
|
private List<UTKComponentListItemDataBase> FilterDataByTab(int tabIndex)
|
|
{
|
|
// 전체 탭이면 모든 데이터 반환
|
|
if (tabIndex == ALL_TAB_INDEX)
|
|
{
|
|
return _originalRoots;
|
|
}
|
|
|
|
// 카테고리 탭이면 해당 카테고리의 자식들만 반환 (부모 없이 자식만 표시)
|
|
var categories = _originalRoots
|
|
.Where(r => r.IsCategory && r.children.Count > 0)
|
|
.ToList();
|
|
|
|
if (tabIndex >= 0 && tabIndex < categories.Count)
|
|
{
|
|
return categories[tabIndex].children ?? new List<UTKComponentListItemDataBase>();
|
|
}
|
|
|
|
return _originalRoots;
|
|
}
|
|
#endregion
|
|
|
|
#region 내부 헬퍼 메서드 (Internal Helper Methods)
|
|
/// <summary>
|
|
/// 원본 데이터에서 항목을 재귀적으로 삭제합니다.
|
|
/// </summary>
|
|
/// <param name="target">삭제할 항목</param>
|
|
/// <returns>삭제 성공 여부</returns>
|
|
private bool RemoveItemFromRoots(UTKComponentListItemDataBase target)
|
|
{
|
|
if (_originalRoots.Remove(target))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
foreach (var root in _originalRoots)
|
|
{
|
|
if (RemoveFromChildren(root, target))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 부모 노드의 자식들에서 대상 항목을 재귀적으로 삭제합니다.
|
|
/// </summary>
|
|
/// <param name="parent">부모 노드</param>
|
|
/// <param name="target">삭제할 대상 항목</param>
|
|
/// <returns>삭제 성공 여부</returns>
|
|
private bool RemoveFromChildren(UTKComponentListItemDataBase parent, UTKComponentListItemDataBase target)
|
|
{
|
|
if (parent.children == null || parent.children.Count == 0) return false;
|
|
|
|
// 카테고리인 경우에만 자식에서 삭제 가능
|
|
if (parent is UTKComponentListCategoryData category)
|
|
{
|
|
if (category.Remove(target))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
foreach (var child in parent.children)
|
|
{
|
|
if (RemoveFromChildren(child, target))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endregion
|
|
|
|
#region 테마 (Theme)
|
|
|
|
private void SubscribeToThemeChanges()
|
|
{
|
|
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
|
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
|
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
|
}
|
|
|
|
private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
|
|
{
|
|
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
|
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
|
}
|
|
|
|
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt)
|
|
{
|
|
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
|
}
|
|
|
|
private void OnThemeChanged(UTKTheme theme)
|
|
{
|
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
/// <summary>
|
|
/// 리소스를 해제하고 이벤트 핸들러를 정리합니다.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
// 테마 변경 이벤트 해제
|
|
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
|
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
|
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
|
|
|
// 내부 UTKComponentList 정리
|
|
_componentList?.Dispose();
|
|
_componentList = null;
|
|
|
|
// 탭 버튼 정리
|
|
_tabButtons.Clear();
|
|
_tabContainer = null;
|
|
|
|
// 닫기 버튼 이벤트 해제 및 정리
|
|
if (_closeButton != null)
|
|
{
|
|
_closeButton.OnClicked -= OnCloseButtonClicked;
|
|
_closeButton.Dispose();
|
|
}
|
|
|
|
// 외부 이벤트 구독자 정리
|
|
OnClosed = null;
|
|
|
|
// 데이터 정리
|
|
_originalRoots.Clear();
|
|
_tabSearchQueries.Clear();
|
|
|
|
// UI 참조 정리
|
|
_closeButton = null;
|
|
_titleLabel = null;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|