ComponentList Tab 기능 추가

This commit is contained in:
logonkhi
2025-09-26 12:01:35 +09:00
parent 1aa64487d0
commit 52f60f3fc4
22 changed files with 94761 additions and 324418 deletions

View File

@@ -26,7 +26,7 @@ namespace UVC.Factory.Tab
public void Start()
{
// 1. TabConfig 설정
tabController?.AddTabConfig("Explorer", "탐색기", "Prefabs/UI/Tab/TabContentComponentList", "Prefabs/UI/images/icon_side_tab_explorer_128", "탐색기", true);
tabController?.AddTabConfig("Explorer", "탐색기", "Prefabs/UI/Tab/TabContentTabComponentList", "Prefabs/UI/images/icon_side_tab_explorer_128", "탐색기", true);
tabController?.AddTabConfig("Library", "라이브러리", "Prefabs/UI/Tab/TabContentSample", "Prefabs/UI/images/icon_side_tab_library_128", "라이브러리", true);
tabController?.AddTabConfig("ResourceExplorer", "자원 탐색기", "Prefabs/UI/Tab/TabContentSample", "Prefabs/UI/images/icon_side_tab_resource_128", "자원 탐색기", true);
tabController?.AddTabConfig("FleetManager", "이동기 관리", "Prefabs/UI/Tab/TabContentSample", "Prefabs/UI/images/icon_side_tab_fleet_128", "이동기 관리", true);

View File

@@ -13,12 +13,13 @@ namespace UVC.Factory.Tab
public class TabContentComponentList : MonoBehaviour, ITabContent
{
// [SerializeField]는 Unity 에디터의 Inspector 창에서 이 변수를 직접 연결할 수 있게 해줍니다.
// 이 창이 실제로 제어할 리스트 UI 컴포넌트를 가리킵니다.
[Tooltip("실제 데이터 리스트를 표시하는 ComponentList UI 컴포넌트입니다.")]
[SerializeField]
protected ComponentList? componentList = null;
private string category = "ALL";
/// <summary>
/// 이 컴포넌트가 처음 초기화될 때 호출됩니다. (SingletonScene의 일부)
/// 'componentList' 변수에 필요한 컴포넌트를 찾아 할당하는 역할을 합니다.
@@ -46,6 +47,15 @@ namespace UVC.Factory.Tab
// 팩토리 내 모든 객체 정보를 카테고리별로 정렬하여 가져옵니다.
var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
// 가져온 데이터를 ComponentList 컴포넌트에 전달하여 실제 UI 리스트를 생성하도록 합니다.
if (category != "ALL" && infos.ContainsKey(category))
{
var filteredInfos = new SortedDictionary<string, List<FactoryObjectInfo>>
{
{ category, infos[category] }
};
componentList?.SetupData(filteredInfos);
return;
}
componentList?.SetupData(infos);
}
@@ -81,8 +91,7 @@ namespace UVC.Factory.Tab
/// </summary>
public void Refresh()
{
var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
componentList?.SetupData(infos);
SetupData();
}
/// <summary>
@@ -91,7 +100,8 @@ namespace UVC.Factory.Tab
/// <param name="data">전달할 데이터 객체</param>
public void SetContentData(object? data)
{
Debug.Log("TabContentComponentList: SetContentData called");
Debug.Log($"TabContentComponentList: SetContentData called. data:{data}");
category = data as string ?? "ALL";
SetupData();
}

View File

@@ -0,0 +1,78 @@
#nullable enable
using Cysharp.Threading.Tasks;
using UnityEngine;
using UVC.Factory.Component;
using UVC.UI.Tab;
namespace Assets.Scripts.UVC.Factory.Tab
{
public class TabContentTabComponentList : MonoBehaviour, ITabContent
{
[SerializeField]
private TabController? tabController;
protected void Awake()
{
if (tabController == null)
{
Debug.LogError("SideTabBar: TabController가 할당되지 않았습니다.");
return;
}
}
public void Start()
{
var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
// 1. TabConfig 설정
tabController?.AddTabConfig("ALL", "ALL", "Prefabs/UI/Tab/TabContentComponentList", "", "ALL", true);
foreach (var info in infos)
{
//info.Key 카테고리 이름
tabController?.AddTabConfig(info.Key, info.Key, "Prefabs/UI/Tab/TabContentComponentList", "", info.Key, true);
}
// 2. 컨트롤러 초기화
tabController?.Initialize();
if (tabController != null)
{
tabController.OnTabChanged += (index) =>
{
Debug.Log($"탭이 변경되었습니다: {index}");
};
}
}
/// <summary>
/// 탭 콘텐츠에 데이터를 전달합니다.
/// </summary>
/// <param name="data">전달할 데이터 객체</param>
public void SetContentData(object? data)
{
Debug.Log("TabContentTabComponentList: SetContentData called");
}
/// <summary>
/// 탭 전환 시 데이터가 있는 경우 전달 되는 데이터. SetContentData 이후 호출 됨
/// </summary>
/// <param name="data">전달할 데이터 객체</param>
public void UpdateContentData(object? data)
{
}
/// <summary>
/// 닫힐 때 실행되는 로직을 처리합니다.
/// </summary>
/// <returns>비동기 닫기 작업을 나타내는 <see cref="UniTask"/>입니다.</returns>
public UniTask OnCloseAsync()
{
Debug.Log("TabContentTabComponentList: OnClose called");
return UniTask.CompletedTask;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 28aaae2c78789f346a82a972ca52d44b

View File

@@ -62,7 +62,6 @@ namespace UVC.UI.List.ComponentList
}
}
protected void OnDestroy()
{
if (inputField != null)
@@ -87,42 +86,68 @@ namespace UVC.UI.List.ComponentList
if (inputField != null) inputField.text = string.Empty;
//toggle 않된 카테고리 찾기
List<string> notExpendedCategory = new List<string>();
if (scrollList.GetDataCount() > 0)
{
List<InfiniteScrollData> scrollDatalist = scrollList.GetDataList();
foreach (var data in scrollDatalist)
{
if (data is ComponentListItemData itemData && itemData.isCategory)
{
itemData.OnCategoryExpendAction = null;
if (!itemData.isExpanded) notExpendedCategory.Add(itemData.categoryName);
}
}
}
//transform.SetAsFirstSibling();
scrollList.ClearData(); // 스크롤 리스트의 기존 데이터를 모두 비웁니다.
data?.Clear();
if (data != null) data?.Clear();
data = new List<ComponentListItemData>();
// 1. FactoryObjectManager에서 카테고리별로 그룹화된 객체 정보를 가져옵니다.
//var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
//보여주기 용 list
List<ComponentListItemData> listData = new List<ComponentListItemData>();
// 2. 각 카테고리에 대해 루프를 돕니다.
foreach (var info in objectsData)
{
bool isExpended = !notExpendedCategory.Contains(info.Key);
// 2-1. 카테고리 아이템 데이터를 추가합니다.
data.Add(new ComponentListItemData
var categoryData = new ComponentListItemData
{
isCategory = true,
categoryName = info.Key, // 카테고리 이름 (예: "전기 설비")
categoryBadgeCount = info.Value.Count, // 해당 카테고리의 항목 수
OnCategoryExpendAction = ToggleCategory // 액션 할당
});
OnCategoryExpendAction = ToggleCategory, // 액션 할당
isExpanded = isExpended,
};
data.Add(categoryData);
listData.Add(categoryData);
// 2-2. 해당 카테고리에 속한 모든 일반 항목 데이터를 추가합니다.
foreach (var item in info.Value)
{
data.Add(new ComponentListItemData
var itemData = new ComponentListItemData
{
isCategory = false,
generalName = item.Name, // 항목 이름 (예: "분전반")
generalOption = item.Floor, // 항목 옵션 (예: "1F")
factoryObjectInfo = item // 이 항목이 속한 카테고리 이름 (필터링을 위해 필요)
});
};
data.Add(itemData);
//카테고리가 축소된 상태라면, 자식 항목들을 추가하지 않고 다음 카테고리로 넘어갑니다.
if (isExpended) listData.Add(itemData);
}
}
// 3. 완성된 데이터 리스트를 InfiniteScroll에 한 번에 삽입하여 UI를 업데이트합니다.
scrollList.InsertData(data.ToArray());
scrollList.InsertData(listData.ToArray());
}
/// <summary>
@@ -134,15 +159,12 @@ namespace UVC.UI.List.ComponentList
if (scrollList == null || data == null) return;
// 1. 현재 스크롤 리스트에서 토글할 카테고리의 인덱스를 찾습니다.
int categoryIndex = -1;
for (int i = 0; i < scrollList.GetDataCount(); i++)
List<InfiniteScrollData> scrollDatalist = scrollList.GetDataList();
int categoryIndex = scrollDatalist.FindIndex(0, (item) =>
{
if (scrollList.GetData(i) == categoryData)
{
categoryIndex = i;
break;
}
}
if (item is ComponentListItemData data) return data.categoryName == categoryData.categoryName;
return false;
});
if (categoryIndex == -1) return; // 카테고리를 찾지 못하면 중단

