UTKAccodion 완료. UTKComponentList 수정 중

This commit is contained in:
logonkhi
2026-01-22 20:12:22 +09:00
parent e00953de52
commit 59d473c87b
59 changed files with 2482 additions and 498 deletions

View File

@@ -32,6 +32,88 @@ namespace UVC.UIToolkit
/// <item>비동기 이미지 로딩 및 캐싱</item>
/// </list>
///
/// <para><b>UXML 사용 예시:</b></para>
/// <code><![CDATA[
/// <!-- UXML 파일에서 UTKAccordionList 사용 -->
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
/// <utk:UTKAccordionList name="accordion-list" />
/// </ui:UXML>
/// ]]></code>
///
/// <para><b>C# 사용 예시:</b></para>
/// <code><![CDATA[
/// // 1. 아코디언 리스트 참조 획득
/// var accordionList = root.Q<UTKAccordionList>("accordion-list");
///
/// // 2. 데이터 구성 - 섹션(카테고리)과 그리드 아이템
/// var data = new List<UTKAccordionItemData>
/// {
/// // 섹션 생성 (카테고리)
/// new UTKAccordionItemData
/// {
/// nodeType = UTKAccordionNodeType.Section,
/// name = "가구",
/// isExpanded = true,
/// children = new List<UTKAccordionItemData>
/// {
/// // 그리드 아이템 (이미지 + 캡션)
/// new UTKAccordionItemData
/// {
/// nodeType = UTKAccordionNodeType.GridItem,
/// name = "의자",
/// caption = "사무용 의자",
/// imagePath = "Prefabs/Thumbnails/chair",
/// prefabPath = "Prefabs/Furniture/Chair"
/// },
/// new UTKAccordionItemData
/// {
/// nodeType = UTKAccordionNodeType.GridItem,
/// name = "책상",
/// caption = "사무용 책상",
/// imagePath = "Prefabs/Thumbnails/desk",
/// prefabPath = "Prefabs/Furniture/Desk"
/// }
/// }
/// }
/// };
///
/// // 3. 데이터 설정
/// accordionList.SetData(data);
///
/// // 4. 이벤트 구독 - 아이템 클릭
/// accordionList.OnItemClick += (item) =>
/// {
/// Debug.Log($"클릭된 아이템: {item.name}");
/// };
///
/// // 5. 드래그 앤 드롭 이벤트
/// accordionList.OnItemDrop += (item) =>
/// {
/// // 드롭된 위치에 프리팹 인스턴스화
/// if (!string.IsNullOrEmpty(item.prefabPath))
/// {
/// var prefab = Resources.Load<GameObject>(item.prefabPath);
/// Instantiate(prefab, dropPosition, Quaternion.identity);
/// }
/// };
///
/// // 6. 리스트 영역 밖으로 드래그 시 3D 미리보기 표시
/// accordionList.OnDragExitList += (item, screenPos) =>
/// {
/// Show3DPreview(item.prefabPath, screenPos);
/// };
///
/// // 7. 검색 실행
/// accordionList.Search("의자");
///
/// // 8. 모든 섹션 펼치기/접기
/// accordionList.ExpandAll();
/// accordionList.CollapseAll();
///
/// // 9. 리소스 해제 (OnDestroy에서 호출)
/// accordionList.Dispose();
/// ]]></code>
///
/// <para><b>관련 리소스:</b></para>
/// <list type="bullet">
/// <item>Resources/UIToolkit/List/UTKAccordionList.uxml - 메인 레이아웃</item>
@@ -47,6 +129,7 @@ namespace UVC.UIToolkit
#region (Constants)
private const string UXML_PATH = "UIToolkit/List/UTKAccordionList";
private const string USS_PATH = "UIToolkit/List/UTKAccordionListUss";
private const string SECTION_UXML_PATH = "UIToolkit/List/UTKAccordionSection";
private const string HORIZONTAL_ITEM_UXML_PATH = "UIToolkit/List/UTKAccordionHorizontalItem";
private const string GRID_ITEM_UXML_PATH = "UIToolkit/List/UTKAccordionGridItem";
@@ -67,7 +150,7 @@ namespace UVC.UIToolkit
#region UI (UI Component References)
private TextField? _searchField;
private Button? _clearButton;
private UTKButton? _clearButton;
private Label? _searchResultLabel;
private TreeView? _treeView;
@@ -179,9 +262,6 @@ namespace UVC.UIToolkit
public UTKAccordionList()
{
// 테마 적용
UTKThemeManager.Instance.ApplyThemeToElement(this);
// 메인 UXML 로드
var visualTree = Resources.Load<VisualTreeAsset>(UXML_PATH);
if (visualTree == null)
@@ -194,10 +274,27 @@ namespace UVC.UIToolkit
// 템플릿 로드
LoadTemplates();
// 테마 적용 및 변경 구독
UTKThemeManager.Instance.ApplyThemeToElement(this);
SubscribeToThemeChanges();
// USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨)
var uss = Resources.Load<StyleSheet>(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
// UI 요소 참조 획득
_searchField = this.Q<TextField>("search-field");
_clearButton = this.Q<Button>("clear-btn");
_clearButton = this.Q<UTKButton>("clear-btn");
_searchResultLabel = this.Q<Label>("search-result-label");
// Clear 버튼 아이콘 설정
if (_clearButton != null)
{
_clearButton.SetMaterialIcon(UTKMaterialIcons.Close, 12);
}
_treeView = this.Q<TreeView>("accordion-tree-view");
// 초기화
@@ -234,7 +331,7 @@ namespace UVC.UIToolkit
// 검색어 지우기 버튼
if (_clearButton != null)
{
_clearButton.clicked += OnClearButtonClicked;
_clearButton.OnClicked += OnClearButtonClicked;
// 초기에는 숨김
_clearButton.style.display = DisplayStyle.None;
}
@@ -734,7 +831,7 @@ namespace UVC.UIToolkit
var tailBtn = CreateTailButton(tailSpec);
if (tailBtn != null)
{
tailBtn.clicked += () =>
tailBtn.OnClicked += () =>
{
// 레거시 이벤트
var legacyItem = ConvertToLegacyHorizontalItem(item);
@@ -897,21 +994,11 @@ namespace UVC.UIToolkit
}, TrickleDown.TrickleDown);
}
private Button CreateTailButton(UTKAccordionContentSpec spec)
private UTKButton CreateTailButton(UTKAccordionContentSpec spec)
{
var btn = new Button();
var btn = new UTKButton("", spec.IconName ?? "", UTKButton.ButtonVariant.Text, spec.IconSize ?? 12) { IconOnly = true };
btn.AddToClassList("accordion-tail-button");
var icon = new VisualElement();
icon.AddToClassList("accordion-tail-icon");
if (!string.IsNullOrEmpty(spec.IconName))
{
LoadIconAsync(icon, spec.IconName).Forget();
}
btn.Add(icon);
if (!string.IsNullOrEmpty(spec.Tooltip))
{
btn.tooltip = spec.Tooltip;
@@ -924,8 +1011,24 @@ namespace UVC.UIToolkit
{
if (spec.Kind == UTKAccordionContentKind.Image && !string.IsNullOrEmpty(spec.ImagePath))
{
BindImageAsync(imageElement, spec.ImagePath).Forget();
imageElement.style.unityBackgroundImageTintColor = spec.ImageColor;
string iconChar = UTKMaterialIcons.GetIcon(spec.ImagePath);
Debug.Log($"[UTKAccordionList] Content image path: '{spec.ImagePath} ' → icon char: '{iconChar}'");
if (iconChar != string.Empty)
{
Debug.Log($"[UTKAccordionList] Using material icon '{spec.ImagePath}' for content image.");
// 머티리얼 아이콘 사용
Label iconLabel = new Label(iconChar);
UTKMaterialIcons.ApplyIconStyle(iconLabel, null); //uss에서 설정하기에 null 전달
imageElement.Add(iconLabel);
if(spec.ImageColor != null) iconLabel.style.color = spec.ImageColor.Value;
}
else
{
// 일반 이미지 로드
BindImageAsync(imageElement, spec.ImagePath).Forget();
if(spec.ImageColor != null) imageElement.style.unityBackgroundImageTintColor = spec.ImageColor.Value;
}
}
}
@@ -1586,6 +1689,24 @@ namespace UVC.UIToolkit
#endregion
#region (Theme)
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback<DetachFromPanelEvent>(_ =>
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
});
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region IDisposable
public void Dispose()
@@ -1593,6 +1714,9 @@ namespace UVC.UIToolkit
if (_disposed) return;
_disposed = true;
// 테마 변경 이벤트 해제
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
// 이미지 로딩 취소
CancelImageLoading();
@@ -1614,7 +1738,8 @@ namespace UVC.UIToolkit
if (_clearButton != null)
{
_clearButton.clicked -= OnClearButtonClicked;
_clearButton.OnClicked -= OnClearButtonClicked;
_clearButton.Dispose();
}
// TreeView 이벤트 해제

View File

@@ -47,11 +47,14 @@ namespace UVC.UIToolkit
public string? ImagePath { get; set; }
/// <summary>이미지 색상</summary>
public Color ImageColor { get; set; } = Color.white;
public Color? ImageColor { get; set; }
/// <summary>아이콘 이름 또는 경로</summary>
public string? IconName { get; set; }
/// <summary>아이콘 크기 (정사각형 기준, 픽셀 단위)</summary>
public int? IconSize { get; set; }
/// <summary>툴팁 텍스트</summary>
public string? Tooltip { get; set; }
@@ -77,16 +80,17 @@ namespace UVC.UIToolkit
{
Kind = UTKAccordionContentKind.Image,
ImagePath = path,
ImageColor = color ?? Color.white,
ImageColor = color,
Tooltip = tooltip
};
/// <summary>아이콘 버튼 스펙을 생성합니다.</summary>
public static UTKAccordionContentSpec FromIconButton(string iconName, string? actionId = null, string? tooltip = null, object? userData = null)
public static UTKAccordionContentSpec FromIconButton(string iconName, int iconSize, string? actionId = null, string? tooltip = null, object? userData = null)
=> new UTKAccordionContentSpec
{
Kind = UTKAccordionContentKind.IconButton,
IconName = iconName,
IconSize = iconSize,
ActionId = actionId,
Tooltip = tooltip,
UserData = userData

View File

@@ -27,22 +27,123 @@ namespace UVC.UIToolkit
/// <item>선택 이벤트 처리</item>
/// </list>
///
/// <para><b>UXML에서 사용:</b></para>
/// <code>
/// <UVC.UIToolkit.Window.UTKComponentList name="tree-list" />
/// </code>
/// <para><b>UXML 사용 예시:</b></para>
/// <code><![CDATA[
/// <!-- UXML 파일에서 UTKComponentList 사용 -->
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
/// <utk:UTKComponentList name="component-list" />
/// </ui:UXML>
/// ]]></code>
///
/// <para><b>코드에서 사용:</b></para>
/// <code>
/// var list = root.Q<UTKComponentList>();
/// list.OnSelectionChanged += (item) => Debug.Log($"선택: {item.name}");
/// list.OnVisibilityChanged += (item) => model.SetActive(item.id, item.IsVisible);
/// list.SetData(treeItems);
/// </code>
/// <para><b>C# 사용 예시:</b></para>
/// <code><![CDATA[
/// // 1. 컴포넌트 리스트 참조 획득
/// var componentList = root.Q<UTKComponentList>("component-list");
///
/// // 2. 데이터 구성 - 카테고리(그룹)와 일반 아이템
/// var data = new List<UTKComponentListItemDataBase>
/// {
/// // 카테고리(그룹) 생성
/// new UTKComponentListCategoryData
/// {
/// name = "모델 그룹 A",
/// isExpanded = true,
/// children = new List<UTKComponentListItemDataBase>
/// {
/// // 일반 아이템
/// new UTKComponentListItemData
/// {
/// name = "의자 모델",
/// ExternalKey = "chair_001",
/// IsVisible = true
/// },
/// new UTKComponentListItemData
/// {
/// name = "책상 모델",
/// ExternalKey = "desk_001",
/// IsVisible = true
/// }
/// }
/// }
/// };
///
/// // 3. 데이터 설정
/// componentList.SetData(data);
///
/// // 4. 선택 이벤트 구독 - 아이템 선택 시 호출
/// componentList.OnItemSelected += (selectedItems) =>
/// {
/// foreach (var item in selectedItems)
/// {
/// Debug.Log($"선택됨: {item.name}");
/// }
/// };
///
/// // 5. 선택 해제 이벤트 구독
/// componentList.OnItemDeselected += (deselectedItems) =>
/// {
/// foreach (var item in deselectedItems)
/// {
/// Debug.Log($"선택 해제: {item.name}");
/// }
/// };
///
/// // 6. 가시성 변경 이벤트 구독 - 눈 아이콘 클릭 시 호출
/// componentList.OnItemVisibilityChanged += (item, isVisible) =>
/// {
/// // 3D 모델의 GameObject 활성화/비활성화
/// var gameObject = FindGameObjectByKey(item.ExternalKey);
/// if (gameObject != null)
/// {
/// gameObject.SetActive(isVisible);
/// }
/// };
///
/// // 7. 삭제 이벤트 구독 (Delete/Backspace 키)
/// componentList.EnabledDeleteItem = true; // 삭제 기능 활성화 필수
/// componentList.OnItemDeleted += (item) =>
/// {
/// Debug.Log($"삭제 요청: {item.name}");
/// componentList.DeleteItem(item); // 리스트에서 제거
/// };
///
/// // 8. 더블클릭 이벤트 구독
/// componentList.OnItemDoubleClicked += (item) =>
/// {
/// Debug.Log($"더블클릭: {item.name}");
/// // 카메라 포커스 등의 동작 수행
/// };
///
/// // 9. 아이콘 클릭 이벤트 (setting-btn, search-btn)
/// componentList.OnItemIconClicked += (iconName, item) =>
/// {
/// if (iconName == "setting-btn")
/// {
/// ShowCategorySettings(item);
/// }
/// };
///
/// // 10. 검색 실행
/// componentList.ApplySearch("의자");
///
/// // 11. 프로그래밍 방식 선택
/// componentList.SelectItem("의자 모델", notify: true);
/// componentList.DeselectItem("의자 모델", notify: false);
/// componentList.ClearSelection();
///
/// // 12. 아이템 추가/삭제
/// var newItem = new UTKComponentListItemData { name = "새 아이템" };
/// componentList.AddItem(newItem); // 루트에 추가
/// componentList.AddItem(categoryData, newItem); // 특정 카테고리에 추가
/// componentList.DeleteItem(newItem); // 삭제
///
/// // 13. 리소스 해제 (OnDestroy에서 호출)
/// componentList.Dispose();
/// ]]></code>
///
/// <para><b>관련 리소스:</b></para>
/// <list type="bullet">
/// <item>Resources/UIToolkit/Window/UTKComponentList.uxml - 메인 레이아웃</item>
/// <item>Resources/UIToolkit/List/UTKComponentList.uxml - 메인 레이아웃</item>
/// <item>Resources/UIToolkit/List/UTKComponentListItem.uxml - 개별 항목 템플릿</item>
/// <item>Resources/UIToolkit/List/UTKComponentListGroupItem.uxml - 그룹 항목 템플릿</item>
/// </list>
@@ -63,6 +164,8 @@ namespace UVC.UIToolkit
/// <summary>메인 UXML 파일 경로 (Resources 폴더 기준)</summary>
private const string UXML_PATH = "UIToolkit/List/UTKComponentList";
private const string USS_PATH = "UIToolkit/List/UTKComponentListUss";
/// <summary>일반 항목 UXML 파일 경로 (Resources 폴더 기준)</summary>
private const string ITEM_UXML_PATH = "UIToolkit/List/UTKComponentListItem";
@@ -85,8 +188,8 @@ namespace UVC.UIToolkit
/// <summary>Unity UI Toolkit의 TreeView 컴포넌트</summary>
private TreeView? _treeView;
/// <summary>검색어 지우기 버튼</summary>
private Button? _clearButton;
/// <summary>검색어 지우기 버튼 (UTKButton)</summary>
private UTKButton? _clearButton;
#endregion
#region (Internal Data)
@@ -199,6 +302,8 @@ namespace UVC.UIToolkit
}
visualTree!.CloneTree(this);
// 2. 항목 템플릿 로드
_itemTemplate = Resources.Load<VisualTreeAsset>(ITEM_UXML_PATH);
_groupItemTemplate = Resources.Load<VisualTreeAsset>(GROUP_ITEM_UXML_PATH);
@@ -207,13 +312,29 @@ namespace UVC.UIToolkit
if (_groupItemTemplate == null)
Debug.LogError($"[UTKComponentList] Group Item UXML not found at: {GROUP_ITEM_UXML_PATH}");
// 테마 적용 및 변경 구독
UTKThemeManager.Instance.ApplyThemeToElement(this);
SubscribeToThemeChanges();
// 2. 자식 요소 참조 획득 (UXML의 name 속성으로 찾음)
// USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨)
var uss = Resources.Load<StyleSheet>(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
// 3. 자식 요소 참조 획득 (UXML의 name 속성으로 찾음)
_searchField = this.Q<TextField>("search-field");
_treeView = this.Q<TreeView>("main-tree-view");
_clearButton = this.Q<Button>("clear-btn");
_clearButton = this.Q<UTKButton>("clear-btn");
// 3. 이벤트 연결 및 로직 초기화
// 4. Clear 버튼 아이콘 설정
if (_clearButton != null)
{
_clearButton.SetMaterialIcon(UTKMaterialIcons.Close, 12);
}
// 5. 이벤트 연결 및 로직 초기화
InitializeLogic();
}
#endregion
@@ -251,15 +372,7 @@ namespace UVC.UIToolkit
if(_clearButton != null)
{
_clearButton.style.display = DisplayStyle.None; // 초기에는 숨김
_clearButton.clicked += () =>
{
if (_searchField.value.Length > 0)
{
_searchField.value = string.Empty;
OnSearch(string.Empty);
}
_clearButton.style.display = DisplayStyle.None; // 클리어 후 숨김
};
_clearButton.OnClicked += OnClearButtonClicked;
}
// 스크롤바 hover/active 색상 설정
@@ -1372,6 +1485,22 @@ namespace UVC.UIToolkit
OnSearch(_searchField?.value ?? string.Empty);
}
/// <summary>
/// Clear 버튼 클릭 이벤트를 처리합니다.
/// </summary>
private void OnClearButtonClicked()
{
if (_searchField != null && _searchField.value.Length > 0)
{
_searchField.value = string.Empty;
OnSearch(string.Empty);
}
if (_clearButton != null)
{
_clearButton.style.display = DisplayStyle.None;
}
}
/// <summary>
/// 검색어에 따라 트리를 필터링합니다.
/// 검색어가 비어있으면 원본 데이터로 복원됩니다.
@@ -1562,6 +1691,24 @@ namespace UVC.UIToolkit
}
#endregion
#region (Theme)
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback<DetachFromPanelEvent>(_ =>
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
});
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region IDisposable
/// <summary>
/// 리소스를 해제하고 이벤트 핸들러를 정리합니다.
@@ -1571,6 +1718,9 @@ namespace UVC.UIToolkit
if (_disposed) return;
_disposed = true;
// 테마 변경 이벤트 해제
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
// 검색 필드 이벤트 해제
if (_searchField != null)
{
@@ -1589,6 +1739,13 @@ namespace UVC.UIToolkit
_treeView.makeItem = null;
}
// Clear 버튼 이벤트 해제 및 정리
if (_clearButton != null)
{
_clearButton.OnClicked -= OnClearButtonClicked;
_clearButton.Dispose();
}
// 외부 이벤트 구독자 정리
OnItemVisibilityChanged = null;
OnItemSelected = null;

