#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// 커스텀 버튼 컴포넌트. /// 텍스트와 아이콘을 동시에 표시하거나, 아이콘만 표시할 수 있습니다. /// 배경 색상, 외곽선 굵기 등을 설정할 수 있습니다. /// [UxmlElement] public partial class UTKButton : VisualElement, IDisposable { #region Constants private const string USS_PATH = "UIToolkit/Button/UTKButton"; #endregion #region Fields private bool _disposed; private Label? _iconLabel; private Label? _textLabel; private string _text = ""; private string _icon = ""; private ButtonVariant _variant = ButtonVariant.Normal; private ButtonSize _size = ButtonSize.Medium; private Color? _backgroundColor; private int _borderWidth = -1; private bool _iconOnly; private bool _isEnabled = true; #endregion #region Events /// 버튼 클릭 이벤트 public event Action? OnClicked; #endregion #region Properties /// 버튼 텍스트 [UxmlAttribute] public string Text { get => _text; set { _text = value; UpdateContent(); } } /// 아이콘 (유니코드 문자 또는 텍스트) [UxmlAttribute] public string Icon { get => _icon; set { _icon = value; UpdateContent(); } } /// 버튼 스타일 변형 [UxmlAttribute] public ButtonVariant Variant { get => _variant; set { _variant = value; UpdateVariant(); } } /// 버튼 크기 [UxmlAttribute] public ButtonSize Size { get => _size; set { _size = value; UpdateSize(); } } /// 커스텀 배경 색상 (null이면 기본 스타일 사용) public Color? BackgroundColor { get => _backgroundColor; set { _backgroundColor = value; UpdateCustomStyles(); } } /// 외곽선 굵기 (-1이면 기본값 사용) [UxmlAttribute] public int BorderWidth { get => _borderWidth; set { _borderWidth = value; UpdateCustomStyles(); } } /// 아이콘만 표시 모드 [UxmlAttribute] public bool IconOnly { get => _iconOnly; set { _iconOnly = value; UpdateContent(); } } /// 버튼 활성화 상태 [UxmlAttribute] public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; SetEnabled(value); EnableInClassList("utk-button--disabled", !value); } } #endregion #region Enums public enum ButtonVariant { Normal, Primary, Secondary, Ghost, Danger, OutlineNormal, OutlinePrimary, OutlineDanger } public enum ButtonSize { Small, Medium, Large } #endregion #region Constructor public UTKButton() { UTKThemeManager.Instance.ApplyThemeToElement(this); var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } CreateUI(); SetupEvents(); SubscribeToThemeChanges(); } public UTKButton(string text, string icon = "", ButtonVariant variant = ButtonVariant.Normal) : this() { _text = text; _icon = icon; _variant = variant; UpdateContent(); UpdateVariant(); } #endregion #region UI Creation private void CreateUI() { AddToClassList("utk-button"); focusable = true; _iconLabel = new Label { name = "icon", pickingMode = PickingMode.Ignore }; _iconLabel.AddToClassList("utk-button__icon"); Add(_iconLabel); _textLabel = new Label { name = "text", pickingMode = PickingMode.Ignore }; _textLabel.AddToClassList("utk-button__text"); Add(_textLabel); UpdateContent(); UpdateVariant(); UpdateSize(); } private void SetupEvents() { RegisterCallback(OnClick); RegisterCallback(OnKeyDown); } private void SubscribeToThemeChanges() { UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged; RegisterCallback(_ => { UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; }); } private void OnThemeChanged(UTKTheme theme) { UTKThemeManager.Instance.ApplyThemeToElement(this); } #endregion #region Event Handlers private void OnClick(ClickEvent evt) { if (!_isEnabled) return; OnClicked?.Invoke(); evt.StopPropagation(); } private void OnKeyDown(KeyDownEvent evt) { if (!_isEnabled) return; if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.Space) { OnClicked?.Invoke(); evt.StopPropagation(); } } #endregion #region Update Methods private void UpdateContent() { bool hasIcon = !string.IsNullOrEmpty(_icon); bool hasText = !string.IsNullOrEmpty(_text); if (_iconLabel != null) { _iconLabel.text = _icon; _iconLabel.style.display = hasIcon ? DisplayStyle.Flex : DisplayStyle.None; } if (_textLabel != null) { _textLabel.text = _text; _textLabel.style.display = (_iconOnly || !hasText) ? DisplayStyle.None : DisplayStyle.Flex; } EnableInClassList("utk-button--icon-only", _iconOnly || (hasIcon && !hasText)); EnableInClassList("utk-button--has-icon", hasIcon && hasText && !_iconOnly); } private void UpdateVariant() { RemoveFromClassList("utk-button--normal"); RemoveFromClassList("utk-button--primary"); RemoveFromClassList("utk-button--secondary"); RemoveFromClassList("utk-button--ghost"); RemoveFromClassList("utk-button--danger"); RemoveFromClassList("utk-button--outline-normal"); RemoveFromClassList("utk-button--outline-primary"); RemoveFromClassList("utk-button--outline-danger"); var variantClass = _variant switch { ButtonVariant.Primary => "utk-button--primary", ButtonVariant.Secondary => "utk-button--secondary", ButtonVariant.Ghost => "utk-button--ghost", ButtonVariant.Danger => "utk-button--danger", ButtonVariant.OutlineNormal => "utk-button--outline-normal", ButtonVariant.OutlinePrimary => "utk-button--outline-primary", ButtonVariant.OutlineDanger => "utk-button--outline-danger", _ => "utk-button--normal" }; AddToClassList(variantClass); } private void UpdateSize() { RemoveFromClassList("utk-button--small"); RemoveFromClassList("utk-button--medium"); RemoveFromClassList("utk-button--large"); var sizeClass = _size switch { ButtonSize.Small => "utk-button--small", ButtonSize.Large => "utk-button--large", _ => "utk-button--medium" }; AddToClassList(sizeClass); } private void UpdateCustomStyles() { if (_backgroundColor.HasValue) { style.backgroundColor = _backgroundColor.Value; } else { style.backgroundColor = StyleKeyword.Null; } if (_borderWidth >= 0) { style.borderTopWidth = _borderWidth; style.borderBottomWidth = _borderWidth; style.borderLeftWidth = _borderWidth; style.borderRightWidth = _borderWidth; } else { style.borderTopWidth = StyleKeyword.Null; style.borderBottomWidth = StyleKeyword.Null; style.borderLeftWidth = StyleKeyword.Null; style.borderRightWidth = StyleKeyword.Null; } } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; OnClicked = null; } #endregion } }