#nullable enable using System; using UnityEngine; using UnityEngine.UIElements; namespace UVC.UIToolkit { /// /// 최소-최대 범위 슬라이더 컴포넌트. /// Unity MinMaxSlider를 래핑하여 커스텀 스타일을 적용합니다. /// 두 개의 핸들로 범위(최소값~최대값)를 선택할 수 있습니다. /// /// /// MinMaxSlider(범위 슬라이더)란? /// /// MinMaxSlider는 일반 슬라이더와 달리 두 개의 핸들을 가지며, 사용자가 범위를 지정할 수 있습니다. /// 가격 필터, 시간대 선택, 수치 범위 지정 등에 활용됩니다. /// /// /// 주요 속성: /// /// lowLimit - 슬라이더 전체 범위의 최소값 (왼쪽 끝) /// highLimit - 슬라이더 전체 범위의 최대값 (오른쪽 끝) /// MinValue - 현재 선택된 범위의 최소값 (왼쪽 핸들) /// MaxValue - 현재 선택된 범위의 최대값 (오른쪽 핸들) /// /// /// Limit vs Value 차이: /// /// - lowLimit/highLimit: 슬라이더가 표현할 수 있는 전체 범위 (고정) /// - MinValue/MaxValue: 사용자가 선택한 현재 범위 (변경 가능) /// /// /// 실제 활용 예시: /// /// 쇼핑몰 - 가격대 필터 (₩10,000 ~ ₩50,000) /// 미디어 플레이어 - 재생 구간 선택 (클리핑) /// 검색 필터 - 날짜/시간 범위 선택 /// 게임 설정 - 레벨/난이도 범위 필터 /// /// /// /// C# 코드에서 사용: /// /// // 가격 범위 슬라이더 /// var priceSlider = new UTKMinMaxSlider(); /// priceSlider.label = "가격 범위"; /// priceSlider.lowLimit = 0; // 전체 범위: 0원 /// priceSlider.highLimit = 100000; // 전체 범위: 100,000원 /// priceSlider.MinValue = 10000; // 선택 범위: 10,000원부터 /// priceSlider.MaxValue = 50000; // 선택 범위: 50,000원까지 /// /// // 값 변경 이벤트 (Vector2로 반환) /// priceSlider.OnValueChanged += (range) => { /// Debug.Log($"가격: {range.x:N0}원 ~ {range.y:N0}원"); /// FilterProducts(range.x, range.y); /// }; /// /// // 생성자로 한 번에 설정 /// var ageSlider = new UTKMinMaxSlider("연령대", 0, 100, 20, 40); /// /// // 시간 범위 선택 (0~24시) /// var timeSlider = new UTKMinMaxSlider("영업 시간", 0, 24, 9, 18); /// timeSlider.OnValueChanged += (time) => { /// Debug.Log($"영업시간: {time.x:F0}시 ~ {time.y:F0}시"); /// }; /// /// UXML에서 사용: /// /// /// /// /// /// /// /// /// /// /// [UxmlElement] public partial class UTKMinMaxSlider : MinMaxSlider, IDisposable { #region Constants private const string USS_PATH = "UIToolkit/Slider/UTKMinMaxSlider"; #endregion #region Fields private bool _disposed; private bool _isEnabled = true; #endregion #region Events /// 값 변경 이벤트 public event Action? OnValueChanged; #endregion #region Properties /// 최소값 public float MinValue { get => minValue; set => minValue = value; } /// 최대값 public float MaxValue { get => maxValue; set => maxValue = value; } /// 활성화 상태 [UxmlAttribute("is-enabled")] public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; SetEnabled(value); EnableInClassList("utk-minmax-slider--disabled", !value); } } #endregion #region Constructor public UTKMinMaxSlider() : base() { UTKThemeManager.Instance.ApplyThemeToElement(this); var uss = Resources.Load(USS_PATH); if (uss != null) { styleSheets.Add(uss); } SetupStyles(); SetupEvents(); SubscribeToThemeChanges(); } public UTKMinMaxSlider(string label, float minLimit = 0f, float maxLimit = 100f, float minValue = 0f, float maxValue = 100f) : this() { this.label = label; this.lowLimit = minLimit; this.highLimit = maxLimit; this.minValue = minValue; this.maxValue = maxValue; } #endregion #region Setup private void SetupStyles() { AddToClassList("utk-minmax-slider"); } private void SetupEvents() { RegisterCallback>(OnSliderValueChanged); } 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 OnSliderValueChanged(ChangeEvent evt) { OnValueChanged?.Invoke(evt.newValue); } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged; OnValueChanged = null; UnregisterCallback>(OnSliderValueChanged); } #endregion } }