View File

@@ -23,22 +23,125 @@ namespace UVC.UIToolkit
/// <para><b>주요 기능:</b></para>
/// <list type="bullet">
/// <item>이미지+텍스트 형태의 아이템을 2열 그리드로 표시</item>
/// <item>실시간 검색 필터링 (3글자 이상)</item>
/// <item>실시간 검색 필터링 (2글자 이상)</item>
/// <item>드래그 앤 드롭 지원</item>
/// <item>가상화를 통한 대량 데이터 성능 최적화</item>
/// </list>
///
/// <para><b>UXML에서 사용:</b></para>
/// <code>
/// <uvc:UTKImageList name="image-list" />
/// </code>
/// <para><b>UXML 사용 예시:</b></para>
/// <code><![CDATA[
/// <!-- UXML 파일에서 UTKImageList 사용 -->
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
/// <utk:UTKImageList name="image-list" />
/// </ui:UXML>
/// ]]></code>
///
/// <para><b>코드에서 사용:</b></para>
/// <code>
/// var list = root.Q<UTKImageList>();
/// list.OnItemClick += (item) => Debug.Log($"클릭: {item.itemName}");
/// list.SetData(imageItems);
/// </code>
/// <para><b>C# 사용 예시:</b></para>
/// <code><![CDATA[
/// // 1. 이미지 리스트 참조 획득
/// var imageList = root.Q<UTKImageList>("image-list");
///
/// // 2. 데이터 구성 - 이미지 경로와 이름을 가진 아이템들
/// var data = new List<UTKImageListItemData>
/// {
/// new UTKImageListItemData
/// {
/// itemName = "의자",
/// imagePath = "Prefabs/Thumbnails/chair",
/// prefabPath = "Prefabs/Furniture/Chair"
/// },
/// new UTKImageListItemData
/// {
/// itemName = "책상",
/// imagePath = "Prefabs/Thumbnails/desk",
/// prefabPath = "Prefabs/Furniture/Desk"
/// },
/// new UTKImageListItemData
/// {
/// itemName = "소파",
/// imagePath = "Prefabs/Thumbnails/sofa",
/// prefabPath = "Prefabs/Furniture/Sofa"
/// }
/// };
///
/// // 3. 데이터 설정
/// imageList.SetData(data);
///
/// // 4. 아이템 클릭 이벤트 구독
/// imageList.OnItemClick += (item) =>
/// {
/// Debug.Log($"클릭된 아이템: {item.itemName}");
/// };
///
/// // 5. 드래그 앤 드롭 이벤트 (씬에 프리팹 배치용)
/// imageList.OnItemBeginDrag += (item, screenPos) =>
/// {
/// Debug.Log($"드래그 시작: {item.itemName}");
/// };
///
/// imageList.OnItemDrag += (item, screenPos) =>
/// {
/// // 드래그 중 - 3D 미리보기 위치 업데이트 등
/// };
///
/// imageList.OnItemEndDrag += (item, screenPos) =>
/// {
/// Debug.Log($"드래그 종료: {item.itemName}");
/// };
///
/// imageList.OnItemDrop += (item) =>
/// {
/// // 드롭 완료 - 프리팹 인스턴스화
/// if (!string.IsNullOrEmpty(item.prefabPath))
/// {
/// var prefab = Resources.Load<GameObject>(item.prefabPath);
/// Instantiate(prefab, dropPosition, Quaternion.identity);
/// }
/// };
///
/// // 6. 리스트 영역 밖으로 드래그 시 3D 미리보기 표시
/// imageList.OnDragExitList += (item, screenPos) =>
/// {
/// // 리스트 밖 = 3D 씬 영역
/// Show3DPreview(item.prefabPath, screenPos);
/// };
///
/// // 7. 리스트 영역 안으로 다시 들어왔을 때 미리보기 숨김
/// imageList.OnDragEnterList += (item, screenPos) =>
/// {
/// Hide3DPreview();
/// };
///
/// // 8. 드래그 영역 설정 (부모 윈도우 영역 기준으로 체크)
/// imageList.DragBoundsElement = parentWindow;
///
/// // 9. 드래그 고스트 이미지 따라다니기 설정
/// imageList.DragImageFollowCursor = true;
///
/// // 10. 검색 실행 (2글자 이상)
/// imageList.ApplySearch("의자");
///
/// // 11. 현재 검색어 확인
/// string currentQuery = imageList.SearchQuery;
///
/// // 12. 아이템 추가/제거
/// var newItem = new UTKImageListItemData { itemName = "새 아이템" };
/// imageList.AddItem(newItem);
/// imageList.RemoveItem(newItem);
///
/// // 13. 전체 삭제
/// imageList.Clear();
///
/// // 14. 아이템 개수 확인
/// int count = imageList.ItemCount;
///
/// // 15. 리스트 표시/숨김
/// imageList.Show();
/// imageList.Hide();
///
/// // 16. 리소스 해제 (OnDestroy에서 호출)
/// imageList.Dispose();
/// ]]></code>
///
/// <para><b>관련 리소스:</b></para>
/// <list type="bullet">
@@ -54,6 +157,7 @@ namespace UVC.UIToolkit
/// <summary>메인 UXML 파일 경로 (Resources 폴더 기준)</summary>
private const string UXML_PATH = "UIToolkit/List/UTKImageList";
private const string USS_PATH = "UIToolkit/List/UTKImageListUss";
/// <summary>아이템 UXML 파일 경로 (Resources 폴더 기준)</summary>
private const string ITEM_UXML_PATH = "UIToolkit/List/UTKImageListItem";
@@ -84,8 +188,8 @@ namespace UVC.UIToolkit
/// <summary>Unity UI Toolkit의 ListView 컴포넌트</summary>
private ListView? _listView;
/// <summary>검색어 지우기 버튼</summary>
private Button? _clearButton;
/// <summary>검색어 지우기 버튼 (UTKButton)</summary>
private UTKButton? _clearButton;
/// <summary>검색 결과 건수 라벨</summary>
private Label? _searchResultLabel;
@@ -248,13 +352,30 @@ namespace UVC.UIToolkit
Debug.LogError($"[UTKImageList] Item UXML not found at: {ITEM_UXML_PATH}");
}
// 3. UI 요소 참조 획득
// 3. 테마 적용 및 변경 구독
UTKThemeManager.Instance.ApplyThemeToElement(this);
SubscribeToThemeChanges();
// USS 로드 (테마 변수 스타일시트 이후에 로드되어야 변수가 해석됨)
var uss = Resources.Load<StyleSheet>(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
// 4. UI 요소 참조 획득
_searchField = this.Q<TextField>("search-field");
_listView = this.Q<ListView>("main-list-view");
_clearButton = this.Q<Button>("clear-btn");
_clearButton = this.Q<UTKButton>("clear-btn");
_searchResultLabel = this.Q<Label>("search-result-label");
// 4. 이벤트 연결 및 로직 초기화
// 5. Clear 버튼 아이콘 설정
if (_clearButton != null)
{
_clearButton.SetMaterialIcon(UTKMaterialIcons.Close, 12);
}
// 6. 이벤트 연결 및 로직 초기화
InitializeLogic();
}
@@ -277,7 +398,7 @@ namespace UVC.UIToolkit
// 검색어 지우기 버튼 이벤트 등록
if (_clearButton != null)
{
_clearButton.clicked += OnClearButtonClicked;
_clearButton.OnClicked += OnClearButtonClicked;
_clearButton.style.display = DisplayStyle.None; // 초기에는 숨김
}
@@ -1144,6 +1265,24 @@ namespace UVC.UIToolkit
#endregion
#region (Theme)
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback<DetachFromPanelEvent>(_ =>
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
});
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region IDisposable
/// <summary>
@@ -1154,6 +1293,9 @@ namespace UVC.UIToolkit
if (_disposed) return;
_disposed = true;
// 테마 변경 이벤트 해제
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
// 이미지 로딩 취소
CancelImageLoading();
@@ -1176,7 +1318,8 @@ namespace UVC.UIToolkit
if (_clearButton != null)
{
_clearButton.clicked -= OnClearButtonClicked;
_clearButton.OnClicked -= OnClearButtonClicked;
_clearButton.Dispose();
}
// 드래그 고스트 정리