View File

@@ -104,6 +104,9 @@ namespace UVC.UI.List.ComponentList
// 카테고리 아이템일 경우
categoryText.text = itemData.categoryName;
categoryBageText.text = itemData.categoryBadgeCount.ToString();
// 애니메이션을 위해 현재 각도에서 목표 각도로 회전시킵니다.
categoryExtendButton.transform.DORotate(new Vector3(0, 0, itemData.isExpanded ? 0 : 90), 0.0f);
}
else
{

View File

@@ -73,7 +73,7 @@ namespace UVC.UI.Tab
[SerializeField] private TabContentConfig[] tabConfigs = new TabContentConfig[0];
[Tooltip("활성화 탭 버튼을 클릭 시 해당 탭 비활성화 할지 여부")]
[SerializeField] private bool closableTab = false;
[SerializeField] private bool togglableTab = false;
// 탭 모델
private TabModel? _tabModel;
@@ -89,7 +89,7 @@ namespace UVC.UI.Tab
private void Awake()
{
_tabModel = new TabModel(closableTab);
_tabModel = new TabModel(togglableTab);
if (initializeOnStart)
{
Initialize();
@@ -153,7 +153,7 @@ namespace UVC.UI.Tab
// TabView에 설정 전달하여 초기화
List<TabContentConfig> allConfigs = GetAllTabConfigs();
tabView.InitializeTabs(_tabModel.Tabs, allConfigs, OnTabButtonSelected, closableTab);
tabView.InitializeTabs(_tabModel.Tabs, allConfigs, OnTabButtonSelected, togglableTab);
// 모델 이벤트 구독
_tabModel.OnTabChanged += HandleTabChanged;

View File

@@ -40,11 +40,11 @@ namespace UVC.UI.Tab
public event Action<int, object?>? OnTabChanged;
//활성화 탭 버튼을 클릭 시 해당 탭 비활성화 할지 여부
private bool closableTab = false;
private bool togglableTab = false;
public TabModel(bool closableTab = false)
public TabModel(bool togglableTab = false)
{
this.closableTab = closableTab;
this.togglableTab = togglableTab;
}
/// <summary>
@@ -56,7 +56,7 @@ namespace UVC.UI.Tab
_tabs.Add(tab);
// 첫 번째 추가된 탭을 기본 활성화 탭으로 설정
if (_activeTabIndex == -1 && _tabs.Count == 1 && !closableTab)
if (_activeTabIndex == -1 && _tabs.Count == 1 && !togglableTab)
{
_activeTabIndex = 0;
}
@@ -79,7 +79,7 @@ namespace UVC.UI.Tab
// 같은 탭을 다시 선택한 경우 무시
if (_activeTabIndex == tabIndex)
{
if (!closableTab)
if (!togglableTab)
{
return;
}

View File

@@ -51,7 +51,7 @@ namespace UVC.UI.Tab
//활성화 탭 버튼을 클릭 시 해당 탭 비활성화 할지 여부
private bool closableTab = false;
private bool togglableTab = false;
/// <summary>
/// 탭 시스템을 초기화합니다.
@@ -59,9 +59,10 @@ namespace UVC.UI.Tab
/// <param name="tabs">탭 데이터 목록</param>
/// <param name="configs">탭 설정 목록 (TabController에서 전달)</param>
/// <param name="onTabSelected">탭 선택 시 호출될 콜백</param>
public void InitializeTabs(IReadOnlyList<TabData> tabs, List<TabContentConfig> configs, Action<int> onTabSelected, bool closableTab = false)
/// <param name="togglableTab">전체를 비활성화 할 수 있는지</param>
public void InitializeTabs(IReadOnlyList<TabData> tabs, List<TabContentConfig> configs, Action<int> onTabSelected, bool togglableTab = false)
{
this.closableTab = closableTab;
this.togglableTab = togglableTab;
// 기존 탭 정리
ClearTabs();
@@ -223,7 +224,7 @@ namespace UVC.UI.Tab
/// <param name="updateData">업데이트할 데이터 (선택적)</param>
public async UniTask UpdateActiveTab(int tabIndex, int prevTabIndex, TabData? tabData, object? updateData = null)
{
if ((tabIndex < 0 && !closableTab) || tabIndex >= _tabConfigs.Count) return;
if ((tabIndex < 0 && !togglableTab) || tabIndex >= _tabConfigs.Count) return;
if (tabIndex > -1 && tabData != null)
{
var config = _tabConfigs[tabIndex];

View File

@@ -94,8 +94,7 @@ namespace UVC.UI.Window
/// </summary>
public void Refresh()
{
var infos = FactoryObjectManager.Instance.GetFactoryObjectInfosByCategory();
componentList?.SetupData(infos);
SetupData();
}
/// <summary>

View File

@@ -63,7 +63,7 @@ namespace UVC.UI.Window.PropertyWindow
// UI를 다시 그리기 전에 기존에 생성된 모든 자식 오브젝트를 삭제합니다.
foreach (Transform child in _container)
{
Destroy(child.gameObject);
DestroyImmediate(child.gameObject);
}
if (_controller == null) return;