StyleGuide Sample 완료
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
@@ -10,6 +12,104 @@ namespace UVC.UIToolkit
|
||||
/// 텍스트와 아이콘을 동시에 표시하거나, 아이콘만 표시할 수 있습니다.
|
||||
/// 배경 색상, 외곽선 굵기 등을 설정할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 버튼 생성
|
||||
/// var btn = new UTKButton("확인");
|
||||
/// btn.OnClicked += () => Debug.Log("클릭됨!");
|
||||
///
|
||||
/// // 텍스트와 Material Icon이 있는 버튼
|
||||
/// var saveBtn = new UTKButton("저장", UTKMaterialIcons.Save, UTKButton.ButtonVariant.Primary);
|
||||
///
|
||||
/// // 텍스트와 아이콘 (크기 지정)
|
||||
/// var largeIconBtn = new UTKButton("설정", UTKMaterialIcons.Settings, UTKButton.ButtonVariant.Primary, 28);
|
||||
/// var smallImageBtn = new UTKButton("닫기", UTKImageIcons.BtnClose22, UTKButton.ButtonVariant.Danger, 20);
|
||||
///
|
||||
/// // Material Icon 설정
|
||||
/// // 텍스트와 아이콘 (크기 지정)
|
||||
/// var largeIconBtn = new UTKButton("설정", UTKMaterialIcons.Settings, UTKButton.ButtonVariant.Primary, 28);
|
||||
/// var smallImageBtn = new UTKButton("닫기", UTKImageIcons.BtnClose22, UTKButton.ButtonVariant.Danger, 20);
|
||||
///
|
||||
/// // Text 버튼 스타일에서 아이콘 크기 지정
|
||||
/// var textSmallIcon = new UTKButton("Small", UTKMaterialIcons.Settings, UTKButton.ButtonVariant.Text, 16);
|
||||
/// var textMediumIcon = new UTKButton("Medium", UTKMaterialIcons.Settings, UTKButton.ButtonVariant.Text, 24);
|
||||
/// var textLargeIcon = new UTKButton("Large", UTKMaterialIcons.Settings, UTKButton.ButtonVariant.Text, 32);
|
||||
///
|
||||
/// // Material Icon 설정
|
||||
/// var imgBtn = new UTKButton("닫기");
|
||||
/// imgBtn.SetImageIcon(UTKImageIcons.BtnClose22);
|
||||
/// imgBtn.SetImageIconByName("icon_setting_22");
|
||||
///
|
||||
/// // 비동기 아이콘 설정
|
||||
/// await btn.SetMaterialIconAsync(UTKMaterialIcons.Search);
|
||||
/// await btn.SetImageIconAsync(UTKImageIcons.IconSetting22);
|
||||
///
|
||||
/// // 아이콘만 표시하는 버튼
|
||||
/// var iconOnlyBtn = new UTKButton { IconOnly = true };
|
||||
/// iconOnlyBtn.SetMaterialIcon(UTKMaterialIcons.Close);
|
||||
///
|
||||
/// // 버튼 스타일 변형
|
||||
/// btn.Variant = UTKButton.ButtonVariant.Primary;
|
||||
/// btn.Variant = UTKButton.ButtonVariant.Danger;
|
||||
/// btn.Variant = UTKButton.ButtonVariant.Ghost;
|
||||
///
|
||||
/// // 버튼 크기
|
||||
/// btn.Size = UTKButton.ButtonSize.Small;
|
||||
/// btn.Size = UTKButton.ButtonSize.Large;
|
||||
///
|
||||
/// // 주의: 생성자에서 icon 파라미터로 아이콘을 전달하면 다음 순서로 타입이 감지됩니다.
|
||||
/// // 1. UTKMaterialIcons에서 먼저 검사 (Material Symbols Outlined 아이콘)
|
||||
/// // 2. UTKImageIcons에서 검사 (이미지 기반 아이콘)
|
||||
/// // 3. 둘 다 아니면 텍스트로 처리됨
|
||||
///
|
||||
/// // 아이콘 제거
|
||||
/// btn.ClearIcon();
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <!-- 네임스페이스 선언 -->
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
///
|
||||
/// <!-- 기본 버튼 -->
|
||||
/// <utk:UTKButton Text="확인" />
|
||||
///
|
||||
/// <!-- Material Icon 버튼 (유니코드 직접 입력) -->
|
||||
/// <utk:UTKButton Text="설정" Icon="" />
|
||||
///
|
||||
/// <!-- 버튼 변형 -->
|
||||
/// <utk:UTKButton Text="저장" Variant="Primary" />
|
||||
/// <utk:UTKButton Text="삭제" Variant="Danger" />
|
||||
/// <utk:UTKButton Text="취소" Variant="Ghost" />
|
||||
/// <utk:UTKButton Text="링크" Variant="Text" />
|
||||
///
|
||||
/// <!-- 외곽선 변형 -->
|
||||
/// <utk:UTKButton Text="확인" Variant="OutlinePrimary" />
|
||||
/// <utk:UTKButton Text="삭제" Variant="OutlineDanger" />
|
||||
///
|
||||
/// <!-- 크기 변형 -->
|
||||
/// <utk:UTKButton Text="작은 버튼" Size="Small" />
|
||||
/// <utk:UTKButton Text="큰 버튼" Size="Large" />
|
||||
///
|
||||
/// <!-- 아이콘만 표시 -->
|
||||
/// <utk:UTKButton Icon="" IconOnly="true" />
|
||||
/// <utk:UTKButton Text="" Icon="Close" Variant="Text" IconSize="12" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKButton Text="비활성화" IsEnabled="false" />
|
||||
///
|
||||
/// <!-- 외곽선 굵기 -->
|
||||
/// <utk:UTKButton Text="두꺼운 외곽선" BorderWidth="2" />
|
||||
///
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 로드 후 C#에서 아이콘 설정:</b></para>
|
||||
/// <code>
|
||||
/// var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
/// var btn = root.Q<UTKButton>("my-button");
|
||||
/// btn.SetMaterialIcon(UTKMaterialIcons.Settings);
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKButton : VisualElement, IDisposable
|
||||
{
|
||||
@@ -21,6 +121,7 @@ namespace UVC.UIToolkit
|
||||
private bool _disposed;
|
||||
private Label? _iconLabel;
|
||||
private Label? _textLabel;
|
||||
private VisualElement? _imageIcon;
|
||||
|
||||
private string _text = "";
|
||||
private string _icon = "";
|
||||
@@ -145,7 +246,9 @@ namespace UVC.UIToolkit
|
||||
Danger,
|
||||
OutlineNormal,
|
||||
OutlinePrimary,
|
||||
OutlineDanger
|
||||
OutlineDanger,
|
||||
/// <summary>배경과 외곽선이 투명하고 텍스트/아이콘만 표시</summary>
|
||||
Text
|
||||
}
|
||||
|
||||
public enum ButtonSize
|
||||
@@ -170,15 +273,52 @@ namespace UVC.UIToolkit
|
||||
CreateUI();
|
||||
SetupEvents();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
UpdateContent();
|
||||
UpdateVariant();
|
||||
UpdateSize();
|
||||
});
|
||||
}
|
||||
|
||||
public UTKButton(string text, string icon = "", ButtonVariant variant = ButtonVariant.Normal) : this()
|
||||
/// <summary>
|
||||
/// 텍스트, 아이콘, 변형, 크기를 설정하여 버튼을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">버튼에 표시할 텍스트</param>
|
||||
/// <param name="icon">아이콘 이름 또는 경로 (Material Icon 또는 Image Icon)</param>
|
||||
/// <param name="variant">버튼 스타일 변형</param>
|
||||
/// <param name="iconSize">아이콘 크기 (생략 시 기본값 사용)</param>
|
||||
/// <remarks>
|
||||
/// 아이콘 타입 감지 순서:
|
||||
/// 1. UTKMaterialIcons 검사 (Material Symbols Outlined 아이콘)
|
||||
/// 2. UTKImageIcons 검사 (이미지 기반 아이콘)
|
||||
/// 3. 둘 다 아니면 텍스트로 처리됨
|
||||
/// </remarks>
|
||||
public UTKButton(string text, string icon = "", ButtonVariant variant = ButtonVariant.Normal, int? iconSize = null) : this()
|
||||
{
|
||||
_text = text;
|
||||
_icon = icon;
|
||||
_variant = variant;
|
||||
UpdateContent();
|
||||
UpdateVariant();
|
||||
|
||||
// 아이콘 타입 자동 감지 및 적용
|
||||
if (!string.IsNullOrEmpty(icon))
|
||||
{
|
||||
// 1순위: UTKMaterialIcons에 해당하는지 확인
|
||||
if (UTKMaterialIcons.GetIcon(icon) != null)
|
||||
{
|
||||
SetMaterialIcon(icon, iconSize);
|
||||
}
|
||||
// 2순위: UTKImageIcons에 해당하는지 확인
|
||||
else if (!string.IsNullOrEmpty(UTKImageIcons.GetPath(icon)))
|
||||
{
|
||||
SetImageIcon(icon, iconSize);
|
||||
}
|
||||
// 3순위: 둘 다 아니면 현재 로직 유지 (UpdateContent에서 이미 처리됨)
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -187,6 +327,7 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
AddToClassList("utk-button");
|
||||
focusable = true;
|
||||
pickingMode = PickingMode.Position;
|
||||
|
||||
_iconLabel = new Label
|
||||
{
|
||||
@@ -281,6 +422,7 @@ namespace UVC.UIToolkit
|
||||
RemoveFromClassList("utk-button--outline-normal");
|
||||
RemoveFromClassList("utk-button--outline-primary");
|
||||
RemoveFromClassList("utk-button--outline-danger");
|
||||
RemoveFromClassList("utk-button--text");
|
||||
|
||||
var variantClass = _variant switch
|
||||
{
|
||||
@@ -291,6 +433,7 @@ namespace UVC.UIToolkit
|
||||
ButtonVariant.OutlineNormal => "utk-button--outline-normal",
|
||||
ButtonVariant.OutlinePrimary => "utk-button--outline-primary",
|
||||
ButtonVariant.OutlineDanger => "utk-button--outline-danger",
|
||||
ButtonVariant.Text => "utk-button--text",
|
||||
_ => "utk-button--normal"
|
||||
};
|
||||
AddToClassList(variantClass);
|
||||
@@ -339,6 +482,179 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Icon Methods
|
||||
|
||||
/// <summary>
|
||||
/// Material Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="icon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Settings)</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public void SetMaterialIcon(string icon, int? fontSize = null)
|
||||
{
|
||||
ClearImageIcon();
|
||||
Icon = icon;
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
UTKMaterialIcons.ApplyIconStyle(_iconLabel, fontSize ?? GetDefaultIconSize());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material Icon을 비동기로 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="icon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Settings)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public async UniTask SetMaterialIconAsync(string icon, CancellationToken ct = default, int? fontSize = null)
|
||||
{
|
||||
ClearImageIcon();
|
||||
Icon = icon;
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
await UTKMaterialIcons.ApplyIconStyleAsync(_iconLabel, ct, fontSize ?? GetDefaultIconSize());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Material Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 이름 (예: "settings", "home")</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public void SetMaterialIconByName(string iconName, int? fontSize = null)
|
||||
{
|
||||
var iconChar = UTKMaterialIcons.GetIcon(iconName);
|
||||
if (!string.IsNullOrEmpty(iconChar))
|
||||
{
|
||||
SetMaterialIcon(iconChar, fontSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKButton] Material icon '{iconName}'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">이미지 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public void SetImageIcon(string resourcePath, int? iconSize = null)
|
||||
{
|
||||
var texture = UTKImageIcons.LoadTexture(resourcePath);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKButton] Image icon '{resourcePath}'을(를) 로드할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon을 비동기로 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">이미지 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public async UniTask SetImageIconAsync(string resourcePath, CancellationToken ct = default, int? iconSize = null)
|
||||
{
|
||||
var texture = await UTKImageIcons.LoadTextureAsync(resourcePath, ct);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKButton] Image icon '{resourcePath}'을(를) 로드할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Image Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 이름 (예: "icon_setting_22", "btn_close_16")</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 버튼 크기에 맞춤)</param>
|
||||
public void SetImageIconByName(string iconName, int? iconSize = null)
|
||||
{
|
||||
var texture = UTKImageIcons.LoadTextureByName(iconName);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKButton] Image icon '{iconName}'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 아이콘을 제거합니다.
|
||||
/// </summary>
|
||||
public void ClearIcon()
|
||||
{
|
||||
Icon = "";
|
||||
ClearImageIcon();
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyImageIcon(Texture2D texture, int? iconSize)
|
||||
{
|
||||
// 기존 아이콘 Label 숨기기
|
||||
Icon = "";
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
// 이미지 아이콘용 VisualElement 생성 또는 재사용
|
||||
if (_imageIcon == null)
|
||||
{
|
||||
_imageIcon = new VisualElement
|
||||
{
|
||||
name = "image-icon",
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
_imageIcon.AddToClassList("utk-button__image-icon");
|
||||
Insert(0, _imageIcon);
|
||||
}
|
||||
|
||||
var size = iconSize ?? GetDefaultIconSize();
|
||||
_imageIcon.style.width = size;
|
||||
_imageIcon.style.height = size;
|
||||
_imageIcon.style.backgroundImage = new StyleBackground(texture);
|
||||
_imageIcon.style.display = DisplayStyle.Flex;
|
||||
|
||||
EnableInClassList("utk-button--has-image-icon", true);
|
||||
}
|
||||
|
||||
private void ClearImageIcon()
|
||||
{
|
||||
if (_imageIcon != null)
|
||||
{
|
||||
_imageIcon.style.display = DisplayStyle.None;
|
||||
}
|
||||
EnableInClassList("utk-button--has-image-icon", false);
|
||||
}
|
||||
|
||||
private int GetDefaultIconSize()
|
||||
{
|
||||
return _size switch
|
||||
{
|
||||
ButtonSize.Small => 16,
|
||||
ButtonSize.Large => 24,
|
||||
_ => 20 // Medium
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Common;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
@@ -9,12 +10,37 @@ namespace UVC.UIToolkit
|
||||
/// 체크박스 컴포넌트.
|
||||
/// 선택/해제 상태를 토글할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 체크박스
|
||||
/// var checkbox = new UTKCheckBox();
|
||||
/// checkbox.Text = "약관에 동의합니다";
|
||||
/// checkbox.OnValueChanged += (isChecked) => Debug.Log($"체크: {isChecked}");
|
||||
///
|
||||
/// // 상태 설정
|
||||
/// checkbox.IsChecked = true;
|
||||
/// checkbox.IsIndeterminate = true; // 부분 선택 상태
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 체크박스 -->
|
||||
/// <utk:UTKCheckBox Text="이메일 수신 동의" />
|
||||
///
|
||||
/// <!-- 기본값 체크됨 -->
|
||||
/// <utk:UTKCheckBox Text="자동 로그인" IsChecked="true" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKCheckBox Text="필수 동의" IsEnabled="false" IsChecked="true" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKCheckBox : VisualElement, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Button/UTKCheckBox";
|
||||
private const string CHECK_ICON = "✓";
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
@@ -95,6 +121,13 @@ namespace UVC.UIToolkit
|
||||
CreateUI();
|
||||
SetupEvents();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
if (_label != null) _label.text = _text;
|
||||
UpdateState();
|
||||
});
|
||||
}
|
||||
|
||||
public UTKCheckBox(string text, bool isChecked = false) : this()
|
||||
@@ -113,8 +146,9 @@ namespace UVC.UIToolkit
|
||||
_checkBox = new VisualElement { name = "checkbox" };
|
||||
_checkBox.AddToClassList("utk-checkbox__box");
|
||||
|
||||
_checkIcon = new Label { name = "check-icon", text = CHECK_ICON };
|
||||
_checkIcon = new Label { name = "check-icon", text = UTKMaterialIcons.Check };
|
||||
_checkIcon.AddToClassList("utk-checkbox__icon");
|
||||
UTKMaterialIcons.ApplyIconStyle(_checkIcon, 14);
|
||||
_checkBox.Add(_checkIcon);
|
||||
|
||||
Add(_checkBox);
|
||||
@@ -200,7 +234,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (_checkIcon != null)
|
||||
{
|
||||
_checkIcon.text = _isIndeterminate ? "−" : CHECK_ICON;
|
||||
_checkIcon.text = _isIndeterminate ? UTKMaterialIcons.Remove : UTKMaterialIcons.Check;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -9,6 +9,33 @@ namespace UVC.UIToolkit
|
||||
/// 라디오 버튼 컴포넌트.
|
||||
/// Unity RadioButton을 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 라디오 버튼 그룹
|
||||
/// var group = new RadioButtonGroup();
|
||||
/// var radio1 = new UTKRadioButton { Label = "옵션 1" };
|
||||
/// var radio2 = new UTKRadioButton { Label = "옵션 2" };
|
||||
/// var radio3 = new UTKRadioButton { Label = "옵션 3" };
|
||||
/// group.Add(radio1);
|
||||
/// group.Add(radio2);
|
||||
/// group.Add(radio3);
|
||||
///
|
||||
/// radio1.OnValueChanged += (isSelected) => {
|
||||
/// if (isSelected) Debug.Log("옵션 1 선택됨");
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <ui:RadioButtonGroup>
|
||||
/// <utk:UTKRadioButton label="소형" />
|
||||
/// <utk:UTKRadioButton label="중형" value="true" />
|
||||
/// <utk:UTKRadioButton label="대형" />
|
||||
/// </ui:RadioButtonGroup>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKRadioButton : RadioButton, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,29 @@ namespace UVC.UIToolkit
|
||||
/// 토글 스위치 컴포넌트.
|
||||
/// Unity Toggle을 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 토글
|
||||
/// var toggle = new UTKToggle();
|
||||
/// toggle.Label = "알림 받기";
|
||||
/// toggle.IsOn = true;
|
||||
/// toggle.OnValueChanged += (isOn) => Debug.Log($"토글: {isOn}");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 토글 -->
|
||||
/// <utk:UTKToggle label="다크 모드" />
|
||||
///
|
||||
/// <!-- 기본값 켜짐 -->
|
||||
/// <utk:UTKToggle label="자동 저장" value="true" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKToggle label="프리미엄 기능" IsEnabled="false" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKToggle : Toggle, IDisposable
|
||||
{
|
||||
|
||||
@@ -10,6 +10,37 @@ namespace UVC.UIToolkit
|
||||
/// 토글 버튼 그룹 컴포넌트.
|
||||
/// Unity ToggleButtonGroup을 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 토글 버튼 그룹 생성
|
||||
/// var group = new UTKToggleButtonGroup();
|
||||
/// group.Add(new Button { text = "왼쪽" });
|
||||
/// group.Add(new Button { text = "가운데" });
|
||||
/// group.Add(new Button { text = "오른쪽" });
|
||||
///
|
||||
/// // 단일 선택 모드
|
||||
/// group.allowEmptySelection = false;
|
||||
///
|
||||
/// // 다중 선택 모드
|
||||
/// group.isMultipleSelection = true;
|
||||
///
|
||||
/// // 선택 변경 이벤트
|
||||
/// group.OnSelectionChanged += (indices) => {
|
||||
/// Debug.Log($"선택됨: {string.Join(", ", indices)}");
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKToggleButtonGroup allow-empty-selection="false">
|
||||
/// <ui:Button text="왼쪽" />
|
||||
/// <ui:Button text="가운데" />
|
||||
/// <ui:Button text="오른쪽" />
|
||||
/// </utk:UTKToggleButtonGroup>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKToggleButtonGroup : ToggleButtonGroup, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,44 @@ namespace UVC.UIToolkit
|
||||
/// 카드 컴포넌트.
|
||||
/// 콘텐츠를 카드 형태로 표시합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 카드
|
||||
/// var card = new UTKCard();
|
||||
/// card.Title = "카드 제목";
|
||||
/// card.Subtitle = "부제목";
|
||||
/// card.Variant = UTKCard.CardVariant.Elevated;
|
||||
///
|
||||
/// // 이미지 설정
|
||||
/// card.SetImage(myTexture);
|
||||
///
|
||||
/// // 콘텐츠 추가
|
||||
/// card.AddContent(new Label("카드 내용"));
|
||||
///
|
||||
/// // 액션 버튼 추가
|
||||
/// card.AddActionButton("자세히", () => Debug.Log("클릭"));
|
||||
///
|
||||
/// // 클릭 가능 카드
|
||||
/// card.IsClickable = true;
|
||||
/// card.OnClicked += () => Debug.Log("카드 클릭");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 카드 -->
|
||||
/// <utk:UTKCard Title="제목" Subtitle="부제목" Variant="Elevated">
|
||||
/// <ui:Label text="카드 내용" />
|
||||
/// </utk:UTKCard>
|
||||
///
|
||||
/// <!-- 클릭 가능 카드 -->
|
||||
/// <utk:UTKCard Title="클릭해보세요" IsClickable="true" />
|
||||
///
|
||||
/// <!-- 외곽선 카드 -->
|
||||
/// <utk:UTKCard Title="외곽선" Variant="Outlined" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKCard : VisualElement, IDisposable
|
||||
{
|
||||
@@ -138,6 +176,23 @@ namespace UVC.UIToolkit
|
||||
CreateUI();
|
||||
SetupEvents();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
_titleLabel.text = _title;
|
||||
_titleLabel.style.display = string.IsNullOrEmpty(_title) ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
if (_subtitleLabel != null)
|
||||
{
|
||||
_subtitleLabel.text = _subtitle;
|
||||
_subtitleLabel.style.display = string.IsNullOrEmpty(_subtitle) ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
UpdateVariant();
|
||||
UpdateHeaderVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
public UTKCard(string title, string subtitle = "") : this()
|
||||
|
||||
@@ -9,6 +9,38 @@ namespace UVC.UIToolkit
|
||||
/// 접을 수 있는 섹션 컴포넌트.
|
||||
/// Unity Foldout을 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 폴드아웃
|
||||
/// var foldout = new UTKFoldout("고급 설정", expanded: false);
|
||||
/// foldout.Add(new Label("옵션 1"));
|
||||
/// foldout.Add(new Label("옵션 2"));
|
||||
///
|
||||
/// // 상태 변경 이벤트
|
||||
/// foldout.OnValueChanged += (isExpanded) => {
|
||||
/// Debug.Log(isExpanded ? "펼쳐짐" : "접힘");
|
||||
/// };
|
||||
///
|
||||
/// // 프로그래밍 방식으로 상태 제어
|
||||
/// foldout.IsExpanded = true;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 폴드아웃 -->
|
||||
/// <utk:UTKFoldout text="설정" value="true">
|
||||
/// <ui:Label text="내용 1" />
|
||||
/// <ui:Label text="내용 2" />
|
||||
/// </utk:UTKFoldout>
|
||||
///
|
||||
/// <!-- 접힌 상태 -->
|
||||
/// <utk:UTKFoldout text="고급 옵션" value="false">
|
||||
/// <ui:Label text="숨겨진 내용" />
|
||||
/// </utk:UTKFoldout>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKFoldout : Foldout, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,44 @@ namespace UVC.UIToolkit
|
||||
/// 도움말 박스 컴포넌트.
|
||||
/// Unity HelpBox를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 정보 메시지
|
||||
/// var infoBox = new UTKHelpBox()
|
||||
/// {
|
||||
/// Message = "이 기능은 베타 버전입니다.",
|
||||
/// messageType = HelpBoxMessageType.Info
|
||||
/// };
|
||||
///
|
||||
/// // 경고 메시지
|
||||
/// var warningBox = new UTKHelpBox()
|
||||
/// {
|
||||
/// Message = "주의: 되돌릴 수 없습니다.",
|
||||
/// messageType = HelpBoxMessageType.Warning
|
||||
/// };
|
||||
///
|
||||
/// // 오류 메시지
|
||||
/// var errorBox = new UTKHelpBox()
|
||||
/// {
|
||||
/// Message = "오류: 파일을 찾을 수 없습니다.",
|
||||
/// messageType = HelpBoxMessageType.Error
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 정보 -->
|
||||
/// <utk:UTKHelpBox text="정보 메시지입니다." message-type="Info" />
|
||||
///
|
||||
/// <!-- 경고 -->
|
||||
/// <utk:UTKHelpBox text="경고 메시지입니다." message-type="Warning" />
|
||||
///
|
||||
/// <!-- 오류 -->
|
||||
/// <utk:UTKHelpBox text="오류 메시지입니다." message-type="Error" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKHelpBox : HelpBox, IDisposable
|
||||
{
|
||||
|
||||
359
Assets/Scripts/UVC/UIToolkit/Common/UTKImageIcons.cs
Normal file
359
Assets/Scripts/UVC/UIToolkit/Common/UTKImageIcons.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
// <auto-generated>
|
||||
// 이 파일은 UTKImageIconsGenerator에 의해 자동 생성되었습니다.
|
||||
// 직접 수정하지 마세요. Tools > UTK > Image Icons Generator 메뉴로 재생성하세요.
|
||||
// Source: Assets/Resources/UIToolkit/Images
|
||||
// </auto-generated>
|
||||
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 이미지 아이콘 리소스 경로 상수 클래스입니다.
|
||||
/// 총 41개의 아이콘을 포함합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 상수로 리소스 경로 사용
|
||||
/// string path = UTKImageIcons.Home;
|
||||
///
|
||||
/// // 동기 Sprite 로드 (캐싱됨)
|
||||
/// Sprite sprite = UTKImageIcons.LoadSprite(UTKImageIcons.Home);
|
||||
///
|
||||
/// // 비동기 Sprite 로드 (UniTask, 캐싱됨)
|
||||
/// Sprite? sprite = await UTKImageIcons.LoadSpriteAsync(UTKImageIcons.Home, cancellationToken);
|
||||
///
|
||||
/// // 동기 Texture2D 로드 (캐싱됨)
|
||||
/// Texture2D texture = UTKImageIcons.LoadTexture(UTKImageIcons.Settings);
|
||||
///
|
||||
/// // 비동기 Texture2D 로드 (UniTask, 캐싱됨)
|
||||
/// Texture2D? texture = await UTKImageIcons.LoadTextureAsync(UTKImageIcons.Settings, cancellationToken);
|
||||
///
|
||||
/// // 이름으로 Sprite 로드
|
||||
/// Sprite icon = UTKImageIcons.LoadSpriteByName("home");
|
||||
///
|
||||
/// // 이름으로 비동기 Sprite 로드
|
||||
/// Sprite? icon = await UTKImageIcons.LoadSpriteByNameAsync("home", cancellationToken);
|
||||
///
|
||||
/// // 이름으로 경로 조회
|
||||
/// string iconPath = UTKImageIcons.GetPath("settings");
|
||||
///
|
||||
/// // 아이콘 존재 여부 확인
|
||||
/// if (UTKImageIcons.HasIcon("search")) { }
|
||||
///
|
||||
/// // 전체 아이콘 이름 순회
|
||||
/// foreach (var name in UTKImageIcons.GetAllIconNames()) { }
|
||||
///
|
||||
/// // 캐시 클리어
|
||||
/// UTKImageIcons.ClearCache();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// <para><b>UXML에서 사용하기:</b></para>
|
||||
/// <para>UXML에서 이미지 아이콘을 사용하려면 USS에서 background-image를 설정합니다.</para>
|
||||
/// <code>
|
||||
/// /* USS 파일 */
|
||||
/// .my-icon {
|
||||
/// width: 24px;
|
||||
/// height: 24px;
|
||||
/// background-image: resource('UIToolkit/Images/icon_setting_22');
|
||||
/// }
|
||||
/// </code>
|
||||
/// <code>
|
||||
/// <!-- UXML 파일 -->
|
||||
/// <ui:VisualElement class="my-icon" />
|
||||
/// </code>
|
||||
/// <para><b>C# 코드에서 UXML 요소에 이미지 적용:</b></para>
|
||||
/// <code>
|
||||
/// var iconElement = root.Q<VisualElement>("my-icon");
|
||||
/// var texture = UTKImageIcons.LoadTextureByName("icon_setting_22");
|
||||
/// iconElement.style.backgroundImage = new StyleBackground(texture);
|
||||
/// </code>
|
||||
/// <para><b>Image 요소에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// var image = root.Q<Image>("my-image");
|
||||
/// image.sprite = UTKImageIcons.LoadSpriteByName("btn_close_16");
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public static class UTKImageIcons
|
||||
{
|
||||
/// <summary>btn_cancel_64.png</summary>
|
||||
public const string BtnCancel64 = "UIToolkit/Images/btn_cancel_64";
|
||||
/// <summary>btn_close_16.png</summary>
|
||||
public const string BtnClose16 = "UIToolkit/Images/btn_close_16";
|
||||
/// <summary>btn_close_22.png</summary>
|
||||
public const string BtnClose22 = "UIToolkit/Images/btn_close_22";
|
||||
/// <summary>cursor_arc_32.png</summary>
|
||||
public const string CursorArc32 = "UIToolkit/Images/cursor_arc_32";
|
||||
/// <summary>cursor_ask_32.png</summary>
|
||||
public const string CursorAsk32 = "UIToolkit/Images/cursor_ask_32";
|
||||
/// <summary>cursor_context_menu_32.png</summary>
|
||||
public const string CursorContextMenu32 = "UIToolkit/Images/cursor_context_menu_32";
|
||||
/// <summary>cursor_copy_32.png</summary>
|
||||
public const string CursorCopy32 = "UIToolkit/Images/cursor_copy_32";
|
||||
/// <summary>cursor_default_black_32.png</summary>
|
||||
public const string CursorDefaultBlack32 = "UIToolkit/Images/cursor_default_black_32";
|
||||
/// <summary>cursor_default_white_32.png</summary>
|
||||
public const string CursorDefaultWhite32 = "UIToolkit/Images/cursor_default_white_32";
|
||||
/// <summary>cursor_export_32.png</summary>
|
||||
public const string CursorExport32 = "UIToolkit/Images/cursor_export_32";
|
||||
/// <summary>cursor_grabbing_32.png</summary>
|
||||
public const string CursorGrabbing32 = "UIToolkit/Images/cursor_grabbing_32";
|
||||
/// <summary>cursor_hand_32.png</summary>
|
||||
public const string CursorHand32 = "UIToolkit/Images/cursor_hand_32";
|
||||
/// <summary>cursor_link_32.png</summary>
|
||||
public const string CursorLink32 = "UIToolkit/Images/cursor_link_32";
|
||||
/// <summary>cursor_move_32.png</summary>
|
||||
public const string CursorMove32 = "UIToolkit/Images/cursor_move_32";
|
||||
/// <summary>cursor_node_32.png</summary>
|
||||
public const string CursorNode32 = "UIToolkit/Images/cursor_node_32";
|
||||
/// <summary>cursor_point_white_32.png</summary>
|
||||
public const string CursorPointWhite32 = "UIToolkit/Images/cursor_point_white_32";
|
||||
/// <summary>cursor_resize_col_32.png</summary>
|
||||
public const string CursorResizeCol32 = "UIToolkit/Images/cursor_resize_col_32";
|
||||
/// <summary>cursor_resize_down_32.png</summary>
|
||||
public const string CursorResizeDown32 = "UIToolkit/Images/cursor_resize_down_32";
|
||||
/// <summary>cursor_resize_h_32.png</summary>
|
||||
public const string CursorResizeH32 = "UIToolkit/Images/cursor_resize_h_32";
|
||||
/// <summary>cursor_resize_left_32.png</summary>
|
||||
public const string CursorResizeLeft32 = "UIToolkit/Images/cursor_resize_left_32";
|
||||
/// <summary>cursor_resize_right_32.png</summary>
|
||||
public const string CursorResizeRight32 = "UIToolkit/Images/cursor_resize_right_32";
|
||||
/// <summary>cursor_resize_row_32.png</summary>
|
||||
public const string CursorResizeRow32 = "UIToolkit/Images/cursor_resize_row_32";
|
||||
/// <summary>cursor_resize_tlbr_32.png</summary>
|
||||
public const string CursorResizeTlbr32 = "UIToolkit/Images/cursor_resize_tlbr_32";
|
||||
/// <summary>cursor_resize_trbl_32.png</summary>
|
||||
public const string CursorResizeTrbl32 = "UIToolkit/Images/cursor_resize_trbl_32";
|
||||
/// <summary>cursor_resize_up_32.png</summary>
|
||||
public const string CursorResizeUp32 = "UIToolkit/Images/cursor_resize_up_32";
|
||||
/// <summary>cursor_resize_v_32.png</summary>
|
||||
public const string CursorResizeV32 = "UIToolkit/Images/cursor_resize_v_32";
|
||||
/// <summary>cursor_rotate_bottom_left_32.png</summary>
|
||||
public const string CursorRotateBottomLeft32 = "UIToolkit/Images/cursor_rotate_bottom_left_32";
|
||||
/// <summary>cursor_rotate_bottom_right_32.png</summary>
|
||||
public const string CursorRotateBottomRight32 = "UIToolkit/Images/cursor_rotate_bottom_right_32";
|
||||
/// <summary>cursor_rotate_top_left_32.png</summary>
|
||||
public const string CursorRotateTopLeft32 = "UIToolkit/Images/cursor_rotate_top_left_32";
|
||||
/// <summary>cursor_rotate_top_right_32.png</summary>
|
||||
public const string CursorRotateTopRight32 = "UIToolkit/Images/cursor_rotate_top_right_32";
|
||||
/// <summary>cursor_select_32.png</summary>
|
||||
public const string CursorSelect32 = "UIToolkit/Images/cursor_select_32";
|
||||
/// <summary>cursor_wait_32.png</summary>
|
||||
public const string CursorWait32 = "UIToolkit/Images/cursor_wait_32";
|
||||
/// <summary>cursor_zoom_in_32.png</summary>
|
||||
public const string CursorZoomIn32 = "UIToolkit/Images/cursor_zoom_in_32";
|
||||
/// <summary>cursor_zoom_out_32.png</summary>
|
||||
public const string CursorZoomOut32 = "UIToolkit/Images/cursor_zoom_out_32";
|
||||
/// <summary>icon_down_20x16.png</summary>
|
||||
public const string IconDown20x16 = "UIToolkit/Images/icon_down_20x16";
|
||||
/// <summary>icon_down_22.png</summary>
|
||||
public const string IconDown22 = "UIToolkit/Images/icon_down_22";
|
||||
/// <summary>icon_eye_22x16.png</summary>
|
||||
public const string IconEye22x16 = "UIToolkit/Images/icon_eye_22x16";
|
||||
/// <summary>icon_eye_close_22x16.png</summary>
|
||||
public const string IconEyeClose22x16 = "UIToolkit/Images/icon_eye_close_22x16";
|
||||
/// <summary>icon_right_22.png</summary>
|
||||
public const string IconRight22 = "UIToolkit/Images/icon_right_22";
|
||||
/// <summary>icon_search_22x16.png</summary>
|
||||
public const string IconSearch22x16 = "UIToolkit/Images/icon_search_22x16";
|
||||
/// <summary>icon_setting_22.png</summary>
|
||||
public const string IconSetting22 = "UIToolkit/Images/icon_setting_22";
|
||||
|
||||
#region Lookup & Load
|
||||
|
||||
private static readonly Dictionary<string, string> _pathsByName = new()
|
||||
{
|
||||
["btn_cancel_64"] = "UIToolkit/Images/btn_cancel_64",
|
||||
["btn_close_16"] = "UIToolkit/Images/btn_close_16",
|
||||
["btn_close_22"] = "UIToolkit/Images/btn_close_22",
|
||||
["cursor_arc_32"] = "UIToolkit/Images/cursor_arc_32",
|
||||
["cursor_ask_32"] = "UIToolkit/Images/cursor_ask_32",
|
||||
["cursor_context_menu_32"] = "UIToolkit/Images/cursor_context_menu_32",
|
||||
["cursor_copy_32"] = "UIToolkit/Images/cursor_copy_32",
|
||||
["cursor_default_black_32"] = "UIToolkit/Images/cursor_default_black_32",
|
||||
["cursor_default_white_32"] = "UIToolkit/Images/cursor_default_white_32",
|
||||
["cursor_export_32"] = "UIToolkit/Images/cursor_export_32",
|
||||
["cursor_grabbing_32"] = "UIToolkit/Images/cursor_grabbing_32",
|
||||
["cursor_hand_32"] = "UIToolkit/Images/cursor_hand_32",
|
||||
["cursor_link_32"] = "UIToolkit/Images/cursor_link_32",
|
||||
["cursor_move_32"] = "UIToolkit/Images/cursor_move_32",
|
||||
["cursor_node_32"] = "UIToolkit/Images/cursor_node_32",
|
||||
["cursor_point_white_32"] = "UIToolkit/Images/cursor_point_white_32",
|
||||
["cursor_resize_col_32"] = "UIToolkit/Images/cursor_resize_col_32",
|
||||
["cursor_resize_down_32"] = "UIToolkit/Images/cursor_resize_down_32",
|
||||
["cursor_resize_h_32"] = "UIToolkit/Images/cursor_resize_h_32",
|
||||
["cursor_resize_left_32"] = "UIToolkit/Images/cursor_resize_left_32",
|
||||
["cursor_resize_right_32"] = "UIToolkit/Images/cursor_resize_right_32",
|
||||
["cursor_resize_row_32"] = "UIToolkit/Images/cursor_resize_row_32",
|
||||
["cursor_resize_tlbr_32"] = "UIToolkit/Images/cursor_resize_tlbr_32",
|
||||
["cursor_resize_trbl_32"] = "UIToolkit/Images/cursor_resize_trbl_32",
|
||||
["cursor_resize_up_32"] = "UIToolkit/Images/cursor_resize_up_32",
|
||||
["cursor_resize_v_32"] = "UIToolkit/Images/cursor_resize_v_32",
|
||||
["cursor_rotate_bottom_left_32"] = "UIToolkit/Images/cursor_rotate_bottom_left_32",
|
||||
["cursor_rotate_bottom_right_32"] = "UIToolkit/Images/cursor_rotate_bottom_right_32",
|
||||
["cursor_rotate_top_left_32"] = "UIToolkit/Images/cursor_rotate_top_left_32",
|
||||
["cursor_rotate_top_right_32"] = "UIToolkit/Images/cursor_rotate_top_right_32",
|
||||
["cursor_select_32"] = "UIToolkit/Images/cursor_select_32",
|
||||
["cursor_wait_32"] = "UIToolkit/Images/cursor_wait_32",
|
||||
["cursor_zoom_in_32"] = "UIToolkit/Images/cursor_zoom_in_32",
|
||||
["cursor_zoom_out_32"] = "UIToolkit/Images/cursor_zoom_out_32",
|
||||
["icon_down_20x16"] = "UIToolkit/Images/icon_down_20x16",
|
||||
["icon_down_22"] = "UIToolkit/Images/icon_down_22",
|
||||
["icon_eye_22x16"] = "UIToolkit/Images/icon_eye_22x16",
|
||||
["icon_eye_close_22x16"] = "UIToolkit/Images/icon_eye_close_22x16",
|
||||
["icon_right_22"] = "UIToolkit/Images/icon_right_22",
|
||||
["icon_search_22x16"] = "UIToolkit/Images/icon_search_22x16",
|
||||
["icon_setting_22"] = "UIToolkit/Images/icon_setting_22",
|
||||
};
|
||||
|
||||
private static readonly Dictionary<string, Sprite?> _spriteCache = new();
|
||||
private static readonly Dictionary<string, Texture2D?> _textureCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 리소스 경로를 조회합니다.
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 파일명 (확장자 제외)</param>
|
||||
/// <returns>리소스 경로, 없으면 빈 문자열</returns>
|
||||
public static string GetPath(string iconName)
|
||||
{
|
||||
return _pathsByName.TryGetValue(iconName, out var path) ? path : string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘이 존재하는지 확인합니다.
|
||||
/// </summary>
|
||||
public static bool HasIcon(string iconName) => _pathsByName.ContainsKey(iconName);
|
||||
|
||||
/// <summary>
|
||||
/// 모든 아이콘 이름 목록을 반환합니다.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetAllIconNames() => _pathsByName.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// 전체 아이콘 수를 반환합니다.
|
||||
/// </summary>
|
||||
public static int Count => 41;
|
||||
|
||||
/// <summary>
|
||||
/// 리소스 경로로 Sprite를 동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">리소스 경로 (예: Icons/Home)</param>
|
||||
public static Sprite? LoadSprite(string resourcePath)
|
||||
{
|
||||
if (_spriteCache.TryGetValue(resourcePath, out var cached))
|
||||
return cached;
|
||||
|
||||
var sprite = Resources.Load<Sprite>(resourcePath);
|
||||
_spriteCache[resourcePath] = sprite;
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리소스 경로로 Sprite를 비동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">리소스 경로 (예: Icons/Home)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
public static async UniTask<Sprite?> LoadSpriteAsync(string resourcePath, CancellationToken ct = default)
|
||||
{
|
||||
if (_spriteCache.TryGetValue(resourcePath, out var cached))
|
||||
return cached;
|
||||
|
||||
var request = Resources.LoadAsync<Sprite>(resourcePath);
|
||||
await request.ToUniTask(cancellationToken: ct);
|
||||
|
||||
var sprite = request.asset as Sprite;
|
||||
_spriteCache[resourcePath] = sprite;
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리소스 경로로 Texture2D를 동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">리소스 경로 (예: Icons/Home)</param>
|
||||
public static Texture2D? LoadTexture(string resourcePath)
|
||||
{
|
||||
if (_textureCache.TryGetValue(resourcePath, out var cached))
|
||||
return cached;
|
||||
|
||||
var texture = Resources.Load<Texture2D>(resourcePath);
|
||||
_textureCache[resourcePath] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 리소스 경로로 Texture2D를 비동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">리소스 경로 (예: Icons/Home)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
public static async UniTask<Texture2D?> LoadTextureAsync(string resourcePath, CancellationToken ct = default)
|
||||
{
|
||||
if (_textureCache.TryGetValue(resourcePath, out var cached))
|
||||
return cached;
|
||||
|
||||
var request = Resources.LoadAsync<Texture2D>(resourcePath);
|
||||
await request.ToUniTask(cancellationToken: ct);
|
||||
|
||||
var texture = request.asset as Texture2D;
|
||||
_textureCache[resourcePath] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Sprite를 동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 파일명 (확장자 제외)</param>
|
||||
public static Sprite? LoadSpriteByName(string iconName)
|
||||
{
|
||||
var path = GetPath(iconName);
|
||||
return string.IsNullOrEmpty(path) ? null : LoadSprite(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Sprite를 비동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 파일명 (확장자 제외)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
public static async UniTask<Sprite?> LoadSpriteByNameAsync(string iconName, CancellationToken ct = default)
|
||||
{
|
||||
var path = GetPath(iconName);
|
||||
return string.IsNullOrEmpty(path) ? null : await LoadSpriteAsync(path, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Texture2D를 동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 파일명 (확장자 제외)</param>
|
||||
public static Texture2D? LoadTextureByName(string iconName)
|
||||
{
|
||||
var path = GetPath(iconName);
|
||||
return string.IsNullOrEmpty(path) ? null : LoadTexture(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Texture2D를 비동기로 로드합니다. (캐싱됨)
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 파일명 (확장자 제외)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
public static async UniTask<Texture2D?> LoadTextureByNameAsync(string iconName, CancellationToken ct = default)
|
||||
{
|
||||
var path = GetPath(iconName);
|
||||
return string.IsNullOrEmpty(path) ? null : await LoadTextureAsync(path, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 캐시를 클리어합니다.
|
||||
/// </summary>
|
||||
public static void ClearCache()
|
||||
{
|
||||
_spriteCache.Clear();
|
||||
_textureCache.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12524
Assets/Scripts/UVC/UIToolkit/Common/UTKMaterialIcons.cs
Normal file
12524
Assets/Scripts/UVC/UIToolkit/Common/UTKMaterialIcons.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,37 @@ namespace UVC.UIToolkit
|
||||
/// 스크롤 뷰 컴포넌트.
|
||||
/// Unity ScrollView를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 스크롤 뷰
|
||||
/// var scrollView = new UTKScrollView();
|
||||
/// scrollView.Add(new Label("내용 1"));
|
||||
/// scrollView.Add(new Label("내용 2"));
|
||||
/// scrollView.Add(new Label("내용 3"));
|
||||
///
|
||||
/// // 스크롤 모드 설정
|
||||
/// var verticalScroll = new UTKScrollView(ScrollViewMode.Vertical);
|
||||
/// var horizontalScroll = new UTKScrollView(ScrollViewMode.Horizontal);
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 스크롤 뷰 -->
|
||||
/// <utk:UTKScrollView style="height: 200px;">
|
||||
/// <ui:Label text="항목 1" />
|
||||
/// <ui:Label text="항목 2" />
|
||||
/// <ui:Label text="항목 3" />
|
||||
/// </utk:UTKScrollView>
|
||||
///
|
||||
/// <!-- 수직 스크롤만 -->
|
||||
/// <utk:UTKScrollView mode="Vertical" />
|
||||
///
|
||||
/// <!-- 수평 스크롤만 -->
|
||||
/// <utk:UTKScrollView mode="Horizontal" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKScrollView : ScrollView, IDisposable
|
||||
{
|
||||
|
||||
@@ -10,6 +10,33 @@ namespace UVC.UIToolkit
|
||||
/// 드롭다운 메뉴 컴포넌트.
|
||||
/// Unity DropdownField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 드롭다운
|
||||
/// var dropdown = new UTKDropdown();
|
||||
/// dropdown.Label = "국가 선택";
|
||||
/// dropdown.SetChoices(new List<string> { "한국", "미국", "일본" });
|
||||
/// dropdown.OnSelectionChanged += (index, value) => Debug.Log($"선택: {value}");
|
||||
///
|
||||
/// // 기본값 설정
|
||||
/// dropdown.value = "한국";
|
||||
/// dropdown.index = 0;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 드롭다운 -->
|
||||
/// <utk:UTKDropdown label="정렬" choices="이름,날짜,크기" />
|
||||
///
|
||||
/// <!-- 기본값 지정 -->
|
||||
/// <utk:UTKDropdown label="언어" choices="한국어,English,日本語" index="0" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKDropdown label="선택" IsEnabled="false" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKDropdown : DropdownField, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,38 @@ namespace UVC.UIToolkit
|
||||
/// Enum 선택 드롭다운 컴포넌트.
|
||||
/// Unity EnumField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // Enum 정의
|
||||
/// public enum MyOption { Option1, Option2, Option3 }
|
||||
///
|
||||
/// // 기본 Enum 드롭다운
|
||||
/// var enumDropdown = new UTKEnumDropDown();
|
||||
/// enumDropdown.label = "옵션 선택";
|
||||
/// enumDropdown.Init(MyOption.Option1);
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// enumDropdown.OnValueChanged += (value) => {
|
||||
/// Debug.Log($"선택됨: {value}");
|
||||
/// };
|
||||
///
|
||||
/// // 현재 값 가져오기
|
||||
/// var current = (MyOption)enumDropdown.Value;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- UXML에서는 C#에서 Init() 호출 필요 -->
|
||||
/// <utk:UTKEnumDropDown name="my-enum" label="옵션" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// <para><b>UXML 로드 후 초기화:</b></para>
|
||||
/// <code>
|
||||
/// var enumField = root.Q<UTKEnumDropDown>("my-enum");
|
||||
/// enumField.Init(MyOption.Option1);
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKEnumDropDown : EnumField, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,27 @@ namespace UVC.UIToolkit
|
||||
/// 실수 입력 필드 컴포넌트.
|
||||
/// Unity FloatField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 실수 필드
|
||||
/// var floatField = new UTKFloatField();
|
||||
/// floatField.label = "가격";
|
||||
/// floatField.value = 99.99f;
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// floatField.OnValueChanged += (value) => Debug.Log($"가격: {value}");
|
||||
///
|
||||
/// // 현재 값 접근
|
||||
/// float current = floatField.Value;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKFloatField label="가격" value="99.99" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKFloatField : FloatField, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,47 @@ namespace UVC.UIToolkit
|
||||
/// 입력 필드 컴포넌트.
|
||||
/// Unity TextField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 입력 필드
|
||||
/// var input = new UTKInputField();
|
||||
/// input.Label = "이름";
|
||||
/// input.Placeholder = "이름을 입력하세요";
|
||||
/// input.OnValueChanged += (value) => Debug.Log($"입력값: {value}");
|
||||
///
|
||||
/// // 비밀번호 입력 필드
|
||||
/// var password = new UTKInputField();
|
||||
/// password.Label = "비밀번호";
|
||||
/// password.isPasswordField = true;
|
||||
///
|
||||
/// // 검증 오류 표시
|
||||
/// input.SetError("이름은 필수입니다.");
|
||||
/// input.ClearError();
|
||||
///
|
||||
/// // 변형 스타일
|
||||
/// input.Variant = UTKInputField.InputFieldVariant.Outlined;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 입력 필드 -->
|
||||
/// <utk:UTKInputField label="이름" />
|
||||
///
|
||||
/// <!-- 플레이스홀더 -->
|
||||
/// <utk:UTKInputField label="이메일" Placeholder="example@email.com" />
|
||||
///
|
||||
/// <!-- 비밀번호 필드 -->
|
||||
/// <utk:UTKInputField label="비밀번호" is-password-field="true" />
|
||||
///
|
||||
/// <!-- 여러 줄 입력 -->
|
||||
/// <utk:UTKInputField label="설명" multiline="true" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKInputField label="읽기전용" IsEnabled="false" value="수정 불가" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKInputField : TextField, IDisposable
|
||||
{
|
||||
@@ -133,6 +174,12 @@ namespace UVC.UIToolkit
|
||||
SetupStyles();
|
||||
SetupEvents();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
UpdateVariant();
|
||||
});
|
||||
}
|
||||
|
||||
public UTKInputField(string label, string placeholder = "") : this()
|
||||
|
||||
@@ -9,6 +9,27 @@ namespace UVC.UIToolkit
|
||||
/// 정수 입력 필드 컴포넌트.
|
||||
/// Unity IntegerField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 정수 필드
|
||||
/// var intField = new UTKIntegerField();
|
||||
/// intField.label = "수량";
|
||||
/// intField.value = 10;
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// intField.OnValueChanged += (value) => Debug.Log($"수량: {value}");
|
||||
///
|
||||
/// // 현재 값 접근
|
||||
/// int current = intField.Value;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKIntegerField label="수량" value="10" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKIntegerField : IntegerField, IDisposable
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Common;
|
||||
|
||||
namespace UVC.UIToolkit.Input
|
||||
{
|
||||
@@ -217,15 +218,17 @@ namespace UVC.UIToolkit.Input
|
||||
buttonContainer.AddToClassList("utk-number-stepper__buttons");
|
||||
|
||||
// Up Button
|
||||
_upButton = new Button { name = "stepper-up", text = "\u25B2" }; // ▲
|
||||
_upButton = new Button { name = "stepper-up", text = UTKMaterialIcons.KeyboardArrowUp };
|
||||
_upButton.AddToClassList("utk-number-stepper__btn");
|
||||
_upButton.AddToClassList("utk-number-stepper__btn--up");
|
||||
UTKMaterialIcons.ApplyIconStyle(_upButton, 14);
|
||||
buttonContainer.Add(_upButton);
|
||||
|
||||
// Down Button
|
||||
_downButton = new Button { name = "stepper-down", text = "\u25BC" }; // ▼
|
||||
_downButton = new Button { name = "stepper-down", text = UTKMaterialIcons.KeyboardArrowDown };
|
||||
_downButton.AddToClassList("utk-number-stepper__btn");
|
||||
_downButton.AddToClassList("utk-number-stepper__btn--down");
|
||||
UTKMaterialIcons.ApplyIconStyle(_downButton, 14);
|
||||
buttonContainer.Add(_downButton);
|
||||
|
||||
Add(buttonContainer);
|
||||
|
||||
@@ -9,6 +9,29 @@ namespace UVC.UIToolkit
|
||||
/// Vector3 입력 필드 컴포넌트.
|
||||
/// Unity Vector3Field를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 Vector3 필드
|
||||
/// var vec3Field = new UTKVector3Field();
|
||||
/// vec3Field.label = "위치";
|
||||
/// vec3Field.Value = new Vector3(1, 2, 3);
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// vec3Field.OnValueChanged += (vec) => Debug.Log($"위치: {vec}");
|
||||
///
|
||||
/// // 라벨 커스터마이징
|
||||
/// vec3Field.XLabel = "X위치";
|
||||
/// vec3Field.YLabel = "Y위치";
|
||||
/// vec3Field.ZLabel = "Z위치";
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKVector3Field label="위치" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKVector3Field : Vector3Field, IDisposable
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
@@ -8,7 +10,72 @@ namespace UVC.UIToolkit
|
||||
/// <summary>
|
||||
/// 텍스트 라벨 컴포넌트.
|
||||
/// 다양한 스타일과 크기의 텍스트를 표시합니다.
|
||||
/// Material Icon 또는 Image Icon을 텍스트와 함께 또는 단독으로 표시할 수 있습니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 라벨
|
||||
/// var label = new UTKLabel();
|
||||
/// label.Text = "안녕하세요";
|
||||
/// label.Size = UTKLabel.LabelSize.Body1;
|
||||
/// label.Variant = UTKLabel.LabelVariant.Primary;
|
||||
///
|
||||
/// // 제목 스타일
|
||||
/// var title = new UTKLabel();
|
||||
/// title.Text = "제목";
|
||||
/// title.Size = UTKLabel.LabelSize.Heading1;
|
||||
/// title.IsBold = true;
|
||||
///
|
||||
/// // 스타일 적용
|
||||
/// label.IsItalic = true;
|
||||
/// label.TextAlign = UTKLabel.TextAlign.Center;
|
||||
///
|
||||
/// // Material Icon과 텍스트 함께 사용
|
||||
/// var iconLabel = new UTKLabel("설정", UTKMaterialIcons.Settings);
|
||||
///
|
||||
/// // Image Icon과 텍스트 함께 사용
|
||||
/// var imgLabel = new UTKLabel("닫기", UTKImageIcons.BtnClose16, isImageIcon: true);
|
||||
///
|
||||
/// // Material Icon만 사용
|
||||
/// var iconOnly = new UTKLabel(UTKMaterialIcons.Home);
|
||||
///
|
||||
/// // Image Icon만 사용
|
||||
/// var imgOnly = new UTKLabel(UTKImageIcons.IconSetting22, isImageIcon: true);
|
||||
///
|
||||
/// // 메서드로 아이콘 설정
|
||||
/// label.SetMaterialIcon(UTKMaterialIcons.Search);
|
||||
/// label.SetImageIcon(UTKImageIcons.IconSetting22);
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 라벨 -->
|
||||
/// <utk:UTKLabel Text="일반 텍스트" />
|
||||
///
|
||||
/// <!-- 제목 -->
|
||||
/// <utk:UTKLabel Text="제목" Size="H1" IsBold="true" />
|
||||
///
|
||||
/// <!-- 보조 텍스트 -->
|
||||
/// <utk:UTKLabel Text="설명" Size="Caption" Variant="Secondary" />
|
||||
///
|
||||
/// <!-- Material Icon과 텍스트 -->
|
||||
/// <utk:UTKLabel Text="설정" MaterialIcon="settings" />
|
||||
///
|
||||
/// <!-- Image Icon과 텍스트 -->
|
||||
/// <utk:UTKLabel Text="닫기" ImageIcon="btn_close_16" />
|
||||
///
|
||||
/// <!-- 아이콘만 (Material) -->
|
||||
/// <utk:UTKLabel MaterialIcon="home" IconSize="24" />
|
||||
///
|
||||
/// <!-- 아이콘만 (Image) -->
|
||||
/// <utk:UTKLabel ImageIcon="icon_setting_22" IconSize="22" />
|
||||
///
|
||||
/// <!-- 아이콘 오른쪽 배치 -->
|
||||
/// <utk:UTKLabel Text="다음" MaterialIcon="arrow_forward" IconPlacement="Right" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKLabel : VisualElement, IDisposable
|
||||
{
|
||||
@@ -19,6 +86,8 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private Label? _label;
|
||||
private Label? _iconLabel;
|
||||
private VisualElement? _imageIcon;
|
||||
|
||||
private string _text = "";
|
||||
private LabelSize _size = LabelSize.Body1;
|
||||
@@ -27,6 +96,11 @@ namespace UVC.UIToolkit
|
||||
private bool _isItalic;
|
||||
private TextAlign _textAlign = TextAlign.Left;
|
||||
private bool _isSelectable;
|
||||
private IconPosition _iconPosition = IconPosition.Left;
|
||||
private int _iconSize;
|
||||
private int _gap = 4;
|
||||
private string _materialIconName = "";
|
||||
private string _imageIconName = "";
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -116,6 +190,80 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>아이콘 위치 (텍스트 기준 왼쪽/오른쪽)</summary>
|
||||
[UxmlAttribute]
|
||||
public IconPosition IconPlacement
|
||||
{
|
||||
get => _iconPosition;
|
||||
set
|
||||
{
|
||||
_iconPosition = value;
|
||||
UpdateIconPosition();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>아이콘 크기 (0이면 텍스트 크기에 맞춤)</summary>
|
||||
[UxmlAttribute]
|
||||
public int IconSize
|
||||
{
|
||||
get => _iconSize;
|
||||
set
|
||||
{
|
||||
_iconSize = value;
|
||||
UpdateIconSize();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>아이콘과 텍스트 사이 간격 (px)</summary>
|
||||
[UxmlAttribute]
|
||||
public int Gap
|
||||
{
|
||||
get => _gap;
|
||||
set
|
||||
{
|
||||
_gap = value;
|
||||
UpdateGap();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Material Icon 이름 (예: "settings", "home")</summary>
|
||||
[UxmlAttribute]
|
||||
public string MaterialIcon
|
||||
{
|
||||
get => _materialIconName;
|
||||
set
|
||||
{
|
||||
_materialIconName = value;
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
SetMaterialIconByName(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearIcon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Image Icon 이름 (예: "icon_setting_22", "btn_close_16")</summary>
|
||||
[UxmlAttribute]
|
||||
public string ImageIcon
|
||||
{
|
||||
get => _imageIconName;
|
||||
set
|
||||
{
|
||||
_imageIconName = value;
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
SetImageIconByName(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearIcon();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
@@ -149,6 +297,12 @@ namespace UVC.UIToolkit
|
||||
Center,
|
||||
Right
|
||||
}
|
||||
|
||||
public enum IconPosition
|
||||
{
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -164,13 +318,95 @@ namespace UVC.UIToolkit
|
||||
|
||||
CreateUI();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
if (_label != null) _label.text = _text;
|
||||
UpdateSize();
|
||||
UpdateVariant();
|
||||
UpdateTextAlign();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 텍스트와 크기를 지정하여 라벨을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">표시할 텍스트</param>
|
||||
/// <param name="size">텍스트 크기</param>
|
||||
public UTKLabel(string text, LabelSize size = LabelSize.Body1) : this()
|
||||
{
|
||||
Text = text;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material Icon과 텍스트를 함께 표시하는 라벨을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">표시할 텍스트</param>
|
||||
/// <param name="materialIcon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Settings)</param>
|
||||
/// <param name="iconPosition">아이콘 위치 (기본: 왼쪽)</param>
|
||||
/// <param name="size">텍스트 크기</param>
|
||||
public UTKLabel(string text, string materialIcon, IconPosition iconPosition = IconPosition.Left, LabelSize size = LabelSize.Body1) : this()
|
||||
{
|
||||
Text = text;
|
||||
Size = size;
|
||||
IconPlacement = iconPosition;
|
||||
SetMaterialIcon(materialIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon과 텍스트를 함께 표시하는 라벨을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">표시할 텍스트</param>
|
||||
/// <param name="imageIconPath">Image Icon 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="isImageIcon">true로 설정해야 합니다 (Image Icon 생성자 구분용)</param>
|
||||
/// <param name="iconPosition">아이콘 위치 (기본: 왼쪽)</param>
|
||||
/// <param name="size">텍스트 크기</param>
|
||||
public UTKLabel(string text, string imageIconPath, bool isImageIcon, IconPosition iconPosition = IconPosition.Left, LabelSize size = LabelSize.Body1) : this()
|
||||
{
|
||||
Text = text;
|
||||
Size = size;
|
||||
IconPlacement = iconPosition;
|
||||
if (isImageIcon)
|
||||
{
|
||||
SetImageIcon(imageIconPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMaterialIcon(imageIconPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material Icon만 표시하는 라벨을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="materialIcon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Home)</param>
|
||||
/// <param name="iconSize">아이콘 크기 (0이면 기본 크기)</param>
|
||||
public UTKLabel(string materialIcon, int iconSize = 0) : this()
|
||||
{
|
||||
IconSize = iconSize;
|
||||
SetMaterialIcon(materialIcon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon만 표시하는 라벨을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="imageIconPath">Image Icon 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="isImageIcon">true로 설정해야 합니다 (Image Icon 생성자 구분용)</param>
|
||||
/// <param name="iconSize">아이콘 크기 (0이면 기본 크기)</param>
|
||||
public UTKLabel(string imageIconPath, bool isImageIcon, int iconSize = 0) : this()
|
||||
{
|
||||
IconSize = iconSize;
|
||||
if (isImageIcon)
|
||||
{
|
||||
SetImageIcon(imageIconPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMaterialIcon(imageIconPath);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI Creation
|
||||
@@ -267,6 +503,266 @@ namespace UVC.UIToolkit
|
||||
};
|
||||
AddToClassList(alignClass);
|
||||
}
|
||||
|
||||
private void UpdateIconPosition()
|
||||
{
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.SendToBack();
|
||||
if (_iconPosition == IconPosition.Right)
|
||||
{
|
||||
_iconLabel.BringToFront();
|
||||
}
|
||||
}
|
||||
|
||||
if (_imageIcon != null)
|
||||
{
|
||||
_imageIcon.SendToBack();
|
||||
if (_iconPosition == IconPosition.Right)
|
||||
{
|
||||
_imageIcon.BringToFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIconSize()
|
||||
{
|
||||
var size = GetEffectiveIconSize();
|
||||
|
||||
if (_iconLabel != null && _iconLabel.style.display == DisplayStyle.Flex)
|
||||
{
|
||||
_iconLabel.style.fontSize = size;
|
||||
}
|
||||
|
||||
if (_imageIcon != null && _imageIcon.style.display == DisplayStyle.Flex)
|
||||
{
|
||||
_imageIcon.style.width = size;
|
||||
_imageIcon.style.height = size;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGap()
|
||||
{
|
||||
// 아이콘과 텍스트 사이의 간격을 적용
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.style.marginRight = _iconPosition == IconPosition.Left ? _gap : 0;
|
||||
_iconLabel.style.marginLeft = _iconPosition == IconPosition.Right ? _gap : 0;
|
||||
}
|
||||
|
||||
if (_imageIcon != null)
|
||||
{
|
||||
_imageIcon.style.marginRight = _iconPosition == IconPosition.Left ? _gap : 0;
|
||||
_imageIcon.style.marginLeft = _iconPosition == IconPosition.Right ? _gap : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetEffectiveIconSize()
|
||||
{
|
||||
if (_iconSize > 0) return _iconSize;
|
||||
|
||||
return _size switch
|
||||
{
|
||||
LabelSize.H1 => 28,
|
||||
LabelSize.H2 => 24,
|
||||
LabelSize.H3 => 20,
|
||||
LabelSize.Body1 => 16,
|
||||
LabelSize.Body2 => 14,
|
||||
LabelSize.Label1 => 14,
|
||||
LabelSize.Label2 => 12,
|
||||
LabelSize.Label3 => 12,
|
||||
LabelSize.Caption => 12,
|
||||
_ => 16
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Icon Methods
|
||||
/// <summary>
|
||||
/// Material Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="icon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Settings)</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public void SetMaterialIcon(string icon, int? fontSize = null)
|
||||
{
|
||||
ClearImageIcon();
|
||||
EnsureIconLabel();
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.text = icon;
|
||||
_iconLabel.style.display = DisplayStyle.Flex;
|
||||
UTKMaterialIcons.ApplyIconStyle(_iconLabel, fontSize ?? GetEffectiveIconSize());
|
||||
}
|
||||
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Material Icon을 비동기로 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="icon">Material Icon 유니코드 문자 (예: UTKMaterialIcons.Settings)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public async UniTask SetMaterialIconAsync(string icon, CancellationToken ct = default, int? fontSize = null)
|
||||
{
|
||||
ClearImageIcon();
|
||||
EnsureIconLabel();
|
||||
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.text = icon;
|
||||
_iconLabel.style.display = DisplayStyle.Flex;
|
||||
await UTKMaterialIcons.ApplyIconStyleAsync(_iconLabel, ct, fontSize ?? GetEffectiveIconSize());
|
||||
}
|
||||
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Material Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 이름 (예: "settings", "home")</param>
|
||||
/// <param name="fontSize">아이콘 폰트 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public void SetMaterialIconByName(string iconName, int? fontSize = null)
|
||||
{
|
||||
var iconChar = UTKMaterialIcons.GetIcon(iconName);
|
||||
if (!string.IsNullOrEmpty(iconChar))
|
||||
{
|
||||
SetMaterialIcon(iconChar, fontSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKLabel] Material icon '{iconName}'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">이미지 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public void SetImageIcon(string resourcePath, int? iconSize = null)
|
||||
{
|
||||
var texture = UTKImageIcons.LoadTexture(resourcePath);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKLabel] Image icon '{resourcePath}'을(를) 로드할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Image Icon을 비동기로 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="resourcePath">이미지 리소스 경로 (예: UTKImageIcons.IconSetting22)</param>
|
||||
/// <param name="ct">취소 토큰</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public async UniTask SetImageIconAsync(string resourcePath, CancellationToken ct = default, int? iconSize = null)
|
||||
{
|
||||
var texture = await UTKImageIcons.LoadTextureAsync(resourcePath, ct);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKLabel] Image icon '{resourcePath}'을(를) 로드할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 아이콘 이름으로 Image Icon을 설정합니다.
|
||||
/// </summary>
|
||||
/// <param name="iconName">아이콘 이름 (예: "icon_setting_22", "btn_close_16")</param>
|
||||
/// <param name="iconSize">아이콘 크기 (null이면 텍스트 크기에 맞춤)</param>
|
||||
public void SetImageIconByName(string iconName, int? iconSize = null)
|
||||
{
|
||||
var texture = UTKImageIcons.LoadTextureByName(iconName);
|
||||
if (texture != null)
|
||||
{
|
||||
ApplyImageIcon(texture, iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[UTKLabel] Image icon '{iconName}'을(를) 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 아이콘을 제거합니다.
|
||||
/// </summary>
|
||||
public void ClearIcon()
|
||||
{
|
||||
ClearMaterialIcon();
|
||||
ClearImageIcon();
|
||||
EnableInClassList("utk-label--has-icon", false);
|
||||
}
|
||||
|
||||
private void EnsureIconLabel()
|
||||
{
|
||||
if (_iconLabel != null) return;
|
||||
|
||||
_iconLabel = new Label
|
||||
{
|
||||
name = "icon",
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
_iconLabel.AddToClassList("utk-label__icon");
|
||||
_iconLabel.style.display = DisplayStyle.None;
|
||||
Insert(0, _iconLabel);
|
||||
}
|
||||
|
||||
private void ApplyImageIcon(Texture2D texture, int? iconSize)
|
||||
{
|
||||
ClearMaterialIcon();
|
||||
|
||||
if (_imageIcon == null)
|
||||
{
|
||||
_imageIcon = new VisualElement
|
||||
{
|
||||
name = "image-icon",
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
_imageIcon.AddToClassList("utk-label__image-icon");
|
||||
Insert(0, _imageIcon);
|
||||
}
|
||||
|
||||
var size = iconSize ?? GetEffectiveIconSize();
|
||||
_imageIcon.style.width = size;
|
||||
_imageIcon.style.height = size;
|
||||
_imageIcon.style.backgroundImage = new StyleBackground(texture);
|
||||
_imageIcon.style.display = DisplayStyle.Flex;
|
||||
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
}
|
||||
|
||||
private void ClearMaterialIcon()
|
||||
{
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.text = "";
|
||||
_iconLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearImageIcon()
|
||||
{
|
||||
if (_imageIcon != null)
|
||||
{
|
||||
_imageIcon.style.backgroundImage = StyleKeyword.None;
|
||||
_imageIcon.style.display = DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
@@ -1388,8 +1388,8 @@ namespace UVC.UIToolkit.List
|
||||
// 검색어가 없으면 원본 데이터 복원
|
||||
if (string.IsNullOrEmpty(query))
|
||||
{
|
||||
_treeView.SetRootItems<UTKComponentListItemDataBase>(_rootData);
|
||||
_treeView.Rebuild();
|
||||
_treeView?.SetRootItems<UTKComponentListItemDataBase>(_rootData);
|
||||
_treeView?.Rebuild();
|
||||
ExpandByData(_originalRoots);
|
||||
return;
|
||||
}
|
||||
@@ -1399,8 +1399,8 @@ namespace UVC.UIToolkit.List
|
||||
var filteredWrappers = FilterTree(qLower);
|
||||
|
||||
// 필터링된 결과로 TreeView 갱신
|
||||
_treeView.SetRootItems<UTKComponentListItemDataBase>(filteredWrappers);
|
||||
_treeView.Rebuild();
|
||||
_treeView?.SetRootItems<UTKComponentListItemDataBase>(filteredWrappers);
|
||||
_treeView?.Rebuild();
|
||||
|
||||
// 검색 결과 모두 펼치기
|
||||
ExpandAll(filteredWrappers);
|
||||
|
||||
@@ -10,6 +10,33 @@ namespace UVC.UIToolkit
|
||||
/// 리스트 뷰 컴포넌트.
|
||||
/// Unity ListView를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 리스트 뷰 생성
|
||||
/// var listView = new UTKListView();
|
||||
///
|
||||
/// // 데이터 소스 설정
|
||||
/// var items = new List<string> { "항목 1", "항목 2", "항목 3" };
|
||||
/// listView.itemsSource = items;
|
||||
///
|
||||
/// // 아이템 렌더링
|
||||
/// listView.makeItem = () => new Label();
|
||||
/// listView.bindItem = (element, index) => {
|
||||
/// (element as Label).text = items[index];
|
||||
/// };
|
||||
///
|
||||
/// // 선택 이벤트
|
||||
/// listView.OnItemSelected += (index) => Debug.Log($"선택: {items[index]}");
|
||||
/// listView.OnItemDoubleClicked += (index) => Debug.Log($"더블클릭: {items[index]}");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKListView fixed-item-height="30" selection-type="Single" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKListView : ListView, IDisposable
|
||||
{
|
||||
|
||||
@@ -10,6 +10,39 @@ namespace UVC.UIToolkit
|
||||
/// 트리 뷰 컴포넌트.
|
||||
/// Unity TreeView를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 트리 뷰 생성
|
||||
/// var treeView = new UTKTreeView();
|
||||
///
|
||||
/// // 데이터 구조 설정
|
||||
/// var rootItems = new List<TreeViewItemData<string>> {
|
||||
/// new TreeViewItemData<string>(0, "루트 1", new List<TreeViewItemData<string>> {
|
||||
/// new TreeViewItemData<string>(1, "자식 1-1"),
|
||||
/// new TreeViewItemData<string>(2, "자식 1-2")
|
||||
/// }),
|
||||
/// new TreeViewItemData<string>(3, "루트 2")
|
||||
/// };
|
||||
/// treeView.SetRootItems(rootItems);
|
||||
///
|
||||
/// // 아이템 렌더링
|
||||
/// treeView.makeItem = () => new Label();
|
||||
/// treeView.bindItem = (element, index) => {
|
||||
/// var item = treeView.GetItemDataForIndex<string>(index);
|
||||
/// (element as Label).text = item;
|
||||
/// };
|
||||
///
|
||||
/// // 선택 이벤트
|
||||
/// treeView.OnItemSelected += (index) => Debug.Log($"선택: {index}");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKTreeView fixed-item-height="24" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKTreeView : TreeView, IDisposable
|
||||
{
|
||||
|
||||
@@ -11,6 +11,34 @@ namespace UVC.UIToolkit
|
||||
/// Alert 팝업 컴포넌트.
|
||||
/// 사용자에게 중요한 정보를 알립니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 초기화 (root 설정 필요)
|
||||
/// UTKAlert.Initialize(rootVisualElement);
|
||||
///
|
||||
/// // 기본 알림
|
||||
/// await UTKAlert.Show("알림", "작업이 완료되었습니다.");
|
||||
///
|
||||
/// // 확인 대화상자
|
||||
/// bool confirmed = await UTKAlert.ShowConfirm("삭제 확인", "정말 삭제하시겠습니까?");
|
||||
/// if (confirmed) {
|
||||
/// // 삭제 실행
|
||||
/// }
|
||||
///
|
||||
/// // 타입별 알림
|
||||
/// await UTKAlert.ShowSuccess("성공", "저장되었습니다.");
|
||||
/// await UTKAlert.ShowError("오류", "파일을 찾을 수 없습니다.");
|
||||
/// await UTKAlert.ShowWarning("경고", "변경사항이 저장되지 않았습니다.");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- Alert는 주로 C# 코드로 동적 생성합니다 -->
|
||||
/// <utk:UTKAlert Title="알림" Message="메시지" AlertType="Info" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKAlert : VisualElement, IDisposable
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Common;
|
||||
|
||||
namespace UVC.UIToolkit.Modal
|
||||
{
|
||||
@@ -217,6 +218,9 @@ namespace UVC.UIToolkit.Modal
|
||||
picker._blocker = UTKModalBlocker.Show(parent, 0.5f, false);
|
||||
picker._blocker.OnBlockerClicked += picker.Cancel;
|
||||
|
||||
// 위치 계산 전까지 숨김 (깜빡임 방지)
|
||||
picker.style.visibility = Visibility.Hidden;
|
||||
|
||||
// 피커 추가
|
||||
parent.Add(picker);
|
||||
|
||||
@@ -316,8 +320,9 @@ namespace UVC.UIToolkit.Modal
|
||||
_titleLabel = new Label("Color Picker") { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-color-picker__title");
|
||||
|
||||
_closeButton = new Button { name = "close-btn", text = "\u2715" }; // ✕
|
||||
_closeButton = new Button { name = "close-btn", text = UTKMaterialIcons.Close };
|
||||
_closeButton.AddToClassList("utk-color-picker__close-btn");
|
||||
UTKMaterialIcons.ApplyIconStyle(_closeButton, 18);
|
||||
|
||||
_header.Add(_titleLabel);
|
||||
_header.Add(_closeButton);
|
||||
@@ -453,6 +458,7 @@ namespace UVC.UIToolkit.Modal
|
||||
_cancelButton ??= this.Q<UTKButton>("cancel-btn");
|
||||
_confirmButton ??= this.Q<UTKButton>("confirm-btn");
|
||||
_alphaRow ??= this.Q<VisualElement>("row-a");
|
||||
_header ??= this.Q<VisualElement>("header");
|
||||
}
|
||||
|
||||
private void SetupEvents()
|
||||
@@ -1004,6 +1010,9 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
private void CenterOnScreen()
|
||||
{
|
||||
// position absolute 강제 적용
|
||||
style.position = Position.Absolute;
|
||||
|
||||
schedule.Execute(() =>
|
||||
{
|
||||
var parent = this.parent;
|
||||
@@ -1014,8 +1023,20 @@ namespace UVC.UIToolkit.Modal
|
||||
float selfWidth = resolvedStyle.width;
|
||||
float selfHeight = resolvedStyle.height;
|
||||
|
||||
// NaN 체크
|
||||
if (float.IsNaN(parentWidth) || float.IsNaN(parentHeight) ||
|
||||
float.IsNaN(selfWidth) || float.IsNaN(selfHeight))
|
||||
{
|
||||
// 다음 프레임에 다시 시도
|
||||
schedule.Execute(() => CenterOnScreen());
|
||||
return;
|
||||
}
|
||||
|
||||
style.left = (parentWidth - selfWidth) / 2;
|
||||
style.top = (parentHeight - selfHeight) / 2;
|
||||
|
||||
// 위치 계산 완료 후 표시 (깜빡임 방지)
|
||||
style.visibility = Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.Locale;
|
||||
using UVC.UIToolkit.Common;
|
||||
using UVC.UIToolkit.Input;
|
||||
|
||||
namespace UVC.UIToolkit.Modal
|
||||
@@ -58,6 +59,16 @@ namespace UVC.UIToolkit.Modal
|
||||
/// Debug.Log("Cancelled");
|
||||
/// }
|
||||
///
|
||||
/// // 요일 이름 커스터마이징 (static - 모든 인스턴스에 적용)
|
||||
/// UTKDatePicker.SetDayNames(new[] { "일", "월", "화", "수", "목", "금", "토" });
|
||||
/// UTKDatePicker.SetDayNames(new[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" });
|
||||
///
|
||||
/// // 요일 이름 로컬라이제이션 키 설정
|
||||
/// UTKDatePicker.SetDayNameKeys(new[] { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" });
|
||||
///
|
||||
/// // 요일 이름 기본값으로 초기화
|
||||
/// UTKDatePicker.ResetDayNames();
|
||||
///
|
||||
/// // 인스턴스 직접 생성
|
||||
/// var datePicker = new UTKDatePicker();
|
||||
/// datePicker.SetDate(DateTime.Today);
|
||||
@@ -90,7 +101,12 @@ namespace UVC.UIToolkit.Modal
|
||||
private const string UXML_PATH = "UIToolkit/Modal/UTKDatePicker";
|
||||
private const string USS_PATH = "UIToolkit/Modal/UTKDatePicker";
|
||||
private const int DAYS_IN_GRID = 42; // 6 rows x 7 columns
|
||||
private static readonly string[] DAY_NAME_KEYS = { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" };
|
||||
private static readonly string[] DEFAULT_DAY_NAME_KEYS = { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" };
|
||||
#endregion
|
||||
|
||||
#region Static Fields
|
||||
private static string[] s_dayNameKeys = DEFAULT_DAY_NAME_KEYS;
|
||||
private static string[]? s_customDayNames;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
@@ -124,6 +140,12 @@ namespace UVC.UIToolkit.Modal
|
||||
private UTKNumberStepper? _minuteStepper;
|
||||
private UTKButton? _cancelButton;
|
||||
private UTKButton? _confirmButton;
|
||||
private VisualElement? _header;
|
||||
|
||||
// 드래그 관련 필드
|
||||
private bool _isDragging;
|
||||
private Vector2 _dragStartPosition;
|
||||
private Vector2 _dragStartMousePosition;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -204,6 +226,9 @@ namespace UVC.UIToolkit.Modal
|
||||
picker._blocker = UTKModalBlocker.Show(parent, 0.5f, false);
|
||||
picker._blocker.OnBlockerClicked += picker.Close;
|
||||
|
||||
// 위치 계산 전까지 숨김 (깜빡임 방지)
|
||||
picker.style.visibility = Visibility.Hidden;
|
||||
|
||||
// 피커 추가
|
||||
parent.Add(picker);
|
||||
|
||||
@@ -302,6 +327,9 @@ namespace UVC.UIToolkit.Modal
|
||||
picker._blocker = UTKModalBlocker.Show(parent, 0.5f, false);
|
||||
picker._blocker.OnBlockerClicked += picker.Close;
|
||||
|
||||
// 위치 계산 전까지 숨김 (깜빡임 방지)
|
||||
picker.style.visibility = Visibility.Hidden;
|
||||
|
||||
// 피커 추가
|
||||
parent.Add(picker);
|
||||
|
||||
@@ -399,6 +427,60 @@ namespace UVC.UIToolkit.Modal
|
||||
public void PreviousMonth() => NavigateMonth(-1);
|
||||
public void NextMonth() => NavigateMonth(1);
|
||||
|
||||
/// <summary>
|
||||
/// 요일 이름을 직접 설정합니다. (일, 월, 화, 수, 목, 금, 토 순서로 7개)
|
||||
/// 설정 후 새로 생성되는 피커부터 적용됩니다.
|
||||
/// </summary>
|
||||
/// <param name="dayNames">요일 이름 배열 (7개)</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// UTKDatePicker.SetDayNames(new[] { "일", "월", "화", "수", "목", "금", "토" });
|
||||
/// UTKDatePicker.SetDayNames(new[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" });
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static void SetDayNames(string[] dayNames)
|
||||
{
|
||||
if (dayNames == null || dayNames.Length != 7)
|
||||
{
|
||||
Debug.LogWarning("SetDayNames requires exactly 7 day names (Sun, Mon, Tue, Wed, Thu, Fri, Sat)");
|
||||
return;
|
||||
}
|
||||
|
||||
s_customDayNames = dayNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 요일 이름의 로컬라이제이션 키를 설정합니다. (일, 월, 화, 수, 목, 금, 토 순서로 7개)
|
||||
/// 설정 후 새로 생성되는 피커부터 적용됩니다.
|
||||
/// </summary>
|
||||
/// <param name="dayNameKeys">로컬라이제이션 키 배열 (7개)</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// UTKDatePicker.SetDayNameKeys(new[] { "day_sun", "day_mon", "day_tue", "day_wed", "day_thu", "day_fri", "day_sat" });
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static void SetDayNameKeys(string[] dayNameKeys)
|
||||
{
|
||||
if (dayNameKeys == null || dayNameKeys.Length != 7)
|
||||
{
|
||||
Debug.LogWarning("SetDayNameKeys requires exactly 7 localization keys");
|
||||
return;
|
||||
}
|
||||
|
||||
s_customDayNames = null; // 커스텀 이름 초기화
|
||||
s_dayNameKeys = dayNameKeys;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 요일 이름을 기본값(로컬라이제이션)으로 초기화합니다.
|
||||
/// 설정 후 새로 생성되는 피커부터 적용됩니다.
|
||||
/// </summary>
|
||||
public static void ResetDayNames()
|
||||
{
|
||||
s_customDayNames = null;
|
||||
s_dayNameKeys = DEFAULT_DAY_NAME_KEYS;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
OnClosed?.Invoke();
|
||||
@@ -420,8 +502,9 @@ namespace UVC.UIToolkit.Modal
|
||||
_titleLabel = new Label("Select Date") { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-date-picker__title");
|
||||
|
||||
_closeButton = new Button { name = "close-btn", text = "\u2715" }; // X
|
||||
_closeButton = new Button { name = "close-btn", text = UTKMaterialIcons.Close };
|
||||
_closeButton.AddToClassList("utk-date-picker__close-btn");
|
||||
UTKMaterialIcons.ApplyIconStyle(_closeButton, UTKStyleGuide.FontSizeBody2);
|
||||
|
||||
header.Add(_titleLabel);
|
||||
header.Add(_closeButton);
|
||||
@@ -434,10 +517,12 @@ namespace UVC.UIToolkit.Modal
|
||||
var leftNav = new VisualElement();
|
||||
leftNav.AddToClassList("utk-date-picker__nav-group");
|
||||
|
||||
_prevYearButton = CreateNavButton("prev-year", "\u00AB"); // <<
|
||||
_prevYearButton = CreateNavButton("prev-year", UTKMaterialIcons.FirstPage);
|
||||
_prevYearButton.AddToClassList("utk-date-picker__nav-btn--prev-year");
|
||||
UTKMaterialIcons.ApplyIconStyle(_prevYearButton, 18);
|
||||
|
||||
_prevMonthButton = CreateNavButton("prev-month", "\u2039"); // <
|
||||
_prevMonthButton = CreateNavButton("prev-month", UTKMaterialIcons.ChevronLeft);
|
||||
UTKMaterialIcons.ApplyIconStyle(_prevMonthButton, 18);
|
||||
|
||||
leftNav.Add(_prevYearButton);
|
||||
leftNav.Add(_prevMonthButton);
|
||||
@@ -448,10 +533,12 @@ namespace UVC.UIToolkit.Modal
|
||||
var rightNav = new VisualElement();
|
||||
rightNav.AddToClassList("utk-date-picker__nav-group");
|
||||
|
||||
_nextMonthButton = CreateNavButton("next-month", "\u203A"); // >
|
||||
_nextMonthButton = CreateNavButton("next-month", UTKMaterialIcons.ChevronRight);
|
||||
UTKMaterialIcons.ApplyIconStyle(_nextMonthButton, 18);
|
||||
|
||||
_nextYearButton = CreateNavButton("next-year", "\u00BB"); // >>
|
||||
_nextYearButton = CreateNavButton("next-year", UTKMaterialIcons.LastPage);
|
||||
_nextYearButton.AddToClassList("utk-date-picker__nav-btn--next-year");
|
||||
UTKMaterialIcons.ApplyIconStyle(_nextYearButton, 18);
|
||||
|
||||
rightNav.Add(_nextMonthButton);
|
||||
rightNav.Add(_nextYearButton);
|
||||
@@ -465,9 +552,9 @@ namespace UVC.UIToolkit.Modal
|
||||
_dayNamesRow = new VisualElement { name = "day-names" };
|
||||
_dayNamesRow.AddToClassList("utk-date-picker__day-names");
|
||||
|
||||
for (int i = 0; i < DAY_NAME_KEYS.Length; i++)
|
||||
for (int i = 0; i < s_dayNameKeys.Length; i++)
|
||||
{
|
||||
var dayText = LocalizationManager.Instance.GetString(DAY_NAME_KEYS[i]);
|
||||
var dayText = GetDayName(i);
|
||||
var label = new Label(dayText) { name = $"day-name-{i}" };
|
||||
label.AddToClassList("utk-date-picker__day-name");
|
||||
|
||||
@@ -503,7 +590,7 @@ namespace UVC.UIToolkit.Modal
|
||||
_timeRow = new VisualElement { name = "time-row" };
|
||||
_timeRow.AddToClassList("utk-date-picker__time-row");
|
||||
|
||||
var timeLabel = new Label("시간:");
|
||||
var timeLabel = new Label("Time:");
|
||||
timeLabel.AddToClassList("utk-date-picker__time-label");
|
||||
|
||||
_hourStepper = new UTKNumberStepper(0, 23, 0, 1) { name = "hour-stepper" };
|
||||
@@ -557,6 +644,7 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
private void QueryElements()
|
||||
{
|
||||
_header ??= this.Q<VisualElement>("header");
|
||||
_titleLabel ??= this.Q<Label>("title");
|
||||
_closeButton ??= this.Q<Button>("close-btn");
|
||||
_yearMonthLabel ??= this.Q<Label>("year-month");
|
||||
@@ -599,10 +687,43 @@ namespace UVC.UIToolkit.Modal
|
||||
_nextMonthButton?.RegisterCallback<ClickEvent>(_ => NextMonth());
|
||||
_nextYearButton?.RegisterCallback<ClickEvent>(_ => NextYear());
|
||||
|
||||
// 헤더 드래그 이벤트
|
||||
_header?.RegisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header?.RegisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header?.RegisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
|
||||
// 언어 변경 이벤트 구독
|
||||
LocalizationManager.Instance.OnLanguageChanged += OnLanguageChanged;
|
||||
}
|
||||
|
||||
private void OnHeaderPointerDown(PointerDownEvent evt)
|
||||
{
|
||||
if (evt.target == _closeButton) return;
|
||||
|
||||
_isDragging = true;
|
||||
_dragStartMousePosition = evt.position;
|
||||
_dragStartPosition = new Vector2(resolvedStyle.left, resolvedStyle.top);
|
||||
_header?.CapturePointer(evt.pointerId);
|
||||
evt.StopPropagation();
|
||||
}
|
||||
|
||||
private void OnHeaderPointerMove(PointerMoveEvent evt)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
Vector2 delta = (Vector2)evt.position - _dragStartMousePosition;
|
||||
style.left = _dragStartPosition.x + delta.x;
|
||||
style.top = _dragStartPosition.y + delta.y;
|
||||
}
|
||||
|
||||
private void OnHeaderPointerUp(PointerUpEvent evt)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
_isDragging = false;
|
||||
_header?.ReleasePointer(evt.pointerId);
|
||||
}
|
||||
|
||||
private void OnLanguageChanged(string newLanguage)
|
||||
{
|
||||
UpdateDayNameLabels();
|
||||
@@ -612,15 +733,28 @@ namespace UVC.UIToolkit.Modal
|
||||
{
|
||||
if (_dayNamesRow == null) return;
|
||||
|
||||
for (int i = 0; i < DAY_NAME_KEYS.Length; i++)
|
||||
for (int i = 0; i < s_dayNameKeys.Length; i++)
|
||||
{
|
||||
var label = _dayNamesRow.Q<Label>($"day-name-{i}");
|
||||
if (label != null)
|
||||
{
|
||||
label.text = LocalizationManager.Instance.GetString(DAY_NAME_KEYS[i]);
|
||||
label.text = GetDayName(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 인덱스에 해당하는 요일 이름을 반환합니다.
|
||||
/// 커스텀 이름이 설정되어 있으면 커스텀 이름을, 아니면 로컬라이제이션 키를 사용합니다.
|
||||
/// </summary>
|
||||
private string GetDayName(int index)
|
||||
{
|
||||
if (s_customDayNames != null && index < s_customDayNames.Length)
|
||||
{
|
||||
return s_customDayNames[index];
|
||||
}
|
||||
return LocalizationManager.Instance.GetString(s_dayNameKeys[index]);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods - Logic
|
||||
@@ -860,6 +994,9 @@ namespace UVC.UIToolkit.Modal
|
||||
|
||||
private void CenterOnScreen()
|
||||
{
|
||||
// position absolute 강제 적용
|
||||
style.position = Position.Absolute;
|
||||
|
||||
schedule.Execute(() =>
|
||||
{
|
||||
var parent = this.parent;
|
||||
@@ -870,8 +1007,20 @@ namespace UVC.UIToolkit.Modal
|
||||
float selfWidth = resolvedStyle.width;
|
||||
float selfHeight = resolvedStyle.height;
|
||||
|
||||
// NaN 체크
|
||||
if (float.IsNaN(parentWidth) || float.IsNaN(parentHeight) ||
|
||||
float.IsNaN(selfWidth) || float.IsNaN(selfHeight))
|
||||
{
|
||||
// 다음 프레임에 다시 시도
|
||||
schedule.Execute(() => CenterOnScreen());
|
||||
return;
|
||||
}
|
||||
|
||||
style.left = (parentWidth - selfWidth) / 2;
|
||||
style.top = (parentHeight - selfHeight) / 2;
|
||||
|
||||
// 위치 계산 완료 후 표시 (깜빡임 방지)
|
||||
style.visibility = Visibility.Visible;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -890,10 +1039,10 @@ namespace UVC.UIToolkit.Modal
|
||||
string startText = _rangeStartDate?.ToString("yyyy-MM-dd") ?? "---";
|
||||
string endText = _rangeEndDate?.ToString("yyyy-MM-dd") ?? "---";
|
||||
|
||||
string stateIndicator = _rangeState == RangeSelectionState.SelectingStart ? "▶ " : " ";
|
||||
string endStateIndicator = _rangeState == RangeSelectionState.SelectingEnd ? "▶ " : " ";
|
||||
string stateIndicator = _rangeState == RangeSelectionState.SelectingStart ? "" : " ";
|
||||
string endStateIndicator = _rangeState == RangeSelectionState.SelectingEnd ? "" : " ";
|
||||
|
||||
_rangeInfoLabel.text = $"{stateIndicator}시작: {startText} {endStateIndicator}종료: {endText}";
|
||||
_rangeInfoLabel.text = $"{stateIndicator}Start: {startText} {endStateIndicator}End: {endText}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,6 +10,35 @@ namespace UVC.UIToolkit
|
||||
/// 모달 창 컴포넌트.
|
||||
/// 사용자 정의 콘텐츠를 포함할 수 있는 모달 대화상자입니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 모달 생성 및 표시
|
||||
/// var modal = new UTKModal();
|
||||
/// modal.Title = "설정";
|
||||
/// modal.Size = UTKModal.ModalSize.Medium;
|
||||
/// modal.OnClosed += () => Debug.Log("모달 닫힘");
|
||||
///
|
||||
/// // 콘텐츠 추가
|
||||
/// var content = new Label("모달 내용");
|
||||
/// modal.AddContent(content);
|
||||
///
|
||||
/// // 푸터 버튼 추가
|
||||
/// modal.AddFooterButton("확인", UTKButton.ButtonVariant.Primary, () => modal.Close());
|
||||
/// modal.AddFooterButton("취소", UTKButton.ButtonVariant.Normal, () => modal.Close());
|
||||
///
|
||||
/// // 표시
|
||||
/// modal.Show(rootElement);
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKModal Title="설정" Size="Medium" ShowCloseButton="true">
|
||||
/// <ui:Label text="모달 내용을 여기에 추가하세요" />
|
||||
/// </utk:UTKModal>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKModal : VisualElement, IDisposable
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit.Common;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
@@ -10,6 +11,37 @@ namespace UVC.UIToolkit
|
||||
/// 알림 창 컴포넌트.
|
||||
/// 화면 모서리에 표시되는 알림 메시지입니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 초기화 (root 설정 필요)
|
||||
/// UTKNotification.Initialize(rootVisualElement);
|
||||
///
|
||||
/// // 기본 알림
|
||||
/// UTKNotification.Show("알림", "새로운 메시지가 있습니다.");
|
||||
///
|
||||
/// // 타입별 알림
|
||||
/// UTKNotification.ShowSuccess("성공", "저장되었습니다.");
|
||||
/// UTKNotification.ShowError("오류", "실패했습니다.");
|
||||
/// UTKNotification.ShowWarning("경고", "주의가 필요합니다.");
|
||||
///
|
||||
/// // 위치 설정
|
||||
/// UTKNotification.Show("알림", "메시지", position: NotificationPosition.BottomRight);
|
||||
///
|
||||
/// // 액션 버튼 있는 알림
|
||||
/// UTKNotification.Show("알림", "메시지", actions: new[] {
|
||||
/// ("확인", () => Debug.Log("확인")),
|
||||
/// ("취소", () => Debug.Log("취소"))
|
||||
/// });
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- Notification은 주로 C# 코드로 동적 생성합니다 -->
|
||||
/// <utk:UTKNotification Title="알림" Message="메시지" Type="Info" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKNotification : VisualElement, IDisposable
|
||||
{
|
||||
@@ -129,6 +161,14 @@ namespace UVC.UIToolkit
|
||||
|
||||
CreateUI();
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
// UXML에서 로드될 때 속성이 설정된 후 UI 갱신
|
||||
RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
if (_titleLabel != null) _titleLabel.text = _title;
|
||||
if (_messageLabel != null) _messageLabel.text = _message;
|
||||
UpdateType();
|
||||
});
|
||||
}
|
||||
|
||||
public UTKNotification(string title, string message, NotificationType type = NotificationType.Info) : this()
|
||||
@@ -210,14 +250,16 @@ namespace UVC.UIToolkit
|
||||
|
||||
_iconLabel = new Label { name = "icon" };
|
||||
_iconLabel.AddToClassList("utk-notification__icon");
|
||||
UTKMaterialIcons.ApplyIconStyle(_iconLabel, 20);
|
||||
_header.Add(_iconLabel);
|
||||
|
||||
_titleLabel = new Label { name = "title" };
|
||||
_titleLabel.AddToClassList("utk-notification__title");
|
||||
_header.Add(_titleLabel);
|
||||
|
||||
_closeButton = new Button { name = "close-btn", text = "✕" };
|
||||
_closeButton = new Button { name = "close-btn", text = UTKMaterialIcons.Close };
|
||||
_closeButton.AddToClassList("utk-notification__close-btn");
|
||||
UTKMaterialIcons.ApplyIconStyle(_closeButton, 16);
|
||||
_closeButton.RegisterCallback<ClickEvent>(_ => Close());
|
||||
_header.Add(_closeButton);
|
||||
|
||||
@@ -270,10 +312,10 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
_iconLabel.text = _type switch
|
||||
{
|
||||
NotificationType.Success => "✓",
|
||||
NotificationType.Warning => "⚠",
|
||||
NotificationType.Error => "✕",
|
||||
_ => "ℹ"
|
||||
NotificationType.Success => UTKMaterialIcons.CheckCircle,
|
||||
NotificationType.Warning => UTKMaterialIcons.Warning,
|
||||
NotificationType.Error => UTKMaterialIcons.Error,
|
||||
_ => UTKMaterialIcons.Info
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,46 @@ namespace UVC.UIToolkit
|
||||
/// 패널 컨테이너 컴포넌트.
|
||||
/// 콘텐츠를 그룹화하고 시각적으로 구분합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 패널
|
||||
/// var panel = new UTKPanel();
|
||||
/// panel.Title = "설정";
|
||||
/// panel.AddContent(new Label("패널 내용"));
|
||||
///
|
||||
/// // 접을 수 있는 패널
|
||||
/// panel.IsCollapsible = true;
|
||||
/// panel.IsCollapsed = false;
|
||||
///
|
||||
/// // 헤더 액션 버튼 추가
|
||||
/// panel.AddHeaderAction(UTKMaterialIcons.Settings, () => Debug.Log("설정"));
|
||||
///
|
||||
/// // 푸터 표시
|
||||
/// panel.ShowFooter = true;
|
||||
/// panel.AddFooterContent(new Label("푸터 내용"));
|
||||
///
|
||||
/// // 변형 스타일
|
||||
/// panel.Variant = UTKPanel.PanelVariant.Elevated;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 패널 -->
|
||||
/// <utk:UTKPanel Title="제목">
|
||||
/// <ui:Label text="패널 내용" />
|
||||
/// </utk:UTKPanel>
|
||||
///
|
||||
/// <!-- 접을 수 있는 패널 -->
|
||||
/// <utk:UTKPanel Title="고급 설정" IsCollapsible="true">
|
||||
/// <ui:Label text="내용" />
|
||||
/// </utk:UTKPanel>
|
||||
///
|
||||
/// <!-- 외곽선 스타일 -->
|
||||
/// <utk:UTKPanel Title="외곽선" Variant="Outlined" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKPanel : VisualElement, IDisposable
|
||||
{
|
||||
|
||||
@@ -10,6 +10,35 @@ namespace UVC.UIToolkit
|
||||
/// 토스트 알림 컴포넌트.
|
||||
/// 화면 하단에 일시적으로 표시되는 알림입니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 초기화 (root 설정 필요)
|
||||
/// UTKToast.Initialize(rootVisualElement);
|
||||
///
|
||||
/// // 기본 토스트
|
||||
/// UTKToast.Show("저장되었습니다.");
|
||||
///
|
||||
/// // 타입별 토스트
|
||||
/// UTKToast.ShowSuccess("성공적으로 완료되었습니다.");
|
||||
/// UTKToast.ShowError("오류가 발생했습니다.");
|
||||
/// UTKToast.ShowWarning("주의가 필요합니다.");
|
||||
/// UTKToast.ShowInfo("정보 메시지");
|
||||
///
|
||||
/// // 지속시간 설정 (ms)
|
||||
/// UTKToast.Show("잠시 표시", duration: 2000);
|
||||
///
|
||||
/// // 닫기 버튼 표시
|
||||
/// UTKToast.Show("메시지", showCloseButton: true);
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 토스트는 주로 C# 코드로 동적 생성합니다 -->
|
||||
/// <utk:UTKToast Message="저장됨" Type="Success" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKToast : VisualElement, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,29 @@ namespace UVC.UIToolkit
|
||||
/// 최소-최대 범위 슬라이더 컴포넌트.
|
||||
/// Unity MinMaxSlider를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 범위 슬라이더
|
||||
/// var slider = new UTKMinMaxSlider();
|
||||
/// slider.label = "가격 범위";
|
||||
/// slider.lowLimit = 0;
|
||||
/// slider.highLimit = 1000;
|
||||
/// slider.MinValue = 100;
|
||||
/// slider.MaxValue = 500;
|
||||
///
|
||||
/// // 값 변경 이벤트
|
||||
/// slider.OnValueChanged += (range) => {
|
||||
/// Debug.Log($"범위: {range.x} ~ {range.y}");
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKMinMaxSlider label="범위" low-limit="0" high-limit="100" min-value="20" max-value="80" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKMinMaxSlider : MinMaxSlider, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,40 @@ namespace UVC.UIToolkit
|
||||
/// 프로그레스 바 컴포넌트.
|
||||
/// Unity ProgressBar를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 프로그레스 바
|
||||
/// var progressBar = new UTKProgressBar();
|
||||
/// progressBar.title = "다운로드 중...";
|
||||
/// progressBar.MinValue = 0;
|
||||
/// progressBar.MaxValue = 100;
|
||||
/// progressBar.Value = 50;
|
||||
///
|
||||
/// // 값 표시 설정
|
||||
/// progressBar.ShowValue = true;
|
||||
/// progressBar.ShowPercentage = true;
|
||||
///
|
||||
/// // 무한 로딩 (불확정 상태)
|
||||
/// progressBar.IsIndeterminate = true;
|
||||
///
|
||||
/// // 변형 스타일
|
||||
/// progressBar.Variant = UTKProgressBar.ProgressBarVariant.Success;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 프로그레스 바 -->
|
||||
/// <utk:UTKProgressBar title="진행률" low-value="0" high-value="100" value="30" />
|
||||
///
|
||||
/// <!-- 퍼센트 표시 -->
|
||||
/// <utk:UTKProgressBar ShowPercentage="true" value="75" />
|
||||
///
|
||||
/// <!-- 성공 스타일 -->
|
||||
/// <utk:UTKProgressBar Variant="Success" value="100" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKProgressBar : ProgressBar, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,31 @@ namespace UVC.UIToolkit
|
||||
/// 슬라이더 컴포넌트.
|
||||
/// Unity Slider를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 기본 슬라이더
|
||||
/// var slider = new UTKSlider();
|
||||
/// slider.label = "볼륨";
|
||||
/// slider.lowValue = 0;
|
||||
/// slider.highValue = 100;
|
||||
/// slider.value = 50;
|
||||
/// slider.OnValueChanged += (value) => Debug.Log($"볼륨: {value}");
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 슬라이더 -->
|
||||
/// <utk:UTKSlider label="밝기" low-value="0" high-value="100" value="50" />
|
||||
///
|
||||
/// <!-- 값 표시 -->
|
||||
/// <utk:UTKSlider label="투명도" low-value="0" high-value="1" value="0.5" show-input-field="true" />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKSlider label="잠금" IsEnabled="false" value="75" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKSlider : Slider, IDisposable
|
||||
{
|
||||
|
||||
@@ -9,6 +9,36 @@ namespace UVC.UIToolkit
|
||||
/// 탭 컴포넌트.
|
||||
/// Unity Tab을 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // UTKTabView와 함께 사용
|
||||
/// var tabView = new UTKTabView();
|
||||
///
|
||||
/// var tab1 = new UTKTab();
|
||||
/// tab1.Text = "일반";
|
||||
/// tab1.Add(new Label("일반 탭 내용"));
|
||||
/// tabView.Add(tab1);
|
||||
///
|
||||
/// var tab2 = new UTKTab();
|
||||
/// tab2.Text = "고급";
|
||||
/// tab2.IsEnabled = false; // 비활성화
|
||||
/// tabView.Add(tab2);
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKTabView>
|
||||
/// <utk:UTKTab label="일반">
|
||||
/// <ui:Label text="일반 내용" />
|
||||
/// </utk:UTKTab>
|
||||
/// <utk:UTKTab label="고급">
|
||||
/// <ui:Label text="고급 내용" />
|
||||
/// </utk:UTKTab>
|
||||
/// </utk:UTKTabView>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKTab : Tab, IDisposable
|
||||
{
|
||||
|
||||
@@ -10,6 +10,39 @@ namespace UVC.UIToolkit
|
||||
/// 탭 뷰 컴포넌트.
|
||||
/// Unity TabView를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 탭 뷰 생성
|
||||
/// var tabView = new UTKTabView();
|
||||
///
|
||||
/// // 탭 추가
|
||||
/// var tab1 = tabView.AddTab("일반", UTKMaterialIcons.Settings);
|
||||
/// tab1.Add(new Label("일반 설정 내용"));
|
||||
///
|
||||
/// var tab2 = tabView.AddTab("고급", UTKMaterialIcons.Build);
|
||||
/// tab2.Add(new Label("고급 설정 내용"));
|
||||
///
|
||||
/// // 탭 변경 이벤트
|
||||
/// tabView.OnTabChanged += (index, tab) => Debug.Log($"탭 {index} 선택됨");
|
||||
///
|
||||
/// // 탭 선택
|
||||
/// tabView.SelectedIndex = 0;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <utk:UTKTabView>
|
||||
/// <utk:UTKTab label="일반">
|
||||
/// <ui:Label text="일반 탭 내용" />
|
||||
/// </utk:UTKTab>
|
||||
/// <utk:UTKTab label="고급">
|
||||
/// <ui:Label text="고급 탭 내용" />
|
||||
/// </utk:UTKTab>
|
||||
/// </utk:UTKTabView>
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKTabView : TabView, IDisposable
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user