From acd430c5d718317ad26a595f5e39e48a5a4a0f6e Mon Sep 17 00:00:00 2001 From: logonkhi Date: Thu, 5 Feb 2026 19:12:16 +0900 Subject: [PATCH] =?UTF-8?q?UTKIntPropertyItemView=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/UTKFloatPropertyItemViewUss.uss | 1 - .../Views/UTKIntPropertyItemViewUss.uss | 47 +++++- .../UIToolkit/UTKPropertyListWindowSample.cs | 15 +- .../Property/Items/UTKIntPropertyItem.cs | 32 +++- .../Property/Views/UTKIntPropertyItemView.cs | 137 +++++++++++++++++- 5 files changed, 218 insertions(+), 14 deletions(-) diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss index 1d77ebf7..572821a4 100644 --- a/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss +++ b/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss @@ -32,7 +32,6 @@ .utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__field { flex-grow: 0; min-width: 60px; - max-width: 80px; } /* ReadOnly 상태: Slider 숨김 */ diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss index fbec2430..b8e23d43 100644 --- a/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss +++ b/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss @@ -12,19 +12,24 @@ flex-grow: 1; } -/* 기본 상태: Slider 숨김, Field만 표시 */ +/* 기본 상태: Slider, Stepper 숨김, Field만 표시 */ .utk-property-item-view--int .utk-property-item-view__slider { display: none; flex-grow: 1; margin-right: 8px; } +.utk-property-item-view--int .utk-property-item-view__stepper { + display: none; + flex-grow: 1; +} + .utk-property-item-view--int .utk-property-item-view__field { display: flex; flex-grow: 1; } -/* UseSlider 상태: Slider + Field 모두 표시 */ +/* UseSlider 상태: Slider + Field 모두 표시, Stepper 숨김 */ .utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__slider { display: flex; } @@ -32,14 +37,48 @@ .utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__field { flex-grow: 0; min-width: 60px; - max-width: 80px; } -/* ReadOnly 상태: Slider 숨김 */ +.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__stepper { + display: none; +} + +/* UseStepper 상태: Stepper만 표시, Field/Slider 숨김 */ +.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__stepper { + display: flex; +} + +.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__field { + display: none; +} + +.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__slider { + display: none; +} + +/* UseSlider + UseStepper 상태: Slider + Stepper 모두 표시, Field 숨김 */ +.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__slider { + display: flex; +} + +.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__stepper { + display: flex; +} + +.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__field { + display: none; +} + +/* ReadOnly 상태: Slider/Stepper 숨김, Field만 표시 (flex-grow: 1) */ .utk-property-item-view--int.utk-property-item-view--readonly .utk-property-item-view__slider { display: none; } +.utk-property-item-view--int.utk-property-item-view--readonly .utk-property-item-view__stepper { + display: none; +} + .utk-property-item-view--int.utk-property-item-view--readonly .utk-property-item-view__field { + display: flex; flex-grow: 1; } diff --git a/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs b/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs index bc12bb94..1293ea7e 100644 --- a/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs +++ b/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs @@ -114,9 +114,20 @@ namespace UVC.Sample.UIToolkit entries.Add(new UTKIntPropertyItem("int", "Int", 42, 0, 100, true)); // Int (읽기 전용) - var roInt = new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, true); - roInt.IsReadOnly = true; + var roInt = new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, true, isReadOnly: true); entries.Add(roInt); + + // Int stepper (편집 가능) + entries.Add(new UTKIntPropertyItem("int2", "Int2", 42, 0, 100, false, true)); + + // Int stepper (읽기 전용) + entries.Add(new UTKIntPropertyItem("int2_ro", "Int2 (RO)", 99, 0, 100, false, true, isReadOnly: true)); + + // Int stepper (편집 가능) + entries.Add(new UTKIntPropertyItem("int3", "Int3", 42, 0, 100, true, true)); + + // Int stepper (읽기 전용) + entries.Add(new UTKIntPropertyItem("int3_ro", "Int3 (RO)", 99, 0, 100, true, true, isReadOnly: true)); // Float (편집 가능) entries.Add(new UTKFloatPropertyItem("float", "Float", 3.14f, 0f, 10f, true)); diff --git a/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKIntPropertyItem.cs b/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKIntPropertyItem.cs index 097753eb..33916779 100644 --- a/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKIntPropertyItem.cs +++ b/Assets/Scripts/UVC/UIToolkit/Property/Items/UTKIntPropertyItem.cs @@ -1,4 +1,5 @@ #nullable enable +using System; namespace UVC.UIToolkit { @@ -10,8 +11,10 @@ namespace UVC.UIToolkit { #region Fields private bool _useSlider; + private bool _useStepper; private int _minValue; private int _maxValue = 100; + private int _step = 1; #endregion #region Properties @@ -22,7 +25,31 @@ namespace UVC.UIToolkit public bool UseSlider { get => _useSlider; - set => _useSlider = value; + set + { + _useSlider = value; + // UseSlider가 true이면 UseStepper는 false로 + if (value) _useStepper = false; + } + } + + /// 스테퍼(증감 버튼) 사용 여부 + public bool UseStepper + { + get => _useStepper; + set + { + _useStepper = value; + // UseStepper가 true이면 UseSlider는 false로 + if (value) _useSlider = false; + } + } + + /// 스테퍼 증감 단위 (기본값: 1) + public int Step + { + get => _step; + set => _step = Math.Max(1, value); } /// 최소값 (슬라이더 모드) @@ -62,12 +89,13 @@ namespace UVC.UIToolkit /// 최대값 /// 슬라이더 사용 여부 /// 읽기 전용 여부 - public UTKIntPropertyItem(string id, string name, int initialValue, int minValue, int maxValue, bool useSlider = true, bool isReadOnly = false) + public UTKIntPropertyItem(string id, string name, int initialValue, int minValue, int maxValue, bool useSlider = false, bool useStepper = false, bool isReadOnly = false) : base(id, name, initialValue) { _minValue = minValue; _maxValue = maxValue; _useSlider = useSlider; + _useStepper = useStepper; IsReadOnly = isReadOnly; } #endregion diff --git a/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKIntPropertyItemView.cs b/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKIntPropertyItemView.cs index b1a5a170..fa0bff3e 100644 --- a/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKIntPropertyItemView.cs +++ b/Assets/Scripts/UVC/UIToolkit/Property/Views/UTKIntPropertyItemView.cs @@ -7,7 +7,7 @@ namespace UVC.UIToolkit { /// /// Int 속성 View 클래스입니다. - /// UTKIntegerField 또는 UTKSliderInt를 사용하여 int 값을 표시/편집합니다. + /// UTKIntegerField, UTKSliderInt, 또는 UTKNumberStepper를 사용하여 int 값을 표시/편집합니다. /// /// 사용법 (단독 사용): /// @@ -20,8 +20,14 @@ namespace UVC.UIToolkit /// view.MaxValue = 100; /// parent.Add(view); /// + /// // 스테퍼 모드 + /// var stepperView = new UTKIntPropertyItemView(); + /// stepperView.UseStepper = true; + /// stepperView.Step = 5; + /// /// // UXML에서 사용 /// <utk:UTKIntPropertyItemView label="수량" value="10" use-slider="true" min-value="0" max-value="100" /> + /// <utk:UTKIntPropertyItemView label="개수" value="1" use-stepper="true" step="1" /> /// /// [UxmlElement] @@ -30,11 +36,14 @@ namespace UVC.UIToolkit #region Fields private UTKIntegerField? _intField; private UTKSliderInt? _slider; + private UTKNumberStepper? _stepper; private int _value; private int _minValue; private int _maxValue = 100; + private int _step = 1; private bool _useSlider; + private bool _useStepper; private IUTKPropertyItem? _boundData; #endregion @@ -103,7 +112,39 @@ namespace UVC.UIToolkit if (_useSlider != value) { _useSlider = value; - UpdateSliderClass(); + if (value) _useStepper = false; + UpdateModeClass(); + } + } + } + + /// 스테퍼(증감 버튼) 사용 여부 + [UxmlAttribute("use-stepper")] + public bool UseStepper + { + get => _useStepper; + set + { + if (_useStepper != value) + { + _useStepper = value; + if (value) _useSlider = false; + UpdateModeClass(); + } + } + } + + /// 스테퍼 증감 단위 + [UxmlAttribute("step")] + public int Step + { + get => _step; + set + { + _step = Math.Max(1, value); + if (_stepper != null) + { + _stepper.Step = _step; } } } @@ -168,7 +209,7 @@ namespace UVC.UIToolkit RegisterEvents(); // 슬라이더 클래스 업데이트 - UpdateSliderClass(); + UpdateModeClass(); UpdateValueUI(); UpdateReadOnlyState(); @@ -178,6 +219,7 @@ namespace UVC.UIToolkit { _slider = this.Q("slider-field"); _intField = this.Q("value-field"); + _stepper = this.Q("stepper-field"); // Fallback: UXML에서 못 찾으면 생성 if (_valueContainer != null) @@ -198,6 +240,16 @@ namespace UVC.UIToolkit _intField.AddToClassList("utk-property-item-view__field"); _valueContainer.Add(_intField); } + + if (_stepper == null) + { + _stepper = new UTKNumberStepper(_minValue, _maxValue, _value, _step, IsReadOnly) + { + name = "stepper-field" + }; + _stepper.AddToClassList("utk-property-item-view__stepper"); + _valueContainer.Add(_stepper); + } } // 초기 값 설정 @@ -212,10 +264,19 @@ namespace UVC.UIToolkit _intField.SetValueWithoutNotify(_value); _intField.isReadOnly = IsReadOnly; } + if (_stepper != null) + { + _stepper.MinValue = _minValue; + _stepper.MaxValue = _maxValue; + _stepper.Step = _step; + _stepper.SetValue(_value, false); + _stepper.IsReadOnly = IsReadOnly; + } } - private void UpdateSliderClass() + private void UpdateModeClass() { + // 슬라이더 클래스 if (_useSlider) { AddToClassList("utk-property-item-view--slider"); @@ -224,6 +285,16 @@ namespace UVC.UIToolkit { RemoveFromClassList("utk-property-item-view--slider"); } + + // 스테퍼 클래스 + if (_useStepper) + { + AddToClassList("utk-property-item-view--stepper"); + } + else + { + RemoveFromClassList("utk-property-item-view--stepper"); + } } #endregion @@ -249,6 +320,11 @@ namespace UVC.UIToolkit { _slider.IsEnabled = !isReadOnly; } + + if (_stepper != null) + { + _stepper.IsReadOnly = isReadOnly; + } } #endregion @@ -264,6 +340,11 @@ namespace UVC.UIToolkit { _slider.OnValueChanged += OnSliderChanged; } + + if (_stepper != null) + { + _stepper.OnValueChanged += OnStepperChanged; + } } private void UnregisterEvents() @@ -277,6 +358,11 @@ namespace UVC.UIToolkit { _slider.OnValueChanged -= OnSliderChanged; } + + if (_stepper != null) + { + _stepper.OnValueChanged -= OnStepperChanged; + } } private void OnIntFieldChanged(int newValue) @@ -323,6 +409,20 @@ namespace UVC.UIToolkit } } } + + private void OnStepperChanged(int newValue) + { + if (_value != newValue) + { + _value = newValue; + OnValueChanged?.Invoke(newValue); + + if (_boundData != null && _boundData.Value != newValue) + { + _boundData.Value = newValue; + } + } + } #endregion #region Value Update @@ -337,6 +437,11 @@ namespace UVC.UIToolkit { _slider.SetValueWithoutNotify(_value); } + + if (_stepper != null && _stepper.Value != _value) + { + _stepper.SetValue(_value, false); + } } #endregion @@ -369,11 +474,23 @@ namespace UVC.UIToolkit { _minValue = intItem.MinValue; _maxValue = intItem.MaxValue; + _step = intItem.Step; + // 모드 변경 확인 + bool modeChanged = false; if (_useSlider != intItem.UseSlider) { _useSlider = intItem.UseSlider; - UpdateSliderClass(); + modeChanged = true; + } + if (_useStepper != intItem.UseStepper) + { + _useStepper = intItem.UseStepper; + modeChanged = true; + } + if (modeChanged) + { + UpdateModeClass(); } // 슬라이더 범위 업데이트 @@ -382,6 +499,14 @@ namespace UVC.UIToolkit _slider.lowValue = _minValue; _slider.highValue = _maxValue; } + + // 스테퍼 설정 업데이트 + if (_stepper != null) + { + _stepper.MinValue = _minValue; + _stepper.MaxValue = _maxValue; + _stepper.Step = _step; + } } data.OnTypedValueChanged += OnDataValueChanged; @@ -422,6 +547,8 @@ namespace UVC.UIToolkit OnValueChanged = null; _intField = null; _slider = null; + _stepper?.Dispose(); + _stepper = null; } base.Dispose(disposing);