#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// 토글 스위치 컴포넌트. /// Unity Toggle을 래핑하여 커스텀 스타일을 적용합니다. /// /// /// C# 코드에서 사용: /// /// // 기본 토글 /// var toggle = new UTKToggle(); /// toggle.label = "알림 받기"; /// toggle.IsOn = true; /// toggle.OnValueChanged += (isOn) => Debug.Log($"토글: {isOn}"); /// /// // 상호작용 불가능 (읽기 전용, 프로그래밍 방식으로만 변경 가능) /// var readOnlyToggle = new UTKToggle("읽기 전용 토글"); /// readOnlyToggle.IsOn = true; /// readOnlyToggle.IsInteractive = false; // 사용자 클릭/키보드 입력 차단 /// readOnlyToggle.SetOn(false, true); // 프로그래밍 방식으로는 변경 가능 /// /// // IsEnabled vs IsInteractive /// var disabledToggle = new UTKToggle("완전 비활성화"); /// disabledToggle.IsEnabled = false; // 시각적 비활성화 + 모든 변경 불가 /// /// var nonInteractiveToggle = new UTKToggle("상호작용 비활성화"); /// nonInteractiveToggle.IsInteractive = false; // 사용자 입력만 차단, 시각적 활성화 유지 /// /// UXML에서 사용: /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// [UxmlElement] public partial class UTKToggle : Toggle, IDisposable { #region Constants private const string USS_PATH = "UIToolkit/Button/UTKToggle"; #endregion #region Fields private bool _disposed; private bool _isEnabled = true; private bool _isInteractive = true; #endregion #region Events /// 상태 변경 이벤트 public event Action? OnValueChanged; #endregion #region Properties /// 토글 상태 [UxmlAttribute("is-on")] public bool IsOn { get => value; set => SetOn(value, true); } /// 활성화 상태 [UxmlAttribute("is-enabled")] public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; SetEnabled(value); EnableInClassList("utk-toggle--disabled", !value); } } /// /// 상호작용 가능 여부. false일 경우 마우스/키보드 입력을 무시하지만 프로그래밍 방식으로는 값 변경 가능. /// IsEnabled와 달리 시각적으로는 활성화 상태를 유지합니다. /// [UxmlAttribute("is-interactive")] public bool IsInteractive { get => _isInteractive; set { _isInteractive = value; UpdateInteractiveState(); EnableInClassList("utk-toggle--non-interactive", !value); } } #endregion #region Constructor public UTKToggle() : base() { UTKThemeManager.Instance.ApplyThemeToElement(this); var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } SetupStyles(); SetupEvents(); SubscribeToThemeChanges(); } public UTKToggle(string label) : this() { this.label = label; } #endregion #region Setup private void SetupStyles() { AddToClassList("utk-toggle"); } private void SetupEvents() { RegisterCallback>(OnToggleValueChanged); RegisterCallback(OnMouseDown, TrickleDown.TrickleDown); 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 OnToggleValueChanged(ChangeEvent evt) { EnableInClassList("utk-toggle--on", evt.newValue); OnValueChanged?.Invoke(evt.newValue); } private void OnMouseDown(MouseDownEvent evt) { if (!_isInteractive) { evt.StopImmediatePropagation(); evt.PreventDefault(); } } private void OnKeyDown(KeyDownEvent evt) { if (!_isInteractive) { // Space나 Enter 키로 토글 변경 방지 if (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter) { evt.StopImmediatePropagation(); evt.PreventDefault(); } } } #endregion #region Methods /// /// 상태 설정 /// public void SetOn(bool newValue, bool notify) { if (value == newValue) return; SetValueWithoutNotify(newValue); EnableInClassList("utk-toggle--on", newValue); if (notify) { OnValueChanged?.Invoke(newValue); } } /// /// 상호작용 상태 업데이트 /// private void UpdateInteractiveState() { // IsInteractive가 false일 때 포커스 불가능하게 설정 focusable = _isInteractive; } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; UnregisterCallback(OnAttachToPanelForTheme); UnregisterCallback(OnDetachFromPanelForTheme); OnValueChanged = null; UnregisterCallback>(OnToggleValueChanged); UnregisterCallback(OnMouseDown, TrickleDown.TrickleDown); UnregisterCallback(OnKeyDown, TrickleDown.TrickleDown); } #endregion } }