#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// 체크박스 컴포넌트. /// 선택/해제 상태를 토글할 수 있습니다. /// /// /// C# 코드에서 사용: /// /// // 기본 체크박스 /// var checkbox = new UTKCheckBox(); /// checkbox.Text = "약관에 동의합니다"; /// checkbox.OnValueChanged += (isChecked) => Debug.Log($"체크: {isChecked}"); /// /// // 상태 설정 /// checkbox.IsChecked = true; /// checkbox.IsIndeterminate = true; // 부분 선택 상태 /// /// UXML에서 사용: /// /// /// /// /// /// /// /// /// /// /// /// /// [UxmlElement] public partial class UTKCheckBox : VisualElement, IDisposable { #region Constants private const string USS_PATH = "UIToolkit/Button/UTKCheckBox"; #endregion #region Fields private bool _disposed; private VisualElement? _checkBox; private Label? _checkIcon; private Label? _label; private string _text = ""; private bool _isChecked; private bool _isIndeterminate; private bool _isEnabled = true; #endregion #region Events /// 체크 상태 변경 이벤트 public event Action? OnValueChanged; #endregion #region Properties /// 라벨 텍스트 [UxmlAttribute("text")] public string Text { get => _text; set { _text = value; if (_label != null) _label.text = value; } } /// 체크 상태 [UxmlAttribute("is-checked")] public bool IsChecked { get => _isChecked; set => SetChecked(value, true); } /// 불확정 상태 (일부 선택됨) [UxmlAttribute("is-indeterminate")] public bool IsIndeterminate { get => _isIndeterminate; set { _isIndeterminate = value; UpdateState(); } } /// 활성화 상태 [UxmlAttribute("is-enabled")] public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; SetEnabled(value); EnableInClassList("utk-checkbox--disabled", !value); } } #endregion #region Constructor public UTKCheckBox() { UTKThemeManager.Instance.ApplyThemeToElement(this); var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } CreateUI(); SetupEvents(); SubscribeToThemeChanges(); // UXML에서 로드될 때 속성이 설정된 후 UI 갱신 // Unity 6의 소스 생성기는 Deserialize에서 필드에 직접 값을 할당하므로 // AttachToPanelEvent를 사용하여 패널에 연결된 후 UI를 갱신 RegisterCallback(OnAttachToPanel); } public UTKCheckBox(string text, bool isChecked = false) : this() { Text = text; SetChecked(isChecked, false); } #endregion #region UI Creation private void CreateUI() { AddToClassList("utk-checkbox"); focusable = true; _checkBox = new VisualElement { name = "checkbox" }; _checkBox.AddToClassList("utk-checkbox__box"); _checkIcon = new Label { name = "check-icon", text = UTKMaterialIcons.Check }; _checkIcon.AddToClassList("utk-checkbox__icon"); UTKMaterialIcons.ApplyIconStyle(_checkIcon, 14); _checkBox.Add(_checkIcon); Add(_checkBox); _label = new Label { name = "label" }; _label.AddToClassList("utk-checkbox__label"); Add(_label); UpdateState(); } private void SetupEvents() { RegisterCallback(OnClick); RegisterCallback(OnKeyDown, TrickleDown.TrickleDown); } private void SubscribeToThemeChanges() { UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged; RegisterCallback(OnAttachToPanelForTheme); RegisterCallback(OnDetachFromPanelForTheme); } private void OnAttachToPanelForTheme(AttachToPanelEvent evt) { UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged; UTKThemeManager.Instance.ApplyThemeToElement(this); } private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt) { UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; } private void OnThemeChanged(UTKTheme theme) { UTKThemeManager.Instance.ApplyThemeToElement(this); } #endregion #region Event Handlers private void OnAttachToPanel(AttachToPanelEvent evt) { // UXML 속성이 설정된 후 한 번만 UI 갱신 UnregisterCallback(OnAttachToPanel); if (_label != null) _label.text = _text; UpdateState(); // IsEnabled 상태 적용 SetEnabled(_isEnabled); EnableInClassList("utk-checkbox--disabled", !_isEnabled); } private void OnClick(ClickEvent evt) { if (!_isEnabled) return; Toggle(); evt.StopPropagation(); } private void OnKeyDown(KeyDownEvent evt) { if (!_isEnabled) return; if (evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.Space) { Toggle(); evt.StopPropagation(); } } #endregion #region Methods /// /// 체크 상태 토글 /// public void Toggle() { _isIndeterminate = false; SetChecked(!_isChecked, true); } /// /// 체크 상태 설정 /// public void SetChecked(bool value, bool notify) { if (_isChecked == value && !_isIndeterminate) return; _isChecked = value; _isIndeterminate = false; UpdateState(); if (notify) { OnValueChanged?.Invoke(value); } } private void UpdateState() { EnableInClassList("utk-checkbox--checked", _isChecked && !_isIndeterminate); EnableInClassList("utk-checkbox--indeterminate", _isIndeterminate); if (_checkIcon != null) { _checkIcon.text = _isIndeterminate ? UTKMaterialIcons.Remove : UTKMaterialIcons.Check; } } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; UnregisterCallback(OnAttachToPanelForTheme); UnregisterCallback(OnDetachFromPanelForTheme); OnValueChanged = null; } #endregion } }