#nullable enable
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
namespace UVC.UIToolkit
{
///
/// 탭 정렬 방향
///
public enum TabAlign
{
/// 탭이 위쪽에 배치
Top,
/// 탭이 아래쪽에 배치
Bottom,
/// 탭이 왼쪽에 배치 (세로 정렬)
Left,
/// 탭이 오른쪽에 배치 (세로 정렬)
Right
}
///
/// 탭 뷰 컴포넌트.
/// Unity TabView를 래핑하여 커스텀 스타일을 적용합니다.
/// 여러 콘텐츠 페이지를 탭으로 전환하여 표시합니다.
///
///
/// TabView(탭 뷰)란?
///
/// TabView는 여러 페이지의 콘텐츠를 탭 버튼으로 전환하여 표시하는 UI 컴포넌트입니다.
/// 같은 공간에 여러 내용을 담을 수 있어 화면 공간을 효율적으로 사용합니다.
/// 설정 페이지, 에디터 창, 프로필 화면 등에서 널리 사용됩니다.
///
///
/// TabView 구성:
///
/// - 탭 헤더 - 탭 버튼들이 나열된 영역
/// - 탭 콘텐츠 - 선택된 탭의 내용이 표시되는 영역
///
///
/// 주요 속성:
///
/// - SelectedIndex - 현재 선택된 탭 인덱스
/// - UTKTabs - 탭 목록 (읽기 전용)
/// - Align - 탭 정렬 방향 (Top, Bottom, Left, Right)
///
///
/// 주요 메서드:
///
/// - AddUTKTab(string, VisualElement) - 탭 추가
/// - AddTab(UTKTab) - UTKTab 인스턴스 추가
/// - RemoveTab(UTKTab) - 탭 제거
/// - ClearTabs() - 모든 탭 제거
///
///
/// 이벤트:
///
/// - OnTabChanged - 탭이 변경될 때 (인덱스, Tab 전달)
///
///
/// 실제 활용 예시:
///
/// - 설정 창 - 일반/고급/정보 탭
/// - 에디터 - 씬/게임/애셋 탭
/// - 프로필 - 정보/활동/설정 탭
/// - 문서 뷰어 - 다중 문서 탭
///
///
///
/// C# 코드에서 사용:
///
/// // 탭 뷰 생성
/// var tabView = new UTKTabView();
///
/// // 탭 추가
/// var tab1 = tabView.AddUTKTab("일반");
/// tab1.Add(new Label("일반 설정 내용"));
///
/// var tab2 = tabView.AddUTKTab("고급");
/// tab2.Add(new Label("고급 설정 내용"));
///
/// // 탭 변경 이벤트
/// tabView.OnTabChanged += (index, tab) => Debug.Log($"탭 {index} 선택됨");
///
/// // 탭 정렬 방향 설정
/// tabView.Align = TabAlign.Left; // 탭을 왼쪽에 세로로 배치
///
/// // 탭 선택
/// tabView.SelectedIndex = 0;
///
/// UXML에서 사용:
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
[UxmlElement]
public partial class UTKTabView : TabView, IDisposable
{
#region Constants
private const string USS_PATH = "UIToolkit/Tab/UTKTabView";
#endregion
#region Fields
private bool _disposed;
private readonly List _utkTabs = new();
private TabAlign _align = TabAlign.Top;
#endregion
#region Events
/// 탭 변경 이벤트
public event Action? OnTabChanged;
#endregion
#region Properties
/// 선택된 탭 인덱스
public int SelectedIndex
{
get => selectedTabIndex;
set => selectedTabIndex = value;
}
/// UTK 탭 목록
public IReadOnlyList UTKTabs => _utkTabs;
/// 탭 정렬 방향
[UxmlAttribute("align")]
public TabAlign Align
{
get => _align;
set
{
if (_align == value) return;
_align = value;
ApplyAlignment();
}
}
#endregion
#region Constructor
public UTKTabView() : base()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
var uss = Resources.Load(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
SetupStyles();
SetupEvents();
SubscribeToThemeChanges();
}
#endregion
#region Setup
private void SetupStyles()
{
AddToClassList("utk-tabview");
ApplyAlignment();
}
private void SetupEvents()
{
this.RegisterCallback>(OnTabIndexChanged);
}
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback(OnAttachToPanelForTheme);
RegisterCallback(OnDetachFromPanelForTheme);
}
private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt)
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region Event Handlers
private void OnTabIndexChanged(ChangeEvent evt)
{
UpdateTabSelection();
OnTabChanged?.Invoke(evt.newValue, activeTab);
}
private void UpdateTabSelection()
{
for (int i = 0; i < _utkTabs.Count; i++)
{
_utkTabs[i].IsSelected = (i == selectedTabIndex);
}
}
#endregion
#region Methods
///
/// 탭 정렬 방향 적용
///
private void ApplyAlignment()
{
// 기존 align 클래스 제거
RemoveFromClassList("utk-tabview--align-top");
RemoveFromClassList("utk-tabview--align-bottom");
RemoveFromClassList("utk-tabview--align-left");
RemoveFromClassList("utk-tabview--align-right");
// 새로운 align 클래스 추가
switch (_align)
{
case TabAlign.Top:
AddToClassList("utk-tabview--align-top");
break;
case TabAlign.Bottom:
AddToClassList("utk-tabview--align-bottom");
break;
case TabAlign.Left:
AddToClassList("utk-tabview--align-left");
break;
case TabAlign.Right:
AddToClassList("utk-tabview--align-right");
break;
}
}
///
/// UTK 탭 추가
///
public UTKTab AddUTKTab(string text, VisualElement? content = null)
{
var tab = new UTKTab(text);
if (content != null)
{
tab.Add(content);
}
AddTab(tab);
return tab;
}
///
/// 탭 추가
///
public void AddTab(UTKTab tab)
{
_utkTabs.Add(tab);
Add(tab);
if (_utkTabs.Count == 1)
{
tab.IsSelected = true;
}
}
///
/// 탭 제거
///
public void RemoveTab(UTKTab tab)
{
int index = _utkTabs.IndexOf(tab);
if (index < 0) return;
_utkTabs.RemoveAt(index);
tab.RemoveFromHierarchy();
tab.Dispose();
}
///
/// 모든 탭 제거
///
public void ClearTabs()
{
foreach (var tab in _utkTabs)
{
tab.RemoveFromHierarchy();
tab.Dispose();
}
_utkTabs.Clear();
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
UnregisterCallback(OnAttachToPanelForTheme);
UnregisterCallback(OnDetachFromPanelForTheme);
foreach (var tab in _utkTabs)
{
tab.Dispose();
}
_utkTabs.Clear();
OnTabChanged = null;
}
#endregion
}
}