UTKAccordion 개발 완료. UTKComponentList 완료
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -51,22 +51,23 @@
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
/* TreeView 토글 체크마크 (펼침/접힘 아이콘) */
|
||||
.accordion-section-row #unity-checkmark {
|
||||
.accordion-tree-view .unity-tree-view__item-indent {
|
||||
width: 0;
|
||||
min-width: 0;
|
||||
max-width: 0;
|
||||
flex-basis: 0;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.accordion-tree-view .unity-tree-view__item-toggle .unity-toggle__checkmark {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-image: resource('UIToolkit/Images/icon_right_22');
|
||||
-unity-background-image-tint-color: var(--accordion-icon-color);
|
||||
transition-duration: 0.2s;
|
||||
transition-property: rotate;
|
||||
rotate: 0;
|
||||
}
|
||||
|
||||
/* 펼쳐진 상태 */
|
||||
.accordion-section-row .unity-tree-view__item-toggle:checked #unity-checkmark {
|
||||
rotate: 90deg;
|
||||
}
|
||||
|
||||
.accordion-tree-view {
|
||||
flex-grow: 1;
|
||||
background-color: transparent;
|
||||
@@ -119,7 +120,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 섹션 행 전체 배경색 (토글 영역 포함) */
|
||||
.accordion-section-row {
|
||||
background-color: var(--accordion-header-bg);
|
||||
}
|
||||
@@ -128,7 +128,6 @@
|
||||
background-color: var(--accordion-header-hover);
|
||||
}
|
||||
|
||||
/* 섹션 행의 토글 표시 */
|
||||
.accordion-section-row .unity-tree-view__item-toggle {
|
||||
display: flex;
|
||||
width: 16px;
|
||||
@@ -157,8 +156,7 @@
|
||||
}
|
||||
|
||||
.accordion-section {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-color: var(--accordion-border-color);
|
||||
border-bottom-width: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -175,7 +173,6 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 섹션 헤더 내 펼침 버튼 숨김 (TreeView 토글 사용) */
|
||||
.accordion-expand-button {
|
||||
display: none;
|
||||
}
|
||||
@@ -212,6 +209,7 @@
|
||||
height: 25px;
|
||||
padding: 0 8px 0 28px;
|
||||
background-color: transparent;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.accordion-horizontal-item:hover {
|
||||
@@ -276,7 +274,7 @@
|
||||
background-color: transparent;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin-left: 0px;
|
||||
margin-left: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -297,11 +295,6 @@
|
||||
-unity-background-image-tint-color: var(--accordion-icon-hover);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------
|
||||
그리드 아이템 (UTKImageListItem 스타일 참고)
|
||||
------------------------------------------------ */
|
||||
|
||||
/* 그리드 행 컨테이너 (2개씩 배치) */
|
||||
.accordion-grid-row {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
@@ -309,7 +302,6 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 그리드 아이템 */
|
||||
.accordion-grid-item {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -341,7 +333,6 @@
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* 그리드 이미지 */
|
||||
.accordion-grid-image {
|
||||
width: 116px;
|
||||
height: 87px;
|
||||
@@ -358,7 +349,6 @@
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* 그리드 캡션 */
|
||||
.accordion-grid-caption {
|
||||
color: var(--accordion-text-color);
|
||||
font-size: 11px;
|
||||
@@ -376,7 +366,6 @@
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 레거시 그리드 컨테이너 (하위 호환성) */
|
||||
.accordion-grid-container {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -16,8 +16,25 @@
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#unity-checkmark {
|
||||
/* 카테고리 확장/축소 토글 화살표 스타일 */
|
||||
.unity-tree-view__item-toggle #unity-checkmark {
|
||||
background-image: resource('UIToolkit/Images/icon_down_22');
|
||||
transition-property: rotate;
|
||||
transition-duration: 0.15s;
|
||||
}
|
||||
|
||||
/* 카테고리 항목: hover 시 배경 색상 변경 없음 */
|
||||
.category-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* 카테고리 항목: 선택 스타일도 비활성화 */
|
||||
.category-item:checked {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.category-item:hover:checked {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.visibility-toggle {
|
||||
|
||||
@@ -13,7 +13,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
[SerializeField]
|
||||
public UIDocument uiDocument;
|
||||
|
||||
|
||||
|
||||
|
||||
private UTKComponentListWindow listWindow;
|
||||
private UTKComponentTabListWindow tabListWindow;
|
||||
@@ -22,7 +22,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
{
|
||||
uiDocument ??= GetComponent<UIDocument>();
|
||||
|
||||
|
||||
|
||||
// UTKComponentTabListWindow 사용 (탭 기능 포함)
|
||||
tabListWindow = new UTKComponentTabListWindow();
|
||||
tabListWindow.style.marginLeft = 500;
|
||||
@@ -33,7 +33,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
|
||||
// 이벤트 핸들러 등록
|
||||
RegisterTabListEventHandlers();
|
||||
|
||||
|
||||
// UTKComponentListWindow 사용 (기본)
|
||||
listWindow = new UTKComponentListWindow();
|
||||
uiDocument.rootVisualElement.Add(listWindow);
|
||||
@@ -43,24 +43,25 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
|
||||
// 이벤트 핸들러 등록
|
||||
RegisterEventHandlers();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 테스트용 계층적 트리 데이터를 생성합니다.
|
||||
/// 10개의 루트 항목을 생성하고, 각 루트에 5개의 자식,
|
||||
/// 각 자식에 3개의 손자, 각 손자에 2개의 증손자를 추가합니다.
|
||||
/// 5개의 카테고리를 생성하고, 각 카테고리에 20개의 항목을 추가합니다.
|
||||
/// </summary>
|
||||
private void CreateTestData()
|
||||
{
|
||||
var data = new List<UTKComponentListItemData>();
|
||||
var data = new List<UTKComponentListItemDataBase>();
|
||||
for (int i = 1; i <= 5; i++)
|
||||
{
|
||||
string categoryName = $"Category {i}";
|
||||
UTKComponentListItemData categoryData = new UTKComponentListItemData { name = categoryName };
|
||||
|
||||
// 카테고리 데이터 생성 (UTKComponentListCategoryData 사용)
|
||||
var categoryData = new UTKComponentListCategoryData { name = categoryName };
|
||||
|
||||
for (int j = 1; j <= 20; j++)
|
||||
{
|
||||
// 일반 항목 데이터 생성 (UTKComponentListItemData 사용)
|
||||
var itemData = new UTKComponentListItemData
|
||||
{
|
||||
name = $"Item {i}-{j}",
|
||||
@@ -72,7 +73,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
}
|
||||
listWindow.SetData(data);
|
||||
|
||||
Debug.Log("[UTKComponentListWindowSample] 테스트 데이터 생성 완료: 5개 루트 항목");
|
||||
Debug.Log("[UTKComponentListWindowSample] 테스트 데이터 생성 완료: 5개 카테고리");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,7 +84,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
{
|
||||
// 항목 선택 이벤트 (다중 선택 지원)
|
||||
// 사용자가 항목을 클릭하거나 SelectItem() 호출 시 발생
|
||||
listWindow.OnItemSelected += (List<UTKComponentListItemData> selectedItems) =>
|
||||
listWindow.OnItemSelected += (List<UTKComponentListItemDataBase> selectedItems) =>
|
||||
{
|
||||
foreach (var item in selectedItems)
|
||||
{
|
||||
@@ -93,7 +94,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
|
||||
// 항목 선택 해제 이벤트 (다중 선택 지원)
|
||||
// 사용자가 다른 항목을 선택하거나 DeselectItem() 호출 시 발생
|
||||
listWindow.OnItemDeselected += (List<UTKComponentListItemData> deselectedItems) =>
|
||||
listWindow.OnItemDeselected += (List<UTKComponentListItemDataBase> deselectedItems) =>
|
||||
{
|
||||
foreach (var item in deselectedItems)
|
||||
{
|
||||
@@ -104,7 +105,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
// 항목 삭제 이벤트
|
||||
// 사용자가 Delete 또는 Backspace 키를 누를 때 발생
|
||||
// 실제 삭제는 이 핸들러에서 DeleteItem()을 호출하여 수행
|
||||
listWindow.OnItemDeleted += (UTKComponentListItemData deletedItem) =>
|
||||
listWindow.OnItemDeleted += (UTKComponentListItemDataBase deletedItem) =>
|
||||
{
|
||||
Debug.Log($"[삭제 요청] {deletedItem.name}");
|
||||
// 실제로 항목을 삭제하려면 아래 주석을 해제하세요:
|
||||
@@ -113,21 +114,21 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
|
||||
// 항목 더블클릭 이벤트
|
||||
// 사용자가 항목을 더블클릭하거나 Enter 키를 누를 때 발생
|
||||
listWindow.OnItemDoubleClicked += (UTKComponentListItemData doubleClickedItem) =>
|
||||
listWindow.OnItemDoubleClicked += (UTKComponentListItemDataBase doubleClickedItem) =>
|
||||
{
|
||||
Debug.Log($"[더블클릭] {doubleClickedItem.name}");
|
||||
};
|
||||
|
||||
// 항목 가시성 변경 이벤트
|
||||
// 사용자가 눈 아이콘 버튼을 클릭하여 가시성을 토글할 때 발생
|
||||
listWindow.OnItemVisibilityChanged += (UTKComponentListItemData item, bool isVisible) =>
|
||||
listWindow.OnItemVisibilityChanged += (UTKComponentListItemDataBase item, bool isVisible) =>
|
||||
{
|
||||
Debug.Log($"[가시성 변경] {item.name}, IsVisible: {isVisible}");
|
||||
};
|
||||
|
||||
// 아이콘 클릭 이벤트
|
||||
// 그룹 항목의 setting-btn 등 아이콘 버튼 클릭 시 발생
|
||||
listWindow.OnItemIconClicked += (string iconName, UTKComponentListItemData item) =>
|
||||
listWindow.OnItemIconClicked += (string iconName, UTKComponentListItemDataBase item) =>
|
||||
{
|
||||
Debug.Log($"[아이콘 클릭] {iconName}, Item: {item.name}");
|
||||
};
|
||||
@@ -141,14 +142,16 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
/// </summary>
|
||||
private void CreateTabListTestData()
|
||||
{
|
||||
var data = new List<UTKComponentListItemData>();
|
||||
var data = new List<UTKComponentListItemDataBase>();
|
||||
for (int i = 1; i <= 5; i++)
|
||||
{
|
||||
string categoryName = $"Category {i}";
|
||||
UTKComponentListItemData categoryData = new UTKComponentListItemData { name = categoryName };
|
||||
// 카테고리 데이터 생성 (UTKComponentListCategoryData 사용)
|
||||
var categoryData = new UTKComponentListCategoryData { name = categoryName };
|
||||
|
||||
for (int j = 1; j <= 20; j++)
|
||||
{
|
||||
// 일반 항목 데이터 생성 (UTKComponentListItemData 사용)
|
||||
var itemData = new UTKComponentListItemData
|
||||
{
|
||||
name = $"Item {i}-{j}",
|
||||
@@ -169,7 +172,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
private void RegisterTabListEventHandlers()
|
||||
{
|
||||
// 항목 선택 이벤트 (다중 선택 지원)
|
||||
tabListWindow.OnItemSelected += (List<UTKComponentListItemData> selectedItems) =>
|
||||
tabListWindow.OnItemSelected += (List<UTKComponentListItemDataBase> selectedItems) =>
|
||||
{
|
||||
foreach (var item in selectedItems)
|
||||
{
|
||||
@@ -178,7 +181,7 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
};
|
||||
|
||||
// 항목 선택 해제 이벤트 (다중 선택 지원)
|
||||
tabListWindow.OnItemDeselected += (List<UTKComponentListItemData> deselectedItems) =>
|
||||
tabListWindow.OnItemDeselected += (List<UTKComponentListItemDataBase> deselectedItems) =>
|
||||
{
|
||||
foreach (var item in deselectedItems)
|
||||
{
|
||||
@@ -187,25 +190,25 @@ public class UTKComponentListWindowSample : MonoBehaviour
|
||||
};
|
||||
|
||||
// 항목 삭제 이벤트
|
||||
tabListWindow.OnItemDeleted += (UTKComponentListItemData deletedItem) =>
|
||||
tabListWindow.OnItemDeleted += (UTKComponentListItemDataBase deletedItem) =>
|
||||
{
|
||||
Debug.Log($"[TabList 삭제 요청] {deletedItem.name}");
|
||||
};
|
||||
|
||||
// 항목 더블클릭 이벤트
|
||||
tabListWindow.OnItemDoubleClicked += (UTKComponentListItemData doubleClickedItem) =>
|
||||
tabListWindow.OnItemDoubleClicked += (UTKComponentListItemDataBase doubleClickedItem) =>
|
||||
{
|
||||
Debug.Log($"[TabList 더블클릭] {doubleClickedItem.name}");
|
||||
};
|
||||
|
||||
// 항목 가시성 변경 이벤트
|
||||
tabListWindow.OnItemVisibilityChanged += (UTKComponentListItemData item, bool isVisible) =>
|
||||
tabListWindow.OnItemVisibilityChanged += (UTKComponentListItemDataBase item, bool isVisible) =>
|
||||
{
|
||||
Debug.Log($"[TabList 가시성 변경] {item.name}, IsVisible: {isVisible}");
|
||||
};
|
||||
|
||||
// 아이콘 클릭 이벤트
|
||||
tabListWindow.OnItemIconClicked += (string iconName, UTKComponentListItemData item) =>
|
||||
tabListWindow.OnItemIconClicked += (string iconName, UTKComponentListItemDataBase item) =>
|
||||
{
|
||||
Debug.Log($"[TabList 아이콘 클릭] {iconName}, Item: {item.name}");
|
||||
};
|
||||
|
||||
@@ -534,8 +534,17 @@ namespace UVC.UIToolkit.List
|
||||
// 이전 바인딩에서 추가된 클래스 제거
|
||||
treeViewItem?.RemoveFromClassList("accordion-section-row");
|
||||
|
||||
// 들여쓰기 요소 너비 강제 제거
|
||||
var indentElement = treeViewItem?.Q<VisualElement>("unity-tree-view__item-indent");
|
||||
if (indentElement != null)
|
||||
{
|
||||
indentElement.style.width = 0;
|
||||
}
|
||||
|
||||
// 토글(화살표) 영역 설정
|
||||
var toggle = treeViewItem?.Q<Toggle>("unity-tree-view__item-toggle");
|
||||
var checkmark = toggle?.Q<VisualElement>("unity-checkmark");
|
||||
|
||||
if (toggle != null)
|
||||
{
|
||||
// 섹션만 화살표 표시
|
||||
@@ -544,10 +553,36 @@ namespace UVC.UIToolkit.List
|
||||
toggle.style.display = hasChildren ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// 섹션 행에 배경색 클래스 추가
|
||||
// 섹션 행에 배경색 클래스 추가 및 회전 상태 설정
|
||||
if (item.nodeType == UTKAccordionNodeType.Section && treeViewItem != null)
|
||||
{
|
||||
treeViewItem.AddToClassList("accordion-section-row");
|
||||
|
||||
// 섹션의 확장 상태에 따라 회전 직접 설정 (transition 없이 즉시 적용)
|
||||
if (checkmark != null)
|
||||
{
|
||||
// transition 비활성화 후 회전 설정
|
||||
checkmark.style.transitionDuration = new List<TimeValue> { new TimeValue(0f) };
|
||||
float angle = item.isExpanded ? 90f : 0f;
|
||||
checkmark.style.rotate = new Rotate(angle);
|
||||
|
||||
// 다음 프레임에 transition 복원
|
||||
checkmark.schedule.Execute(() =>
|
||||
{
|
||||
checkmark.style.transitionDuration = new List<TimeValue> { new TimeValue(0.2f, TimeUnit.Second) };
|
||||
checkmark.style.transitionProperty = new List<StylePropertyName> { new StylePropertyName("rotate") };
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 섹션이 아닌 경우 회전 및 transition 제거
|
||||
if (checkmark != null)
|
||||
{
|
||||
checkmark.style.rotate = StyleKeyword.Null;
|
||||
checkmark.style.transitionDuration = StyleKeyword.Null;
|
||||
checkmark.style.transitionProperty = StyleKeyword.Null;
|
||||
}
|
||||
}
|
||||
|
||||
// 노드 타입에 따라 UI 생성
|
||||
@@ -576,6 +611,18 @@ namespace UVC.UIToolkit.List
|
||||
|
||||
private void UnbindTreeItem(VisualElement element, int index)
|
||||
{
|
||||
// TreeView 행 요소에서 섹션 클래스 제거 (가상화 시 재사용 대비)
|
||||
var treeViewItem = element.parent?.parent;
|
||||
treeViewItem?.RemoveFromClassList("accordion-section-row");
|
||||
|
||||
// 토글 체크마크 회전 상태 초기화
|
||||
var toggle = treeViewItem?.Q<Toggle>("unity-tree-view__item-toggle");
|
||||
var checkmark = toggle?.Q<VisualElement>("unity-checkmark");
|
||||
if (checkmark != null)
|
||||
{
|
||||
checkmark.style.rotate = StyleKeyword.Null;
|
||||
}
|
||||
|
||||
// 이벤트 정리 등 필요한 작업
|
||||
element.userData = null;
|
||||
}
|
||||
@@ -926,6 +973,9 @@ namespace UVC.UIToolkit.List
|
||||
{
|
||||
item.isExpanded = evt.isExpanded;
|
||||
|
||||
// 해당 아이템의 회전 상태 업데이트
|
||||
UpdateItemRotation(evt.id, evt.isExpanded);
|
||||
|
||||
if (item.IsSection)
|
||||
{
|
||||
var legacySection = new UTKAccordionSectionData
|
||||
@@ -939,6 +989,35 @@ namespace UVC.UIToolkit.List
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>특정 아이템의 회전 상태를 업데이트합니다.</summary>
|
||||
private void UpdateItemRotation(int itemId, bool isExpanded)
|
||||
{
|
||||
if (_treeView == null) return;
|
||||
|
||||
// TreeView에서 해당 아이템의 index 찾기
|
||||
var index = _treeView.viewController.GetIndexForId(itemId);
|
||||
if (index < 0) return;
|
||||
|
||||
// 해당 index의 VisualElement 찾기
|
||||
var itemElement = _treeView.Q<VisualElement>($"unity-tree-view__item");
|
||||
|
||||
// TreeView 내의 모든 행을 순회하여 해당 아이템 찾기
|
||||
_treeView.Query<VisualElement>(className: "unity-tree-view__item").ForEach(treeViewItem =>
|
||||
{
|
||||
var container = treeViewItem.Q<VisualElement>("accordion-item-container");
|
||||
if (container?.userData is UTKAccordionItemData data && data.id == itemId)
|
||||
{
|
||||
var toggle = treeViewItem.Q<Toggle>("unity-tree-view__item-toggle");
|
||||
var checkmark = toggle?.Q<VisualElement>("unity-checkmark");
|
||||
if (checkmark != null)
|
||||
{
|
||||
float angle = isExpanded ? 90f : 0f;
|
||||
checkmark.style.rotate = new Rotate(angle);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ExpandByData(List<UTKAccordionItemData> items)
|
||||
{
|
||||
if (_treeView == null || items == null) return;
|
||||
|
||||
@@ -95,13 +95,13 @@ namespace UVC.UIToolkit.List
|
||||
/// 원본 루트 데이터입니다.
|
||||
/// 검색 필터 해제 시 원래 데이터로 복원하는 데 사용됩니다.
|
||||
/// </summary>
|
||||
private List<UTKComponentListItemData> _originalRoots = new();
|
||||
private List<UTKComponentListItemDataBase> _originalRoots = new();
|
||||
|
||||
/// <summary>
|
||||
/// TreeView에 바인딩되는 데이터 소스입니다.
|
||||
/// TreeViewItemData는 Unity의 TreeView가 요구하는 래퍼 타입입니다.
|
||||
/// </summary>
|
||||
private List<TreeViewItemData<UTKComponentListItemData>>? _rootData;
|
||||
private List<TreeViewItemData<UTKComponentListItemDataBase>>? _rootData;
|
||||
|
||||
/// <summary>
|
||||
/// 항목 ID 자동 생성을 위한 시드 값입니다.
|
||||
@@ -113,7 +113,7 @@ namespace UVC.UIToolkit.List
|
||||
/// 이전에 선택된 항목들입니다.
|
||||
/// 선택 해제 이벤트 발송에 사용됩니다.
|
||||
/// </summary>
|
||||
private List<UTKComponentListItemData> _previouslySelectedItems = new();
|
||||
private List<UTKComponentListItemDataBase> _previouslySelectedItems = new();
|
||||
|
||||
/// <summary>
|
||||
/// 선택 이벤트 발송을 일시적으로 억제하는 플래그입니다.
|
||||
@@ -152,32 +152,32 @@ namespace UVC.UIToolkit.List
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemSelected;
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemSelected;
|
||||
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemDeselected;
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemDeselected;
|
||||
/// <summary>
|
||||
/// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다.
|
||||
/// 3D 모델의 GameObject 활성화/비활성화에 연동합니다.
|
||||
/// </summary>
|
||||
public event Action<UTKComponentListItemData, bool>? OnItemVisibilityChanged;
|
||||
public event Action<UTKComponentListItemDataBase, bool>? OnItemVisibilityChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키).
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDeleted;
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDoubleClicked;
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDoubleClicked;
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘을 클릭할 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<string, UTKComponentListItemData>? OnItemIconClicked;
|
||||
public Action<string, UTKComponentListItemDataBase>? OnItemIconClicked;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -405,25 +405,25 @@ namespace UVC.UIToolkit.List
|
||||
|
||||
/// <summary>
|
||||
/// 트리 데이터를 설정합니다.
|
||||
/// UTKComponentListItemData 리스트를 받아 TreeView에 바인딩합니다.
|
||||
/// UTKComponentListItemDataBase 리스트를 받아 TreeView에 바인딩합니다.
|
||||
/// </summary>
|
||||
/// <param name="roots">루트 항목들의 리스트 (계층 구조의 최상위 항목들)</param>
|
||||
public void SetData(List<UTKComponentListItemData> roots)
|
||||
public void SetData(List<UTKComponentListItemDataBase> roots)
|
||||
{
|
||||
// 원본 데이터 저장 (필터 복원용)
|
||||
_originalRoots = roots ?? new List<UTKComponentListItemData>();
|
||||
_originalRoots = roots ?? new List<UTKComponentListItemDataBase>();
|
||||
|
||||
// ID 시드 초기화
|
||||
_idSeed = 1;
|
||||
|
||||
// 순환 참조 감지를 위한 방문 집합
|
||||
var visited = new HashSet<UTKComponentListItemData>();
|
||||
var visited = new HashSet<UTKComponentListItemDataBase>();
|
||||
|
||||
// TreeView 형식으로 데이터 변환
|
||||
_rootData = ConvertToTreeViewData(_originalRoots, visited, 0);
|
||||
|
||||
// TreeView에 데이터 설정
|
||||
_treeView!.SetRootItems<UTKComponentListItemData>(_rootData);
|
||||
_treeView!.SetRootItems<UTKComponentListItemDataBase>(_rootData);
|
||||
|
||||
// UI 갱신 및 데이터의 isExpanded 상태에 따라 펼치기
|
||||
_treeView!.Rebuild();
|
||||
@@ -434,7 +434,7 @@ namespace UVC.UIToolkit.List
|
||||
/// 데이터의 isExpanded 값에 따라 항목을 펼치거나 접습니다.
|
||||
/// </summary>
|
||||
/// <param name="items">처리할 항목 리스트</param>
|
||||
private void ExpandByData(List<UTKComponentListItemData> items)
|
||||
private void ExpandByData(List<UTKComponentListItemDataBase> items)
|
||||
{
|
||||
if (_treeView == null || items == null) return;
|
||||
|
||||
@@ -455,7 +455,7 @@ namespace UVC.UIToolkit.List
|
||||
/// <summary>
|
||||
/// ExpandByData의 내부 재귀 구현입니다.
|
||||
/// </summary>
|
||||
private void ExpandByDataInternal(List<UTKComponentListItemData> items)
|
||||
private void ExpandByDataInternal(List<UTKComponentListItemDataBase> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
@@ -510,7 +510,7 @@ namespace UVC.UIToolkit.List
|
||||
/// ID가 0이면 자동으로 할당됩니다.
|
||||
/// </summary>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
@@ -521,13 +521,13 @@ namespace UVC.UIToolkit.List
|
||||
_originalRoots.Add(data);
|
||||
|
||||
// _rootData가 null이면 초기화
|
||||
_rootData ??= new List<TreeViewItemData<UTKComponentListItemData>>();
|
||||
_rootData ??= new List<TreeViewItemData<UTKComponentListItemDataBase>>();
|
||||
|
||||
// TreeViewItemData 래퍼 생성 및 추가
|
||||
var childData = data.children != null && data.children.Count > 0
|
||||
? ConvertToTreeViewData(data.children, new HashSet<UTKComponentListItemData>(), 1)
|
||||
? ConvertToTreeViewData(data.children, new HashSet<UTKComponentListItemDataBase>(), 1)
|
||||
: null;
|
||||
var treeItem = new TreeViewItemData<UTKComponentListItemData>(data.id, data, childData);
|
||||
var treeItem = new TreeViewItemData<UTKComponentListItemDataBase>(data.id, data, childData);
|
||||
_rootData.Add(treeItem);
|
||||
|
||||
// TreeView 갱신
|
||||
@@ -537,11 +537,11 @@ namespace UVC.UIToolkit.List
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 부모 항목의 자식으로 새 항목을 추가합니다.
|
||||
/// 지정된 부모 카테고리의 자식으로 새 항목을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 항목 (null이면 루트에 추가)</param>
|
||||
/// <param name="parent">부모 카테고리 (null이면 루트에 추가)</param>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData? parent, UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListCategoryData? parent, UTKComponentListItemDataBase data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
@@ -556,8 +556,7 @@ namespace UVC.UIToolkit.List
|
||||
if (data.id == 0) data.id = _idSeed++;
|
||||
|
||||
// 부모의 children 리스트에 추가
|
||||
parent.children ??= new List<UTKComponentListItemData>();
|
||||
parent.children.Add(data);
|
||||
parent.Add(data);
|
||||
|
||||
// TreeView 데이터 재구성 및 갱신
|
||||
RefreshTreeViewData();
|
||||
@@ -568,7 +567,7 @@ namespace UVC.UIToolkit.List
|
||||
/// 자식 항목이 있는 경우 함께 삭제됩니다.
|
||||
/// </summary>
|
||||
/// <param name="data">삭제할 항목 데이터</param>
|
||||
public void DeleteItem(UTKComponentListItemData data)
|
||||
public void DeleteItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
@@ -607,7 +606,7 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
/// <param name="data">변경할 항목 데이터</param>
|
||||
/// <param name="newName">새 이름</param>
|
||||
public void SetItemName(UTKComponentListItemData data, string newName)
|
||||
public void SetItemName(UTKComponentListItemDataBase data, string newName)
|
||||
{
|
||||
if (data == null || string.IsNullOrEmpty(newName)) return;
|
||||
|
||||
@@ -734,7 +733,7 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
private void RefreshTreeViewData()
|
||||
{
|
||||
var visited = new HashSet<UTKComponentListItemData>();
|
||||
var visited = new HashSet<UTKComponentListItemDataBase>();
|
||||
_rootData = ConvertToTreeViewData(_originalRoots, visited, 0);
|
||||
_treeView?.SetRootItems(_rootData);
|
||||
_treeView?.Rebuild();
|
||||
@@ -747,14 +746,18 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="parent">부모 노드</param>
|
||||
/// <param name="target">삭제할 대상 항목</param>
|
||||
/// <returns>삭제 성공 여부</returns>
|
||||
private bool RemoveFromChildren(UTKComponentListItemData parent, UTKComponentListItemData target)
|
||||
private bool RemoveFromChildren(UTKComponentListItemDataBase parent, UTKComponentListItemDataBase target)
|
||||
{
|
||||
if (parent.children == null || parent.children.Count == 0) return false;
|
||||
|
||||
// 직접 자식에서 삭제 시도
|
||||
if (parent.children.Remove(target))
|
||||
// 카테고리인 경우에만 자식에서 삭제 가능
|
||||
if (parent is UTKComponentListCategoryData category)
|
||||
{
|
||||
return true;
|
||||
// 직접 자식에서 삭제 시도
|
||||
if (category.Remove(target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 하위 자식들에서 재귀적으로 검색
|
||||
@@ -774,7 +777,7 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
/// <param name="name">찾을 항목의 이름</param>
|
||||
/// <returns>찾은 항목 또는 null</returns>
|
||||
private UTKComponentListItemData? FindItemByName(string name)
|
||||
private UTKComponentListItemDataBase? FindItemByName(string name)
|
||||
{
|
||||
foreach (var root in _originalRoots)
|
||||
{
|
||||
@@ -789,7 +792,7 @@ namespace UVC.UIToolkit.List
|
||||
/// _previouslySelectedItems와 TreeView의 선택 상태 모두에서 제거합니다.
|
||||
/// </summary>
|
||||
/// <param name="data">삭제된 항목</param>
|
||||
private void RemoveFromSelection(UTKComponentListItemData data)
|
||||
private void RemoveFromSelection(UTKComponentListItemDataBase data)
|
||||
{
|
||||
// 삭제할 항목과 모든 자식들의 ID 수집
|
||||
var idsToRemove = new HashSet<int>();
|
||||
@@ -806,7 +809,7 @@ namespace UVC.UIToolkit.List
|
||||
|
||||
foreach (var index in currentSelection)
|
||||
{
|
||||
var selectedItem = _treeView.GetItemDataForIndex<UTKComponentListItemData>(index);
|
||||
var selectedItem = _treeView.GetItemDataForIndex<UTKComponentListItemDataBase>(index);
|
||||
if (selectedItem != null && !idsToRemove.Contains(selectedItem.id))
|
||||
{
|
||||
newSelection.Add(selectedItem.id);
|
||||
@@ -825,7 +828,7 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
/// <param name="node">수집 시작 노드</param>
|
||||
/// <param name="ids">ID를 저장할 HashSet</param>
|
||||
private void CollectItemIds(UTKComponentListItemData node, HashSet<int> ids)
|
||||
private void CollectItemIds(UTKComponentListItemDataBase node, HashSet<int> ids)
|
||||
{
|
||||
ids.Add(node.id);
|
||||
|
||||
@@ -844,7 +847,7 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="node">검색 시작 노드</param>
|
||||
/// <param name="name">찾을 항목의 이름</param>
|
||||
/// <returns>찾은 항목 또는 null</returns>
|
||||
private UTKComponentListItemData? FindItemByNameRecursive(UTKComponentListItemData node, string name)
|
||||
private UTKComponentListItemDataBase? FindItemByNameRecursive(UTKComponentListItemDataBase node, string name)
|
||||
{
|
||||
if (node.name == name) return node;
|
||||
|
||||
@@ -867,7 +870,7 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="visibleNames">표시할 이름 목록</param>
|
||||
/// <param name="maxDepth">최대 검색 깊이 (0이하=무제한)</param>
|
||||
/// <param name="currentDepth">현재 깊이</param>
|
||||
private void SetVisibilityByNames(UTKComponentListItemData node, HashSet<string> visibleNames, int maxDepth, int currentDepth)
|
||||
private void SetVisibilityByNames(UTKComponentListItemDataBase node, HashSet<string> visibleNames, int maxDepth, int currentDepth)
|
||||
{
|
||||
if (node == null) return;
|
||||
|
||||
@@ -908,8 +911,31 @@ namespace UVC.UIToolkit.List
|
||||
// SelectionType.None인 경우 선택 변경 무시
|
||||
if (_treeView?.selectionType == SelectionType.None) return;
|
||||
|
||||
// 현재 선택된 항목들 (TreeView가 selectionType에 따라 이미 필터링함)
|
||||
var currentItems = selectedItems.OfType<UTKComponentListItemData>().ToList();
|
||||
// 카테고리가 선택되었는지 확인
|
||||
var allSelectedItems = selectedItems.OfType<UTKComponentListItemDataBase>().ToList();
|
||||
var categorySelected = allSelectedItems.Any(item => item.IsCategory);
|
||||
|
||||
// 카테고리가 선택된 경우: 기존 선택 상태 유지
|
||||
if (categorySelected)
|
||||
{
|
||||
// 기존 선택 상태로 복원
|
||||
_suppressSelectionEvent = true;
|
||||
try
|
||||
{
|
||||
var previousIds = _previouslySelectedItems.Select(item => item.id).ToList();
|
||||
_treeView?.SetSelection(previousIds);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_suppressSelectionEvent = false;
|
||||
}
|
||||
return; // 이벤트 발송하지 않고 종료
|
||||
}
|
||||
|
||||
// 현재 선택된 항목들 (카테고리 제외)
|
||||
var currentItems = allSelectedItems
|
||||
.Where(item => !item.IsCategory)
|
||||
.ToList();
|
||||
|
||||
// 선택 해제된 항목들 찾기 (이전에는 있었지만 현재는 없는 항목)
|
||||
var deselectedItems = _previouslySelectedItems
|
||||
@@ -957,7 +983,7 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="chosenItems">선택된 항목들</param>
|
||||
private void OnTreeViewItemsChosen(IEnumerable<object> chosenItems)
|
||||
{
|
||||
var item = chosenItems.FirstOrDefault() as UTKComponentListItemData;
|
||||
var item = chosenItems.FirstOrDefault() as UTKComponentListItemDataBase;
|
||||
if (item != null)
|
||||
{
|
||||
OnItemDoubleClicked?.Invoke(item);
|
||||
@@ -998,26 +1024,55 @@ namespace UVC.UIToolkit.List
|
||||
// ExpandByData() 실행 중에는 이벤트 무시 (데이터 덮어쓰기 방지)
|
||||
if (_suppressExpandEvent) return;
|
||||
|
||||
var item = _treeView?.GetItemDataForId<UTKComponentListItemData>(evt.id);
|
||||
var item = _treeView?.GetItemDataForId<UTKComponentListItemDataBase>(evt.id);
|
||||
if (item != null)
|
||||
{
|
||||
item.isExpanded = evt.isExpanded;
|
||||
}
|
||||
|
||||
// 해당 항목의 화살표 회전 업데이트
|
||||
UpdateToggleRotation(evt.id, evt.isExpanded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 항목의 토글 화살표 회전을 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <param name="itemId">항목 ID</param>
|
||||
/// <param name="isExpanded">확장 상태</param>
|
||||
private void UpdateToggleRotation(int itemId, bool isExpanded)
|
||||
{
|
||||
if (_treeView == null) return;
|
||||
|
||||
// TreeView에서 해당 항목의 인덱스 찾기
|
||||
int index = _treeView.viewController.GetIndexForId(itemId);
|
||||
if (index < 0) return;
|
||||
|
||||
// 해당 인덱스의 VisualElement 찾기
|
||||
var itemElement = _treeView.GetRootElementForIndex(index);
|
||||
if (itemElement == null) return;
|
||||
|
||||
// 토글과 체크마크 찾기
|
||||
var toggle = itemElement.Q<Toggle>("unity-tree-view__item-toggle");
|
||||
var checkmark = toggle?.Q<VisualElement>("unity-checkmark");
|
||||
if (checkmark != null)
|
||||
{
|
||||
checkmark.style.rotate = new Rotate(isExpanded ? 0f : -90f);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 데이터 변환 (Data Conversion)
|
||||
/// <summary>
|
||||
/// UTKComponentListItemData 리스트를 TreeView가 요구하는 TreeViewItemData 형식으로 변환합니다.
|
||||
/// UTKComponentListItemDataBase 리스트를 TreeView가 요구하는 TreeViewItemData 형식으로 변환합니다.
|
||||
/// 재귀적으로 자식 항목도 변환하며, 순환 참조를 감지합니다.
|
||||
/// </summary>
|
||||
/// <param name="items">변환할 항목 리스트</param>
|
||||
/// <param name="visited">순환 참조 감지용 방문 집합</param>
|
||||
/// <param name="depth">현재 깊이 (디버깅용)</param>
|
||||
/// <returns>TreeViewItemData 래퍼 리스트</returns>
|
||||
private List<TreeViewItemData<UTKComponentListItemData>> ConvertToTreeViewData(List<UTKComponentListItemData> items, HashSet<UTKComponentListItemData> visited, int depth)
|
||||
private List<TreeViewItemData<UTKComponentListItemDataBase>> ConvertToTreeViewData(List<UTKComponentListItemDataBase> items, HashSet<UTKComponentListItemDataBase> visited, int depth)
|
||||
{
|
||||
var list = new List<TreeViewItemData<UTKComponentListItemData>>(items.Count);
|
||||
var list = new List<TreeViewItemData<UTKComponentListItemDataBase>>(items.Count);
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item == null) continue;
|
||||
@@ -1026,19 +1081,23 @@ namespace UVC.UIToolkit.List
|
||||
if (!visited.Add(item))
|
||||
{
|
||||
Debug.LogWarning($"[UTKComponentList] Cycle detected at item '{item.name}' → children 무시");
|
||||
item.children = null;
|
||||
// 카테고리인 경우 자식 클리어
|
||||
if (item is UTKComponentListCategoryData category)
|
||||
{
|
||||
category.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// ID가 없으면 자동 할당
|
||||
if (item.id == 0) item.id = _idSeed++;
|
||||
|
||||
// 자식 항목 재귀 변환
|
||||
List<TreeViewItemData<UTKComponentListItemData>>? childData = null;
|
||||
List<TreeViewItemData<UTKComponentListItemDataBase>>? childData = null;
|
||||
if (item.children != null && item.children.Count > 0)
|
||||
childData = ConvertToTreeViewData(item.children, visited, depth + 1);
|
||||
|
||||
// TreeViewItemData 래퍼 생성
|
||||
var treeItem = new TreeViewItemData<UTKComponentListItemData>(item.id, item, childData);
|
||||
var treeItem = new TreeViewItemData<UTKComponentListItemDataBase>(item.id, item, childData);
|
||||
list.Add(treeItem);
|
||||
}
|
||||
return list;
|
||||
@@ -1072,19 +1131,81 @@ namespace UVC.UIToolkit.List
|
||||
private void BindTreeItem(VisualElement element, int index)
|
||||
{
|
||||
// 인덱스로 데이터 획득
|
||||
var item = _treeView!.GetItemDataForIndex<UTKComponentListItemData>(index);
|
||||
var item = _treeView!.GetItemDataForIndex<UTKComponentListItemDataBase>(index);
|
||||
if (item == null) return;
|
||||
|
||||
// 자식 유무 확인: 실제 자식이 있거나, 검색 결과 그룹인 경우 (TreeViewItemData에만 자식이 있음)
|
||||
bool hasChildren = (item.children != null && item.children.Count > 0) || item.isSearchResultGroup;
|
||||
|
||||
// 0. 자식이 없는 항목의 토글(화살표) 영역 너비 제거
|
||||
// 0. 자식이 없는 항목의 토글(화살표) 영역 너비 제거 및 회전 설정
|
||||
// element는 TemplateContainer이고, Toggle은 부모(unity-tree-view__item)의 자식
|
||||
var treeViewItem = element.parent?.parent;
|
||||
var toggle = treeViewItem?.Q<Toggle>("unity-tree-view__item-toggle");
|
||||
if (toggle != null)
|
||||
{
|
||||
toggle.style.width = hasChildren ? StyleKeyword.Auto : 0;
|
||||
|
||||
// 확장 상태에 따라 화살표 회전 설정
|
||||
if (hasChildren)
|
||||
{
|
||||
var checkmark = toggle.Q<VisualElement>("unity-checkmark");
|
||||
if (checkmark != null)
|
||||
{
|
||||
bool isExpanded = _treeView!.IsExpanded(item.id);
|
||||
checkmark.style.rotate = new Rotate(isExpanded ? 0f : -90f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 카테고리 항목인 경우: 선택 클릭 이벤트 차단 (Shift/Ctrl 클릭 포함)
|
||||
// treeViewItem 레벨에서 PointerDownEvent를 캡처하여 선택 로직 차단
|
||||
if (item.IsCategory && treeViewItem != null)
|
||||
{
|
||||
// 카테고리 항목에 클래스 추가 (hover 스타일 비활성화용)
|
||||
treeViewItem.AddToClassList("category-item");
|
||||
|
||||
// 기존 핸들러 제거를 위해 userData 사용
|
||||
if (treeViewItem.userData is EventCallback<PointerDownEvent> oldHandler)
|
||||
{
|
||||
treeViewItem.UnregisterCallback(oldHandler, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
EventCallback<PointerDownEvent> categoryClickHandler = (evt) =>
|
||||
{
|
||||
// 토글(화살표) 영역 클릭은 허용 (펼치기/접기 기능 유지)
|
||||
if (toggle != null && toggle.worldBound.Contains(evt.position))
|
||||
{
|
||||
return; // 토글 클릭은 통과
|
||||
}
|
||||
|
||||
// 카테고리 클릭 시 이벤트 전파 완전 차단
|
||||
// 이렇게 하면 TreeView의 선택 로직이 실행되지 않음
|
||||
evt.StopImmediatePropagation();
|
||||
|
||||
// 펼치기/접기 토글 (카테고리 본문 클릭 시)
|
||||
if (_treeView != null)
|
||||
{
|
||||
bool isExpanded = _treeView.IsExpanded(item.id);
|
||||
if (isExpanded)
|
||||
_treeView.CollapseItem(item.id);
|
||||
else
|
||||
_treeView.ExpandItem(item.id);
|
||||
}
|
||||
};
|
||||
|
||||
treeViewItem.userData = categoryClickHandler;
|
||||
treeViewItem.RegisterCallback(categoryClickHandler, TrickleDown.TrickleDown);
|
||||
}
|
||||
else if (treeViewItem != null)
|
||||
{
|
||||
// 일반 항목인 경우: 카테고리 클래스 제거 및 이전에 등록된 핸들러 제거
|
||||
treeViewItem.RemoveFromClassList("category-item");
|
||||
|
||||
if (treeViewItem.userData is EventCallback<PointerDownEvent> oldHandler)
|
||||
{
|
||||
treeViewItem.UnregisterCallback(oldHandler, TrickleDown.TrickleDown);
|
||||
treeViewItem.userData = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 기존 내용 제거 후 적절한 템플릿으로 교체
|
||||
@@ -1194,7 +1315,7 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
/// <param name="node">현재 노드</param>
|
||||
/// <param name="isVisible">설정할 가시성 상태</param>
|
||||
private void SetChildrenVisibility(UTKComponentListItemData node, bool isVisible)
|
||||
private void SetChildrenVisibility(UTKComponentListItemDataBase node, bool isVisible)
|
||||
{
|
||||
if (node.children == null || node.children.Count == 0) return;
|
||||
foreach (var child in node.children)
|
||||
@@ -1266,7 +1387,7 @@ namespace UVC.UIToolkit.List
|
||||
// 검색어가 없으면 원본 데이터 복원
|
||||
if (string.IsNullOrEmpty(query))
|
||||
{
|
||||
_treeView.SetRootItems<UTKComponentListItemData>(_rootData);
|
||||
_treeView.SetRootItems<UTKComponentListItemDataBase>(_rootData);
|
||||
_treeView.Rebuild();
|
||||
ExpandByData(_originalRoots);
|
||||
return;
|
||||
@@ -1277,7 +1398,7 @@ namespace UVC.UIToolkit.List
|
||||
var filteredWrappers = FilterTree(qLower);
|
||||
|
||||
// 필터링된 결과로 TreeView 갱신
|
||||
_treeView.SetRootItems<UTKComponentListItemData>(filteredWrappers);
|
||||
_treeView.SetRootItems<UTKComponentListItemDataBase>(filteredWrappers);
|
||||
_treeView.Rebuild();
|
||||
|
||||
// 검색 결과 모두 펼치기
|
||||
@@ -1289,16 +1410,16 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
/// <param name="qLower">소문자로 변환된 검색어</param>
|
||||
/// <returns>필터링된 TreeViewItemData 리스트 (검색 결과 그룹 포함)</returns>
|
||||
private List<TreeViewItemData<UTKComponentListItemData>> FilterTree(string qLower)
|
||||
private List<TreeViewItemData<UTKComponentListItemDataBase>> FilterTree(string qLower)
|
||||
{
|
||||
// 1. 매칭되는 리프 노드들만 수집
|
||||
var matchedItems = new List<UTKComponentListItemData>();
|
||||
var matchedItems = new List<UTKComponentListItemDataBase>();
|
||||
CollectMatchingLeafNodes(_originalRoots, qLower, matchedItems);
|
||||
|
||||
// 2. 검색 결과가 없으면 빈 리스트 반환
|
||||
if (matchedItems.Count == 0)
|
||||
{
|
||||
return new List<TreeViewItemData<UTKComponentListItemData>>();
|
||||
return new List<TreeViewItemData<UTKComponentListItemDataBase>>();
|
||||
}
|
||||
|
||||
string localeName = LocalizationManager.Instance.GetString("search_result_count");
|
||||
@@ -1308,8 +1429,8 @@ namespace UVC.UIToolkit.List
|
||||
}
|
||||
localeName = localeName.Replace("{0}", matchedItems.Count.ToString());
|
||||
|
||||
// 3. "검색결과 N건" 그룹 데이터 생성
|
||||
var searchResultGroup = new UTKComponentListItemData
|
||||
// 3. "검색결과 N건" 그룹 데이터 생성 (카테고리로 생성)
|
||||
var searchResultGroup = new UTKComponentListCategoryData
|
||||
{
|
||||
id = -1000, // 특별한 ID (충돌 방지)
|
||||
name = localeName,
|
||||
@@ -1318,20 +1439,20 @@ namespace UVC.UIToolkit.List
|
||||
};
|
||||
|
||||
// 4. 매칭된 항목들을 자식으로 래핑 (TreeViewItemData 형태로)
|
||||
var childWrappers = new List<TreeViewItemData<UTKComponentListItemData>>();
|
||||
var childWrappers = new List<TreeViewItemData<UTKComponentListItemDataBase>>();
|
||||
foreach (var item in matchedItems)
|
||||
{
|
||||
childWrappers.Add(new TreeViewItemData<UTKComponentListItemData>(item.id, item));
|
||||
childWrappers.Add(new TreeViewItemData<UTKComponentListItemDataBase>(item.id, item));
|
||||
}
|
||||
|
||||
// 5. 그룹을 TreeViewItemData로 래핑
|
||||
var groupWrapper = new TreeViewItemData<UTKComponentListItemData>(
|
||||
var groupWrapper = new TreeViewItemData<UTKComponentListItemDataBase>(
|
||||
searchResultGroup.id,
|
||||
searchResultGroup,
|
||||
childWrappers
|
||||
);
|
||||
|
||||
return new List<TreeViewItemData<UTKComponentListItemData>> { groupWrapper };
|
||||
return new List<TreeViewItemData<UTKComponentListItemDataBase>> { groupWrapper };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1340,7 +1461,7 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="items">검색할 항목 리스트</param>
|
||||
/// <param name="qLower">소문자로 변환된 검색어</param>
|
||||
/// <param name="result">매칭된 항목을 저장할 리스트</param>
|
||||
private void CollectMatchingLeafNodes(List<UTKComponentListItemData> items, string qLower, List<UTKComponentListItemData> result)
|
||||
private void CollectMatchingLeafNodes(List<UTKComponentListItemDataBase> items, string qLower, List<UTKComponentListItemDataBase> result)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
@@ -1367,21 +1488,21 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="node">필터링할 노드</param>
|
||||
/// <param name="qLower">소문자로 변환된 검색어</param>
|
||||
/// <returns>매치된 경우 TreeViewItemData, 아니면 null</returns>
|
||||
private TreeViewItemData<UTKComponentListItemData>? FilterNode(UTKComponentListItemData node, string qLower)
|
||||
private TreeViewItemData<UTKComponentListItemDataBase>? FilterNode(UTKComponentListItemDataBase node, string qLower)
|
||||
{
|
||||
// 자기 자신이 매치되는지 확인
|
||||
bool selfMatch = NodeMatches(node, qLower);
|
||||
List<TreeViewItemData<UTKComponentListItemData>>? childFiltered = null;
|
||||
List<TreeViewItemData<UTKComponentListItemDataBase>>? childFiltered = null;
|
||||
|
||||
// 자식들도 재귀적으로 필터링
|
||||
if (node.children != null && node.children.Count > 0)
|
||||
{
|
||||
foreach (var child in node.children)
|
||||
{
|
||||
TreeViewItemData<UTKComponentListItemData>? f = FilterNode(child, qLower);
|
||||
TreeViewItemData<UTKComponentListItemDataBase>? f = FilterNode(child, qLower);
|
||||
if (f != null)
|
||||
{
|
||||
childFiltered ??= new List<TreeViewItemData<UTKComponentListItemData>>();
|
||||
childFiltered ??= new List<TreeViewItemData<UTKComponentListItemDataBase>>();
|
||||
childFiltered.Add(f.Value);
|
||||
}
|
||||
}
|
||||
@@ -1390,7 +1511,7 @@ namespace UVC.UIToolkit.List
|
||||
// 자신이 매치되거나 매치된 자식이 있으면 포함
|
||||
if (selfMatch || (childFiltered != null && childFiltered.Count > 0))
|
||||
{
|
||||
return new TreeViewItemData<UTKComponentListItemData>(node.id, node, childFiltered);
|
||||
return new TreeViewItemData<UTKComponentListItemDataBase>(node.id, node, childFiltered);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1402,11 +1523,16 @@ namespace UVC.UIToolkit.List
|
||||
/// <param name="item">검사할 항목</param>
|
||||
/// <param name="qLower">소문자로 변환된 검색어</param>
|
||||
/// <returns>매치 여부</returns>
|
||||
private bool NodeMatches(UTKComponentListItemData item, string qLower)
|
||||
private bool NodeMatches(UTKComponentListItemDataBase item, string qLower)
|
||||
{
|
||||
if (item.name != null && item.name.ToLowerInvariant().Contains(qLower)) return true;
|
||||
if (!string.IsNullOrEmpty(item.option) && item.option.ToLowerInvariant().Contains(qLower)) return true;
|
||||
if (!string.IsNullOrEmpty(item.ExternalKey) && item.ExternalKey.ToLowerInvariant().Contains(qLower)) return true;
|
||||
|
||||
// 일반 항목인 경우 option과 ExternalKey도 검색
|
||||
if (item is UTKComponentListItemData itemData)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(itemData.option) && itemData.option.ToLowerInvariant().Contains(qLower)) return true;
|
||||
if (!string.IsNullOrEmpty(itemData.ExternalKey) && itemData.ExternalKey.ToLowerInvariant().Contains(qLower)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1414,7 +1540,7 @@ namespace UVC.UIToolkit.List
|
||||
/// 필터링된 결과의 모든 항목을 펼칩니다.
|
||||
/// </summary>
|
||||
/// <param name="roots">펼칠 루트 항목들</param>
|
||||
private void ExpandAll(List<TreeViewItemData<UTKComponentListItemData>> roots)
|
||||
private void ExpandAll(List<TreeViewItemData<UTKComponentListItemDataBase>> roots)
|
||||
{
|
||||
foreach (var r in roots)
|
||||
ExpandRecursive(r);
|
||||
@@ -1424,7 +1550,7 @@ namespace UVC.UIToolkit.List
|
||||
/// 노드와 그 자식들을 재귀적으로 펼칩니다.
|
||||
/// </summary>
|
||||
/// <param name="wrapper">펼칠 TreeViewItemData</param>
|
||||
private void ExpandRecursive(TreeViewItemData<UTKComponentListItemData> wrapper)
|
||||
private void ExpandRecursive(TreeViewItemData<UTKComponentListItemDataBase> wrapper)
|
||||
{
|
||||
_treeView.ExpandItem(wrapper.id);
|
||||
if (wrapper.children != null)
|
||||
|
||||
@@ -5,39 +5,22 @@ using System.Collections.Generic;
|
||||
namespace UVC.UIToolkit.List
|
||||
{
|
||||
/// <summary>
|
||||
/// 트리 구조 리스트의 개별 항목 데이터를 나타내는 클래스입니다.
|
||||
/// 트리 구조 리스트의 항목 데이터를 위한 추상 기본 클래스입니다.
|
||||
///
|
||||
/// <para><b>개요:</b></para>
|
||||
/// <para>
|
||||
/// UTKComponentListItemData는 계층적 트리 구조에서 각 노드(항목)를 표현합니다.
|
||||
/// 부모-자식 관계를 통해 중첩된 트리 구조를 구성할 수 있으며,
|
||||
/// UTKComponentListItemDataBase는 카테고리(UTKComponentListCategoryData)와
|
||||
/// 일반 항목(UTKComponentListItemData)의 공통 속성을 정의합니다.
|
||||
/// UTKComponentListWindow(UI)에서 사용됩니다.
|
||||
/// </para>
|
||||
///
|
||||
/// <para><b>주요 용도:</b></para>
|
||||
/// <para><b>상속 구조:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item>3D glTF 모델의 계층 구조를 UI 트리로 표현</item>
|
||||
/// <item>트리 리스트에서 항목 선택/확장/가시성 상태 관리</item>
|
||||
/// <item>모델 뷰어와 트리 리스트 간의 데이터 동기화</item>
|
||||
/// <item>UTKComponentListCategoryData - 카테고리용 (자식 항목을 가짐)</item>
|
||||
/// <item>UTKComponentListItemData - 일반 항목용 (리프 노드)</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>사용 예시:</b></para>
|
||||
/// <code>
|
||||
/// // 루트 항목 생성
|
||||
/// var root = new UTKComponentListItemData { id = 1, name = "루트" };
|
||||
///
|
||||
/// // 자식 항목 추가
|
||||
/// var child1 = new UTKComponentListItemData { id = 2, name = "자식1" };
|
||||
/// var child2 = new UTKComponentListItemData { id = 3, name = "자식2" };
|
||||
/// root.Add(child1);
|
||||
/// root.Add(child2);
|
||||
///
|
||||
/// // 손자 항목 추가
|
||||
/// var grandChild = new UTKComponentListItemData { id = 4, name = "손자" };
|
||||
/// child1.Add(grandChild);
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public class UTKComponentListItemData: IDisposable
|
||||
public abstract class UTKComponentListItemDataBase : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 항목의 고유 식별자입니다.
|
||||
@@ -49,16 +32,9 @@ namespace UVC.UIToolkit.List
|
||||
/// <summary>
|
||||
/// 항목의 표시 이름입니다.
|
||||
/// UI의 TreeView에서 사용자에게 보여지는 텍스트입니다.
|
||||
/// glTF 모델 로드 시 GameObject 이름이 자동으로 설정됩니다.
|
||||
/// </summary>
|
||||
public string name = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 추가 옵션 또는 설명 텍스트입니다.
|
||||
/// 검색 필터링 시 name과 함께 검색 대상이 됩니다.
|
||||
/// </summary>
|
||||
public string option = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 트리뷰에서 해당 항목이 펼쳐져 있는지 여부입니다.
|
||||
/// true: 자식 항목들이 표시됨 / false: 자식 항목들이 숨겨짐
|
||||
@@ -71,20 +47,6 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
public bool isSelected = false;
|
||||
|
||||
/// <summary>
|
||||
/// 부모 항목에 대한 참조입니다.
|
||||
/// 루트 항목의 경우 null입니다.
|
||||
/// Add() 메서드로 자식을 추가하면 자동으로 설정됩니다.
|
||||
/// </summary>
|
||||
public UTKComponentListItemData? parent;
|
||||
|
||||
/// <summary>
|
||||
/// 자식 항목들의 리스트입니다.
|
||||
/// 재귀적 트리 구조를 형성합니다.
|
||||
/// Add() 메서드를 통해 자식을 추가할 수 있습니다.
|
||||
/// </summary>
|
||||
public List<UTKComponentListItemData> children = new List<UTKComponentListItemData>();
|
||||
|
||||
/// <summary>
|
||||
/// 해당 항목(및 연결된 3D 오브젝트)의 가시성 상태입니다.
|
||||
/// UI의 눈 아이콘 버튼으로 토글되며,
|
||||
@@ -93,13 +55,6 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
public bool IsVisible = true;
|
||||
|
||||
/// <summary>
|
||||
/// 외부 시스템(예: 간트 차트)과의 연동을 위한 외부 키입니다.
|
||||
/// 검색 필터링 시 검색 대상에 포함됩니다.
|
||||
/// 모델과 차트 간의 동기화에 사용될 수 있습니다.
|
||||
/// </summary>
|
||||
public string ExternalKey = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 검색 결과를 감싸는 임시 그룹 항목인지 여부입니다.
|
||||
/// true인 경우 setting-btn이 숨겨지고 클릭되지 않습니다.
|
||||
@@ -107,29 +62,156 @@ namespace UVC.UIToolkit.List
|
||||
/// </summary>
|
||||
public bool isSearchResultGroup = false;
|
||||
|
||||
/// <summary>
|
||||
/// 부모 항목에 대한 참조입니다.
|
||||
/// 루트 항목의 경우 null입니다.
|
||||
/// Add() 메서드로 자식을 추가하면 자동으로 설정됩니다.
|
||||
/// </summary>
|
||||
public UTKComponentListItemDataBase? parent;
|
||||
|
||||
/// <summary>
|
||||
/// 자식 항목들의 리스트입니다.
|
||||
/// 카테고리 항목에서만 사용됩니다. 일반 항목에서는 빈 리스트입니다.
|
||||
/// </summary>
|
||||
public abstract List<UTKComponentListItemDataBase> children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 이 항목이 카테고리인지 여부를 반환합니다.
|
||||
/// </summary>
|
||||
public abstract bool IsCategory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 리소스를 해제합니다.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리(그룹) 항목을 나타내는 클래스입니다.
|
||||
/// 자식 항목들을 가질 수 있습니다.
|
||||
///
|
||||
/// <para><b>사용 예시:</b></para>
|
||||
/// <code>
|
||||
/// // 카테고리 생성
|
||||
/// var category = new UTKComponentListCategoryData { name = "카테고리1" };
|
||||
///
|
||||
/// // 일반 항목 추가
|
||||
/// var item1 = new UTKComponentListItemData { name = "항목1", option = "옵션1" };
|
||||
/// var item2 = new UTKComponentListItemData { name = "항목2", option = "옵션2" };
|
||||
/// category.Add(item1);
|
||||
/// category.Add(item2);
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public class UTKComponentListCategoryData : UTKComponentListItemDataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 자식 항목들의 리스트입니다.
|
||||
/// </summary>
|
||||
private List<UTKComponentListItemDataBase> _children = new List<UTKComponentListItemDataBase>();
|
||||
|
||||
/// <summary>
|
||||
/// 자식 항목들의 리스트를 반환합니다.
|
||||
/// </summary>
|
||||
public override List<UTKComponentListItemDataBase> children => _children;
|
||||
|
||||
/// <summary>
|
||||
/// 이 항목이 카테고리임을 나타냅니다.
|
||||
/// </summary>
|
||||
public override bool IsCategory => true;
|
||||
|
||||
/// <summary>
|
||||
/// 자식 항목을 추가합니다.
|
||||
/// 추가되는 자식의 parent 속성이 자동으로 현재 항목으로 설정됩니다.
|
||||
/// </summary>
|
||||
/// <param name="child">추가할 자식 항목</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var parent = new UTKComponentListItemData { name = "부모" };
|
||||
/// var child = new UTKComponentListItemData { name = "자식" };
|
||||
/// parent.Add(child);
|
||||
/// // child.parent == parent (자동 설정됨)
|
||||
/// </code>
|
||||
/// </example>
|
||||
public void Add(UTKComponentListItemData child)
|
||||
public void Add(UTKComponentListItemDataBase child)
|
||||
{
|
||||
child.parent = this;
|
||||
children.Add(child);
|
||||
_children.Add(child);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
/// <summary>
|
||||
/// 자식 항목을 제거합니다.
|
||||
/// </summary>
|
||||
/// <param name="child">제거할 자식 항목</param>
|
||||
/// <returns>제거 성공 여부</returns>
|
||||
public bool Remove(UTKComponentListItemDataBase child)
|
||||
{
|
||||
children.Clear();
|
||||
parent = null;
|
||||
if (_children.Remove(child))
|
||||
{
|
||||
child.parent = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 자식 항목을 제거합니다.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var child in _children)
|
||||
{
|
||||
child.parent = null;
|
||||
}
|
||||
_children.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리소스를 해제합니다.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 일반 항목(리프 노드)을 나타내는 클래스입니다.
|
||||
/// 자식 항목을 가지지 않습니다.
|
||||
///
|
||||
/// <para><b>사용 예시:</b></para>
|
||||
/// <code>
|
||||
/// var item = new UTKComponentListItemData
|
||||
/// {
|
||||
/// name = "항목 이름",
|
||||
/// option = "추가 옵션",
|
||||
/// ExternalKey = "외부 연동 키"
|
||||
/// };
|
||||
/// </code>
|
||||
/// </summary>
|
||||
public class UTKComponentListItemData : UTKComponentListItemDataBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 추가 옵션 또는 설명 텍스트입니다.
|
||||
/// 검색 필터링 시 name과 함께 검색 대상이 됩니다.
|
||||
/// </summary>
|
||||
public string option = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 외부 시스템(예: 간트 차트)과의 연동을 위한 외부 키입니다.
|
||||
/// 검색 필터링 시 검색 대상에 포함됩니다.
|
||||
/// 모델과 차트 간의 동기화에 사용될 수 있습니다.
|
||||
/// </summary>
|
||||
public string ExternalKey = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 일반 항목은 자식을 가지지 않으므로 빈 리스트를 반환합니다.
|
||||
/// </summary>
|
||||
private static readonly List<UTKComponentListItemDataBase> _emptyChildren = new List<UTKComponentListItemDataBase>();
|
||||
|
||||
/// <summary>
|
||||
/// 자식 항목들의 리스트를 반환합니다. 항상 빈 리스트입니다.
|
||||
/// </summary>
|
||||
public override List<UTKComponentListItemDataBase> children => _emptyChildren;
|
||||
|
||||
/// <summary>
|
||||
/// 이 항목이 카테고리가 아님을 나타냅니다.
|
||||
/// </summary>
|
||||
public override bool IsCategory => false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemSelected
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemSelected
|
||||
{
|
||||
get => _componentList?.OnItemSelected;
|
||||
set { if (_componentList != null) _componentList.OnItemSelected = value; }
|
||||
@@ -74,7 +74,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemDeselected
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemDeselected
|
||||
{
|
||||
get => _componentList?.OnItemDeselected;
|
||||
set { if (_componentList != null) _componentList.OnItemDeselected = value; }
|
||||
@@ -83,7 +83,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다.
|
||||
/// </summary>
|
||||
public event Action<UTKComponentListItemData, bool>? OnItemVisibilityChanged
|
||||
public event Action<UTKComponentListItemDataBase, bool>? OnItemVisibilityChanged
|
||||
{
|
||||
add { if (_componentList != null) _componentList.OnItemVisibilityChanged += value; }
|
||||
remove { if (_componentList != null) _componentList.OnItemVisibilityChanged -= value; }
|
||||
@@ -92,7 +92,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키).
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDeleted
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDeleted
|
||||
{
|
||||
get => _componentList?.OnItemDeleted;
|
||||
set { if (_componentList != null) _componentList.OnItemDeleted = value; }
|
||||
@@ -101,7 +101,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDoubleClicked
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDoubleClicked
|
||||
{
|
||||
get => _componentList?.OnItemDoubleClicked;
|
||||
set { if (_componentList != null) _componentList.OnItemDoubleClicked = value; }
|
||||
@@ -110,7 +110,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 아이콘을 클릭할 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<string, UTKComponentListItemData>? OnItemIconClicked
|
||||
public Action<string, UTKComponentListItemDataBase>? OnItemIconClicked
|
||||
{
|
||||
get => _componentList?.OnItemIconClicked;
|
||||
set { if (_componentList != null) _componentList.OnItemIconClicked = value; }
|
||||
@@ -174,7 +174,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// 트리 데이터를 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="roots">루트 항목들의 리스트</param>
|
||||
public void SetData(List<UTKComponentListItemData> roots)
|
||||
public void SetData(List<UTKComponentListItemDataBase> roots)
|
||||
{
|
||||
_componentList?.SetData(roots);
|
||||
}
|
||||
@@ -202,17 +202,17 @@ namespace UVC.UIToolkit.Window
|
||||
/// 루트 레벨에 새 항목을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
_componentList?.AddItem(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 부모 항목의 자식으로 새 항목을 추가합니다.
|
||||
/// 지정된 부모 카테고리의 자식으로 새 항목을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 항목</param>
|
||||
/// <param name="parent">부모 카테고리</param>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData? parent, UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListCategoryData? parent, UTKComponentListItemDataBase data)
|
||||
{
|
||||
_componentList?.AddItem(parent, data);
|
||||
}
|
||||
@@ -221,7 +221,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// 트리에서 항목을 완전히 삭제합니다.
|
||||
/// </summary>
|
||||
/// <param name="data">삭제할 항목 데이터</param>
|
||||
public void DeleteItem(UTKComponentListItemData data)
|
||||
public void DeleteItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
_componentList?.DeleteItem(data);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// </summary>
|
||||
/// <param name="data">변경할 항목 데이터</param>
|
||||
/// <param name="newName">새 이름</param>
|
||||
public void SetItemName(UTKComponentListItemData data, string newName)
|
||||
public void SetItemName(UTKComponentListItemDataBase data, string newName)
|
||||
{
|
||||
_componentList?.SetItemName(data, newName);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace UVC.UIToolkit.Window
|
||||
|
||||
#region 탭 관련 데이터 (Tab Data)
|
||||
/// <summary>원본 전체 데이터</summary>
|
||||
private List<UTKComponentListItemData> _originalRoots = new();
|
||||
private List<UTKComponentListItemDataBase> _originalRoots = new();
|
||||
|
||||
/// <summary>현재 선택된 탭 인덱스 (-1: 전체)</summary>
|
||||
private int _selectedTabIndex = ALL_TAB_INDEX;
|
||||
@@ -87,7 +87,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemSelected
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemSelected
|
||||
{
|
||||
get => _componentList?.OnItemSelected;
|
||||
set { if (_componentList != null) _componentList.OnItemSelected = value; }
|
||||
@@ -96,7 +96,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 선택 해제될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<List<UTKComponentListItemData>>? OnItemDeselected
|
||||
public Action<List<UTKComponentListItemDataBase>>? OnItemDeselected
|
||||
{
|
||||
get => _componentList?.OnItemDeselected;
|
||||
set { if (_componentList != null) _componentList.OnItemDeselected = value; }
|
||||
@@ -105,7 +105,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 항목의 가시성(눈 아이콘)이 변경될 때 발생합니다.
|
||||
/// </summary>
|
||||
public event Action<UTKComponentListItemData, bool>? OnItemVisibilityChanged
|
||||
public event Action<UTKComponentListItemDataBase, bool>? OnItemVisibilityChanged
|
||||
{
|
||||
add { if (_componentList != null) _componentList.OnItemVisibilityChanged += value; }
|
||||
remove { if (_componentList != null) _componentList.OnItemVisibilityChanged -= value; }
|
||||
@@ -114,7 +114,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 삭제될 때 발생합니다 (Delete 키).
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDeleted
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDeleted
|
||||
{
|
||||
get => _componentList?.OnItemDeleted;
|
||||
set { if (_componentList != null) _componentList.OnItemDeleted = value; }
|
||||
@@ -123,7 +123,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 메인/검색 리스트에서 항목이 더블클릭될 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<UTKComponentListItemData>? OnItemDoubleClicked
|
||||
public Action<UTKComponentListItemDataBase>? OnItemDoubleClicked
|
||||
{
|
||||
get => _componentList?.OnItemDoubleClicked;
|
||||
set { if (_componentList != null) _componentList.OnItemDoubleClicked = value; }
|
||||
@@ -132,7 +132,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// <summary>
|
||||
/// 아이콘을 클릭할 때 발생합니다.
|
||||
/// </summary>
|
||||
public Action<string, UTKComponentListItemData>? OnItemIconClicked
|
||||
public Action<string, UTKComponentListItemDataBase>? OnItemIconClicked
|
||||
{
|
||||
get => _componentList?.OnItemIconClicked;
|
||||
set { if (_componentList != null) _componentList.OnItemIconClicked = value; }
|
||||
@@ -200,9 +200,9 @@ namespace UVC.UIToolkit.Window
|
||||
/// 데이터의 카테고리를 분석하여 탭을 자동 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="roots">루트 항목들의 리스트</param>
|
||||
public void SetData(List<UTKComponentListItemData> roots)
|
||||
public void SetData(List<UTKComponentListItemDataBase> roots)
|
||||
{
|
||||
_originalRoots = roots ?? new List<UTKComponentListItemData>();
|
||||
_originalRoots = roots ?? new List<UTKComponentListItemDataBase>();
|
||||
|
||||
// 탭 생성 (카테고리 기반)
|
||||
CreateTabs();
|
||||
@@ -277,7 +277,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// 루트 레벨에 새 항목을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
_originalRoots.Add(data);
|
||||
CreateTabs();
|
||||
@@ -285,11 +285,11 @@ namespace UVC.UIToolkit.Window
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 부모 항목의 자식으로 새 항목을 추가합니다.
|
||||
/// 지정된 부모 카테고리의 자식으로 새 항목을 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="parent">부모 항목</param>
|
||||
/// <param name="parent">부모 카테고리</param>
|
||||
/// <param name="data">추가할 항목 데이터</param>
|
||||
public void AddItem(UTKComponentListItemData? parent, UTKComponentListItemData data)
|
||||
public void AddItem(UTKComponentListCategoryData? parent, UTKComponentListItemDataBase data)
|
||||
{
|
||||
if (parent == null)
|
||||
{
|
||||
@@ -297,8 +297,7 @@ namespace UVC.UIToolkit.Window
|
||||
return;
|
||||
}
|
||||
|
||||
parent.children ??= new List<UTKComponentListItemData>();
|
||||
parent.children.Add(data);
|
||||
parent.Add(data);
|
||||
|
||||
// 탭 및 표시 갱신
|
||||
CreateTabs();
|
||||
@@ -309,7 +308,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// 트리에서 항목을 완전히 삭제합니다.
|
||||
/// </summary>
|
||||
/// <param name="data">삭제할 항목 데이터</param>
|
||||
public void DeleteItem(UTKComponentListItemData data)
|
||||
public void DeleteItem(UTKComponentListItemDataBase data)
|
||||
{
|
||||
if (RemoveItemFromRoots(data))
|
||||
{
|
||||
@@ -323,7 +322,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// </summary>
|
||||
/// <param name="data">변경할 항목 데이터</param>
|
||||
/// <param name="newName">새 이름</param>
|
||||
public void SetItemName(UTKComponentListItemData data, string newName)
|
||||
public void SetItemName(UTKComponentListItemDataBase data, string newName)
|
||||
{
|
||||
_componentList?.SetItemName(data, newName);
|
||||
}
|
||||
@@ -374,9 +373,9 @@ namespace UVC.UIToolkit.Window
|
||||
_tabContainer.Add(allTab);
|
||||
_tabButtons.Add(allTab);
|
||||
|
||||
// 카테고리별 탭 생성 (루트 항목들이 그룹이면 해당 이름으로 탭 생성)
|
||||
// 카테고리별 탭 생성 (카테고리 타입인 항목들만 탭으로 표시)
|
||||
var categories = _originalRoots
|
||||
.Where(r => r.children != null && r.children.Count > 0)
|
||||
.Where(r => r.IsCategory && r.children.Count > 0)
|
||||
.Select(r => r.name)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
@@ -433,7 +432,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// </summary>
|
||||
/// <param name="tabIndex">탭 인덱스 (-1: 전체)</param>
|
||||
/// <returns>필터링된 데이터</returns>
|
||||
private List<UTKComponentListItemData> FilterDataByTab(int tabIndex)
|
||||
private List<UTKComponentListItemDataBase> FilterDataByTab(int tabIndex)
|
||||
{
|
||||
// 전체 탭이면 모든 데이터 반환
|
||||
if (tabIndex == ALL_TAB_INDEX)
|
||||
@@ -443,12 +442,12 @@ namespace UVC.UIToolkit.Window
|
||||
|
||||
// 카테고리 탭이면 해당 카테고리의 자식들만 반환 (부모 없이 자식만 표시)
|
||||
var categories = _originalRoots
|
||||
.Where(r => r.children != null && r.children.Count > 0)
|
||||
.Where(r => r.IsCategory && r.children.Count > 0)
|
||||
.ToList();
|
||||
|
||||
if (tabIndex >= 0 && tabIndex < categories.Count)
|
||||
{
|
||||
return categories[tabIndex].children ?? new List<UTKComponentListItemData>();
|
||||
return categories[tabIndex].children ?? new List<UTKComponentListItemDataBase>();
|
||||
}
|
||||
|
||||
return _originalRoots;
|
||||
@@ -461,7 +460,7 @@ namespace UVC.UIToolkit.Window
|
||||
/// </summary>
|
||||
/// <param name="target">삭제할 항목</param>
|
||||
/// <returns>삭제 성공 여부</returns>
|
||||
private bool RemoveItemFromRoots(UTKComponentListItemData target)
|
||||
private bool RemoveItemFromRoots(UTKComponentListItemDataBase target)
|
||||
{
|
||||
if (_originalRoots.Remove(target))
|
||||
{
|
||||
@@ -485,13 +484,17 @@ namespace UVC.UIToolkit.Window
|
||||
/// <param name="parent">부모 노드</param>
|
||||
/// <param name="target">삭제할 대상 항목</param>
|
||||
/// <returns>삭제 성공 여부</returns>
|
||||
private bool RemoveFromChildren(UTKComponentListItemData parent, UTKComponentListItemData target)
|
||||
private bool RemoveFromChildren(UTKComponentListItemDataBase parent, UTKComponentListItemDataBase target)
|
||||
{
|
||||
if (parent.children == null || parent.children.Count == 0) return false;
|
||||
|
||||
if (parent.children.Remove(target))
|
||||
// 카테고리인 경우에만 자식에서 삭제 가능
|
||||
if (parent is UTKComponentListCategoryData category)
|
||||
{
|
||||
return true;
|
||||
if (category.Remove(target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in parent.children)
|
||||
|
||||
Reference in New Issue
Block a user