diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 3df4cc4d..6a06fb10 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -27,7 +27,9 @@
"Bash(cmd /c \"echo ^^^^^^\")",
"Bash(del \"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKBoolPropertyItemView.uxml\")",
"Bash(del:*)",
- "Bash(cmd /c \"del /q \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKIntPropertyItemViewSliderUss.uss\"\" \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKFloatPropertyItemViewSliderUss.uss\"\"\")"
+ "Bash(cmd /c \"del /q \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKIntPropertyItemViewSliderUss.uss\"\" \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKFloatPropertyItemViewSliderUss.uss\"\"\")",
+ "mcp__UnityMCP__refresh_unity",
+ "Bash(powershell \"Get-Process Unity -ErrorAction SilentlyContinue | Select-Object -First 1 | Format-List\")"
],
"deny": [],
"ask": []
diff --git a/Assets/Resources/UIToolkit/Button/UTKButton.uss b/Assets/Resources/UIToolkit/Button/UTKButton.uss
index 6d1bb19b..08a00ef5 100644
--- a/Assets/Resources/UIToolkit/Button/UTKButton.uss
+++ b/Assets/Resources/UIToolkit/Button/UTKButton.uss
@@ -64,7 +64,6 @@
font-size: var(--font-size-body2);
color: var(--color-text-primary);
-unity-text-align: middle-center;
- margin-top: 2px;
}
/* ===================================
diff --git a/Assets/Resources/UIToolkit/Dropdown/UTKDropdownUss.uss b/Assets/Resources/UIToolkit/Dropdown/UTKDropdownUss.uss
index 4dde602c..f9e16a08 100644
--- a/Assets/Resources/UIToolkit/Dropdown/UTKDropdownUss.uss
+++ b/Assets/Resources/UIToolkit/Dropdown/UTKDropdownUss.uss
@@ -26,6 +26,7 @@
font-size: var(--font-size-body2);
color: var(--color-text-primary);
-unity-font-style: normal;
+ display: none;
}
/* ===================================
diff --git a/Assets/Resources/UIToolkit/Dropdown/UTKEnumDropDownUss.uss b/Assets/Resources/UIToolkit/Dropdown/UTKEnumDropDownUss.uss
index 367b88e6..7d2a14e8 100644
--- a/Assets/Resources/UIToolkit/Dropdown/UTKEnumDropDownUss.uss
+++ b/Assets/Resources/UIToolkit/Dropdown/UTKEnumDropDownUss.uss
@@ -26,6 +26,7 @@
font-size: var(--font-size-body2);
color: var(--color-text-primary);
-unity-font-style: normal;
+ display: none;
}
/* ===================================
diff --git a/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss b/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss
new file mode 100644
index 00000000..c3f339f2
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss
@@ -0,0 +1,156 @@
+/*
+ * ===================================
+ * UTKFloatStepper.uss
+ * Float Stepper Component Styles
+ * ===================================
+ */
+
+/* ===================================
+ Root Container
+ =================================== */
+
+.utk-number-stepper {
+ flex-direction: row;
+ align-items: stretch;
+ height: var(--size-input-height);
+}
+
+
+/* ===================================
+ Text Input Field
+ =================================== */
+
+.utk-number-stepper__input {
+ flex-grow: 1;
+ margin-right: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: var(--font-size-body2);
+}
+
+.utk-number-stepper__text-input {
+ flex-grow: 1;
+ background-color: var(--color-bg-input);
+ color: var(--color-text-primary);
+ --unity-cursor-color: var(--color-text-primary);
+ border-top-left-radius: var(--radius-s);
+ border-bottom-left-radius: var(--radius-s);
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-top-width: var(--border-width);
+ border-bottom-width: var(--border-width);
+ border-left-width: var(--border-width);
+ border-right-width: 0;
+ border-top-color: var(--color-border);
+ border-bottom-color: var(--color-border);
+ border-left-color: var(--color-border);
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-left: var(--space-m);
+ padding-right: var(--space-m);
+ margin: 0;
+ -unity-text-align: middle-right;
+}
+
+
+/* ===================================
+ Button Container
+ =================================== */
+
+.utk-number-stepper__buttons {
+ flex-direction: column;
+ width: 16px;
+}
+
+
+/* ===================================
+ Stepper Buttons (Base)
+ =================================== */
+
+.utk-number-stepper__btn {
+ flex-grow: 1;
+ width: 16px;
+ font-size: 6px;
+ padding: 0;
+ margin: 0;
+ background-color: var(--color-btn-normal);
+ color: var(--color-text-secondary);
+ border-top-width: var(--border-width);
+ border-left-width: 0;
+ border-right-width: var(--border-width);
+ border-top-color: var(--color-border);
+ border-right-color: var(--color-border);
+ border-bottom-color: var(--color-border);
+ transition-duration: var(--anim-fast);
+ transition-property: background-color, color;
+ height: 12px;
+}
+
+.utk-number-stepper__btn:hover {
+ background-color: var(--color-btn-hover);
+ color: var(--color-text-primary);
+}
+
+.utk-number-stepper__btn:active {
+ background-color: var(--color-btn-pressed);
+}
+
+
+/* ===================================
+ Up Button
+ =================================== */
+
+.utk-number-stepper__btn--up {
+ border-bottom-width: 0;
+ border-top-left-radius: 0;
+ border-top-right-radius: var(--radius-s);
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+
+/* ===================================
+ Down Button
+ =================================== */
+
+.utk-number-stepper__btn--down {
+ border-bottom-width: var(--border-width);
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: var(--radius-s);
+}
+
+
+/* ===================================
+ Disabled State
+ =================================== */
+
+.utk-number-stepper--disabled {
+ cursor: arrow;
+}
+
+.utk-number-stepper--disabled .utk-number-stepper__text-input {
+ background-color: var(--color-btn-disabled);
+ color: var(--color-text-disabled);
+ cursor: arrow;
+}
+
+.utk-number-stepper--disabled .utk-number-stepper__text-input:hover {
+ border-color: var(--color-border);
+}
+
+.utk-number-stepper--disabled .utk-number-stepper__btn {
+ background-color: var(--color-btn-disabled);
+ color: var(--color-text-disabled);
+ cursor: arrow;
+}
+
+.utk-number-stepper--disabled .utk-number-stepper__btn:hover {
+ background-color: var(--color-btn-disabled);
+ color: var(--color-text-disabled);
+}
+
+.utk-number-stepper--disabled .utk-number-stepper__btn:active {
+ background-color: var(--color-btn-disabled);
+}
diff --git a/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss.meta b/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss.meta
new file mode 100644
index 00000000..f570f199
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 53011be3e472c5d46a86079c477e8848
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
+ disableValidation: 0
diff --git a/Assets/Resources/UIToolkit/Input/UTKNumberStepper.uss b/Assets/Resources/UIToolkit/Input/UTKIntStepper.uss
similarity index 99%
rename from Assets/Resources/UIToolkit/Input/UTKNumberStepper.uss
rename to Assets/Resources/UIToolkit/Input/UTKIntStepper.uss
index e8ea95fd..aea760b1 100644
--- a/Assets/Resources/UIToolkit/Input/UTKNumberStepper.uss
+++ b/Assets/Resources/UIToolkit/Input/UTKIntStepper.uss
@@ -1,6 +1,6 @@
/*
* ===================================
- * UTKNumberStepper.uss
+ * UTKIntStepper.uss
* Number Stepper Component Styles
* ===================================
*/
diff --git a/Assets/Resources/UIToolkit/Input/UTKNumberStepper.uss.meta b/Assets/Resources/UIToolkit/Input/UTKIntStepper.uss.meta
similarity index 100%
rename from Assets/Resources/UIToolkit/Input/UTKNumberStepper.uss.meta
rename to Assets/Resources/UIToolkit/Input/UTKIntStepper.uss.meta
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml
new file mode 100644
index 00000000..336994eb
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml.meta b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml.meta
new file mode 100644
index 00000000..f6e050e7
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemView.uxml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: d9772db144ccd284ea63c1b71dae6711
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss
new file mode 100644
index 00000000..67010e87
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss
@@ -0,0 +1,23 @@
+/*
+ * UTKButtonItemViewUss.uss
+ * Button PropertyItem View 전용 스타일
+ */
+
+.utk-property-item-view--button {
+ /* Button View 전용 스타일 */
+}
+
+.utk-property-item-view--button .utk-property-item-view__button {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+/* 라벨이 없는 경우 - 버튼이 전체 너비 사용 */
+.utk-property-item-view--button-no-label .utk-property-item-view__label {
+ display: none;
+}
+
+.utk-property-item-view--button-no-label .utk-property-item-view__value {
+ flex-grow: 1;
+}
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss.meta b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss.meta
new file mode 100644
index 00000000..072ee221
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKButtonItemViewUss.uss.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 98488360e849b8c4b91bba3629e93b50
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
+ disableValidation: 0
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKColorStatePropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKColorStatePropertyItemViewUss.uss
index f8e8dc04..73a44966 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKColorStatePropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKColorStatePropertyItemViewUss.uss
@@ -18,7 +18,7 @@
border-radius: 4px;
border-width: 1px;
border-color: rgba(255, 255, 255, 0.2);
- margin-right: 4px;
+ margin-right: 0;
}
.utk-property-item-view--color-state .utk-property-item-view__picker-btn {
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKDropdownPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKDropdownPropertyItemViewUss.uss
index b8897a5c..414596b9 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKDropdownPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKDropdownPropertyItemViewUss.uss
@@ -12,3 +12,9 @@
margin-left: 0;
margin-right: 0;
}
+
+#readonly-field {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
\ No newline at end of file
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKEnumPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKEnumPropertyItemViewUss.uss
index b6197a60..2c7ea121 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKEnumPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKEnumPropertyItemViewUss.uss
@@ -12,3 +12,9 @@
margin-left: 0;
margin-right: 0;
}
+
+#readonly-field {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
\ No newline at end of file
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss
index 572821a4..7435ec95 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKFloatPropertyItemViewUss.uss
@@ -24,14 +24,27 @@
flex-grow: 1;
}
+.utk-property-item-view--float .utk-property-item-view__stepper {
+ display: flex;
+ flex-grow: 1;
+ margin-left: 0;
+}
+
+
+
/* UseSlider 상태: Slider + Field 모두 표시 */
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__slider {
display: flex;
+ flex-grow: 1;
}
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__field {
- flex-grow: 0;
- min-width: 60px;
+ flex-grow: 0.25;
+}
+
+.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__stepper {
+ flex-grow: 0.25;
+ width: 50px;
}
/* ReadOnly 상태: Slider 숨김 */
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss
index b8e23d43..81b6e16e 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKIntPropertyItemViewUss.uss
@@ -10,6 +10,7 @@
.utk-property-item-view--int .utk-property-item-view__value {
flex-direction: row;
flex-grow: 1;
+ align-items: center;
}
/* 기본 상태: Slider, Stepper 숨김, Field만 표시 */
@@ -17,6 +18,7 @@
display: none;
flex-grow: 1;
margin-right: 8px;
+ margin-left: 0;
}
.utk-property-item-view--int .utk-property-item-view__stepper {
@@ -27,16 +29,17 @@
.utk-property-item-view--int .utk-property-item-view__field {
display: flex;
flex-grow: 1;
+ margin-bottom: 0;
}
/* UseSlider 상태: Slider + Field 모두 표시, Stepper 숨김 */
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__slider {
display: flex;
+ flex-grow: 1;
}
-.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__field {
- flex-grow: 0;
- min-width: 60px;
+.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__field {
+ flex-grow: 0.25;
}
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__stepper {
@@ -46,6 +49,8 @@
/* UseStepper 상태: Stepper만 표시, Field/Slider 숨김 */
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__stepper {
display: flex;
+ flex-grow: 1;
+ margin-left: 0;
}
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__field {
@@ -59,10 +64,13 @@
/* 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;
+ flex-grow: 1;
}
.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__stepper {
- display: flex;
+ display: flex;
+ flex-grow: 0.25;
+ width: 50px;
}
.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__field {
@@ -81,4 +89,5 @@
.utk-property-item-view--int.utk-property-item-view--readonly .utk-property-item-view__field {
display: flex;
flex-grow: 1;
+ margin-bottom: 0;
}
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemView.uxml b/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemView.uxml
index 1c1482ed..e3b67ac4 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemView.uxml
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemView.uxml
@@ -4,10 +4,10 @@
-
+
-
+
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemViewUss.uss
index 802f6205..20568360 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKIntRangePropertyItemViewUss.uss
@@ -17,6 +17,7 @@
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;
+ margin-left: 0;
}
.utk-property-item-view--int-range .utk-property-item-view__range-separator {
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml
new file mode 100644
index 00000000..e11f08e7
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml.meta b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml.meta
new file mode 100644
index 00000000..ca576387
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemView.uxml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: a7bd219f87024f5448d8a67d962d52bf
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss
new file mode 100644
index 00000000..4b996c72
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss
@@ -0,0 +1,20 @@
+/*
+ * UTKMultiSelectDropdownPropertyItemViewUss.uss
+ * MultiSelectDropdown PropertyItem View 전용 스타일
+ */
+
+.utk-property-item-view--multiselect-dropdown {
+ /* MultiSelectDropdown View 전용 스타일 */
+}
+
+.utk-property-item-view--multiselect-dropdown .utk-property-item-view__multiselect-dropdown {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+#readonly-field {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
\ No newline at end of file
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss.meta b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss.meta
new file mode 100644
index 00000000..013d6b50
--- /dev/null
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKMultiSelectDropdownPropertyItemViewUss.uss.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ff5637017b4f4ba499070014ab8ba603
+ScriptedImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 2
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+ script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
+ disableValidation: 0
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKPropertyItemViewCommonUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKPropertyItemViewCommonUss.uss
index 6571b4b4..73b8e01c 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKPropertyItemViewCommonUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKPropertyItemViewCommonUss.uss
@@ -26,12 +26,13 @@
border-bottom-color: var(--color-border);
}
-.utk-property-item-view:hover {
+/* .utk-property-item-view:hover {
background-color: var(--color-collection-item-hover);
-}
+} */
.utk-property-item-view TemplateContainer {
flex-grow: 1;
+ min-height: 28px;
}
/* ===================================
@@ -82,6 +83,8 @@
white-space: nowrap;
-unity-text-align: middle-left;
padding-right: 10px;
+ align-items: center;
+ justify-content: center;
}
/* ===================================
@@ -92,6 +95,7 @@
flex-grow: 1;
flex-direction: row;
align-items: stretch;
+ align-self: center;
}
/* ===================================
@@ -344,3 +348,61 @@
color: var(--color-text-secondary);
margin-top: var(--space-xs);
}
+
+/* ===================================
+ 라벨 없는 상태 (ShowLabel=false)
+ =================================== */
+
+.utk-property-item-view--no-label .utk-property-item-view__label {
+ display: none;
+}
+
+.utk-property-item-view--no-label .utk-property-item-view__value {
+ flex-grow: 1;
+}
+
+/* ===================================
+ Stepper 스타일 (Float용)
+ =================================== */
+
+.utk-property-item-view__stepper {
+ flex-direction: column;
+ margin-left: 4px;
+ flex-shrink: 0;
+}
+
+.utk-property-item-view__stepper-btn {
+ width: 16px;
+ height: 11px;
+ min-height: 11px;
+ padding: 0;
+ margin: 0;
+ background-color: var(--color-btn-normal);
+ border-width: var(--border-width);
+ border-color: var(--color-border);
+ font-size: 10px;
+ -unity-text-align: middle-center;
+}
+
+.utk-property-item-view__stepper-btn:hover {
+ background-color: var(--color-btn-hover);
+}
+
+.utk-property-item-view__stepper-btn:active {
+ background-color: var(--color-btn-active);
+}
+
+.utk-property-item-view__stepper-btn--up {
+ border-top-left-radius: var(--radius-s);
+ border-top-right-radius: var(--radius-s);
+ border-bottom-width: 0;
+}
+
+.utk-property-item-view__stepper-btn--down {
+ border-bottom-left-radius: var(--radius-s);
+ border-bottom-right-radius: var(--radius-s);
+}
+
+.utk-property-item-view__field--with-stepper {
+ flex-grow: 1;
+}
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKRadioPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKRadioPropertyItemViewUss.uss
index 0d283976..83725b3f 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKRadioPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKRadioPropertyItemViewUss.uss
@@ -16,3 +16,9 @@
.utk-property-item-view--radio .utk-property-item-view__radio {
margin-right: 12px;
}
+
+#readonly-field {
+ flex-grow: 1;
+ margin-left: 0;
+ margin-right: 0;
+}
\ No newline at end of file
diff --git a/Assets/Resources/UIToolkit/Property/Views/UTKStringPropertyItemViewUss.uss b/Assets/Resources/UIToolkit/Property/Views/UTKStringPropertyItemViewUss.uss
index deff1ca5..fa052d03 100644
--- a/Assets/Resources/UIToolkit/Property/Views/UTKStringPropertyItemViewUss.uss
+++ b/Assets/Resources/UIToolkit/Property/Views/UTKStringPropertyItemViewUss.uss
@@ -10,3 +10,18 @@
.utk-property-item-view--string .utk-property-item-view__field {
flex-grow: 1;
}
+
+/* ActionButton 스타일 */
+.utk-property-item-view--string .utk-property-item-view__action-button {
+ margin-left: 4px;
+ flex-shrink: 0;
+ height: 24px;
+}
+
+.utk-property-item-view--string .utk-property-item-view__action-button-icon {
+ margin-left: 4px;
+ flex-shrink: 0;
+ min-width: 24px;
+ max-width: 24px;
+ height: 24px;
+}
diff --git a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uss b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uss
similarity index 84%
rename from Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uss
rename to Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uss
index da4047d1..e292a4af 100644
--- a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uss
+++ b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uss
@@ -1,6 +1,6 @@
/*
* ===================================
- * UTKNumberStepperSample.uss
+ * UTKIntStepperSample.uss
* Number Stepper Sample Specific Styles
* ===================================
*/
diff --git a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uss.meta b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uss.meta
similarity index 100%
rename from Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uss.meta
rename to Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uss.meta
diff --git a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uxml b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uxml
similarity index 77%
rename from Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uxml
rename to Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uxml
index cc65fc5d..22d83ca7 100644
--- a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uxml
+++ b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uxml
@@ -1,7 +1,7 @@
-
+
@@ -10,7 +10,7 @@
-
+
@@ -18,7 +18,7 @@
-
+
@@ -26,7 +26,7 @@
-
+
diff --git a/Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uxml.meta b/Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uxml.meta
similarity index 100%
rename from Assets/Resources/UIToolkit/Sample/Input/UTKNumberStepperSample.uxml.meta
rename to Assets/Resources/UIToolkit/Sample/Input/UTKIntStepperSample.uxml.meta
diff --git a/Assets/Resources/UIToolkit/Style/UTKDefaultStyle.uss b/Assets/Resources/UIToolkit/Style/UTKDefaultStyle.uss
index f0b4d35a..aea2a4ef 100644
--- a/Assets/Resources/UIToolkit/Style/UTKDefaultStyle.uss
+++ b/Assets/Resources/UIToolkit/Style/UTKDefaultStyle.uss
@@ -261,8 +261,9 @@ Textfield 항목 스타일
height: 24px;
width: auto;
margin-top: 0;
+ margin-bottom: 0;
margin-right: 0;
- margin-left: 0;
+ margin-left: 0;
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Medium');
/* --unity-selection-color: rgba(54, 98, 160, 0.651); 선택 색상 */
--unity-cursor-color: var(--color-base-01); /* 캐럿(커서) 색상 흰색 */
diff --git a/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs b/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs
index 0b7ef0b0..14938f7d 100644
--- a/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs
+++ b/Assets/Sample/UIToolkit/UTKPropertyListWindowSample.cs
@@ -82,6 +82,16 @@ namespace UVC.Sample.UIToolkit
Debug.Log($"Property Changed: {args.PropertyId} {args.PropertyName} ({args.PropertyType}) = {args.NewValue}");
};
+ _propertyWindow.OnPropertyClicked += args =>
+ {
+ Debug.Log($"Property Clicked: {args.Id} {args.DisplayName} ({args.PropertyType})");
+ };
+
+ _propertyWindow.OnPropertyButtonClicked += (id, actionName) =>
+ {
+ Debug.Log($"Button Clicked: {id} - Action: {actionName}");
+ };
+
// 샘플 데이터 생성
var entries = CreateSampleEntries();
_propertyWindow.LoadMixedProperties(entries);
@@ -94,17 +104,41 @@ namespace UVC.Sample.UIToolkit
var entries = new List();
// ===== Group에 속하지 않은 개별 아이템들 =====
-
+
// String (편집 가능)
entries.Add(new UTKStringPropertyItem("string", "String", "Editable text"));
-
+
+ // String with Action Button
+ var stringWithButton = new UTKStringPropertyItem("string_with_btn", "String Button", "Click the button");
+ stringWithButton.ActionButton = new UTKButtonItem("btn_string_action", "string_action", "", UTKMaterialIcons.Search,
+ UTKButton.ButtonVariant.OutlineNormal, UTKButton.ButtonSize.Small);
+ stringWithButton.ActionButton.IconOnly = true;
+ stringWithButton.ActionButton.IconSize = 14;
+ entries.Add(stringWithButton);
+
+ var stringWithButton2 = new UTKStringPropertyItem("string_with_btn2", "String Button2", "Click the button");
+ stringWithButton2.ActionButton = new UTKButtonItem("btn_string_action", "string_action", "Search", UTKMaterialIcons.Search,
+ UTKButton.ButtonVariant.OutlineNormal, UTKButton.ButtonSize.Small);
+ stringWithButton2.ActionButton.IconSize = 14;
+ entries.Add(stringWithButton2);
+
+ // String (ShowLabel = false, 전체 너비)
+ var stringNoLabel = new UTKStringPropertyItem("string_no_label", "No Label String", "Full width input");
+ stringNoLabel.ShowLabel = false;
+ entries.Add(stringNoLabel);
+
// String (읽기 전용)
var roString = new UTKStringPropertyItem("string_ro", "String (RO)", "Read-only text", isReadOnly: true);
entries.Add(roString);
// Bool (편집 가능)
entries.Add(new UTKBoolPropertyItem("bool", "Bool", true));
-
+
+ // Bool (ShowLabel = false)
+ var boolNoLabel = new UTKBoolPropertyItem("bool_no_label", "No Label Bool", false);
+ boolNoLabel.ShowLabel = false;
+ entries.Add(boolNoLabel);
+
// Bool (읽기 전용)
var roBool = new UTKBoolPropertyItem("bool_ro", "Bool (RO)", true);
roBool.IsReadOnly = true;
@@ -112,7 +146,12 @@ namespace UVC.Sample.UIToolkit
// Int (편집 가능)
entries.Add(new UTKIntPropertyItem("int", "Int", 42, 0, 100));
-
+
+ // Int (ShowLabel = false)
+ var intNoLabel = new UTKIntPropertyItem("int_no_label", "No Label Int", 75, 0, 100);
+ intNoLabel.ShowLabel = false;
+ entries.Add(intNoLabel);
+
// Int (읽기 전용)
entries.Add(new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, isReadOnly: true));
@@ -134,13 +173,24 @@ namespace UVC.Sample.UIToolkit
// 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));
-
- // Float (읽기 전용)
- var roFloat = new UTKFloatPropertyItem("float_ro", "Float (RO)", 2.71f, 0f, 10f, true);
- roFloat.IsReadOnly = true;
- entries.Add(roFloat);
+ // Float (useSlider=true, useStepper=true)
+ entries.Add(new UTKFloatPropertyItem("float1", "Float (Slider+Stepper)", 3.14f, 0f, 10f, useSlider: true, useStepper: true));
+
+ // Float (useSlider=true, useStepper=false)
+ entries.Add(new UTKFloatPropertyItem("float2", "Float (Slider)", 5.0f, 0f, 10f, useSlider: true, useStepper: false));
+
+ // Float (useSlider=false, useStepper=true)
+ var floatStepper = new UTKFloatPropertyItem("float3", "Float (Stepper)", 2.5f, 0f, 10f, useSlider: false, useStepper: true)
+ {
+ Step = 0.5f // 0.5씩 증감
+ };
+ entries.Add(floatStepper);
+
+ // Float (useSlider=false, useStepper=false)
+ entries.Add(new UTKFloatPropertyItem("float4", "Float (Field)", 7.2f, 0f, 10f, useSlider: false, useStepper: false));
+
+ // Float (ReadOnly=true)
+ entries.Add(new UTKFloatPropertyItem("float_ro", "Float (ReadOnly)", 2.71f, 0f, 10f, useSlider: true, useStepper: false, isReadOnly: true));
// Vector2 (편집 가능)
entries.Add(new UTKVector2PropertyItem("vec2", "Vector2", new Vector2(1, 2)));
@@ -217,15 +267,27 @@ namespace UVC.Sample.UIToolkit
entries.Add(roEnum);
// Dropdown (편집 가능)
- entries.Add(new UTKDropdownPropertyItem("dropdown", "Dropdown",
+ entries.Add(new UTKDropdownPropertyItem("dropdown", "Dropdown",
new List { "Option A", "Option B", "Option C" }, "Option A"));
-
+
// Dropdown (읽기 전용)
- var roDropdown = new UTKDropdownPropertyItem("dropdown_ro", "Dropdown (RO)",
+ var roDropdown = new UTKDropdownPropertyItem("dropdown_ro", "Dropdown (RO)",
new List { "Option X", "Option Y", "Option Z" }, "Option Y");
roDropdown.IsReadOnly = true;
entries.Add(roDropdown);
-
+
+ // MultiSelectDropdown (편집 가능)
+ entries.Add(new UTKMultiSelectDropdownPropertyItem("multiselect", "MultiSelect",
+ new List { "Tag1", "Tag2", "Tag3", "Tag4" },
+ new List { "Tag1", "Tag3" }));
+
+ // MultiSelectDropdown (읽기 전용)
+ var roMultiSelect = new UTKMultiSelectDropdownPropertyItem("multiselect_ro", "MultiSelect (RO)",
+ new List { "Feature A", "Feature B", "Feature C", "Feature D" },
+ new List { "Feature B", "Feature C" });
+ roMultiSelect.IsReadOnly = true;
+ entries.Add(roMultiSelect);
+
// Radio (편집 가능)
entries.Add(new UTKRadioPropertyItem("radio", "Radio",
new List { "Choice 1", "Choice 2", "Choice 3" }, 0));
@@ -261,6 +323,15 @@ namespace UVC.Sample.UIToolkit
// 기본 속성 그룹 (편집 가능)
var basicGroup = new UTKPropertyGroup("basic", "Basic Properties (Editable)");
basicGroup.AddItem(new UTKStringPropertyItem("name", "Name", "Sample Object"));
+
+ // String with Edit Button
+ var pathItem = new UTKStringPropertyItem("path", "File Path", "C:/Users/Sample/file.txt");
+ pathItem.ActionButton = new UTKButtonItem("btn_browse", "browse_file", "", UTKMaterialIcons.FolderOpen,
+ UTKButton.ButtonVariant.OutlineNormal, UTKButton.ButtonSize.Small);
+ pathItem.ActionButton.IconOnly = true;
+ pathItem.ActionButton.IconSize = 14;
+ basicGroup.AddItem(pathItem);
+
basicGroup.AddItem(new UTKStringPropertyItem("description", "Description", "This is a sample object") { IsMultiline = true });
basicGroup.AddItem(new UTKBoolPropertyItem("active", "Is Active", true));
basicGroup.AddItem(new UTKIntPropertyItem("count", "Count", 10, 0, 100, true));
@@ -363,6 +434,9 @@ namespace UVC.Sample.UIToolkit
selectionGroup.AddItem(new UTKEnumPropertyItem("layer", "Layer", SampleLayer.Default));
selectionGroup.AddItem(new UTKDropdownPropertyItem("tag", "Tag",
new List { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Player"));
+ selectionGroup.AddItem(new UTKMultiSelectDropdownPropertyItem("categories", "Categories",
+ new List { "Physics", "Rendering", "Audio", "UI", "Network" },
+ new List { "Physics", "Rendering" }));
selectionGroup.AddItem(new UTKRadioPropertyItem("quality", "Quality",
new List { "Low", "Medium", "High", "Ultra" }, 2));
entries.Add(selectionGroup);
@@ -376,6 +450,11 @@ namespace UVC.Sample.UIToolkit
new List { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Enemy");
roTag.IsReadOnly = true;
selectionGroupRO.AddItem(roTag);
+ var roCategories = new UTKMultiSelectDropdownPropertyItem("categories_ro", "Categories",
+ new List { "Physics", "Rendering", "Audio", "UI", "Network" },
+ new List { "Audio", "UI", "Network" });
+ roCategories.IsReadOnly = true;
+ selectionGroupRO.AddItem(roCategories);
var roQuality = new UTKRadioPropertyItem("quality_ro", "Quality",
new List { "Low", "Medium", "High", "Ultra" }, 3);
roQuality.IsReadOnly = true;
@@ -398,6 +477,75 @@ namespace UVC.Sample.UIToolkit
rangeGroupRO.AddItem(roDamageRange);
entries.Add(rangeGroupRO);
+ // Button 그룹 (라벨 표시 - ShowLabel = true, 기본값)
+ var buttonGroup = new UTKPropertyGroup("buttons", "Buttons (With Label)");
+
+ // Primary 버튼
+ buttonGroup.AddItem(new UTKButtonItem("btn_save", "save", "Save", UTKMaterialIcons.Save,
+ UTKButton.ButtonVariant.Primary, UTKButton.ButtonSize.Medium));
+
+ // Normal 버튼
+ buttonGroup.AddItem(new UTKButtonItem("btn_load", "load", "Load", UTKMaterialIcons.Download,
+ UTKButton.ButtonVariant.Normal, UTKButton.ButtonSize.Medium));
+
+ // Danger 버튼
+ buttonGroup.AddItem(new UTKButtonItem("btn_delete", "delete", "Delete", UTKMaterialIcons.Delete,
+ UTKButton.ButtonVariant.Danger, UTKButton.ButtonSize.Medium));
+
+ // Icon Only 버튼
+ var iconOnlyBtn = new UTKButtonItem("btn_settings", "settings", "", UTKMaterialIcons.Settings,
+ UTKButton.ButtonVariant.OutlinePrimary, UTKButton.ButtonSize.Medium);
+ iconOnlyBtn.IconOnly = true;
+ iconOnlyBtn.IconSize = 16;
+ buttonGroup.AddItem(iconOnlyBtn);
+
+ // Text 버튼
+ buttonGroup.AddItem(new UTKButtonItem("btn_help", "help", "Help", UTKMaterialIcons.Help,
+ UTKButton.ButtonVariant.Text, UTKButton.ButtonSize.Medium));
+
+ entries.Add(buttonGroup);
+
+ // Button 그룹 (라벨 없음 - ShowLabel = false, 전체 너비 사용)
+ var buttonGroupNoLabel = new UTKPropertyGroup("buttons_no_label", "Buttons (No Label)");
+
+ // Primary 버튼 (전체 너비)
+ var btnSaveNoLabel = new UTKButtonItem("btn_save_no_label", "save_no_label", "Save Changes", UTKMaterialIcons.Save,
+ UTKButton.ButtonVariant.Primary, UTKButton.ButtonSize.Medium);
+ btnSaveNoLabel.ShowLabel = false;
+ buttonGroupNoLabel.AddItem(btnSaveNoLabel);
+
+ // Normal 버튼 (전체 너비)
+ var btnLoadNoLabel = new UTKButtonItem("btn_load_no_label", "load_no_label", "Load Data", UTKMaterialIcons.Download,
+ UTKButton.ButtonVariant.Normal, UTKButton.ButtonSize.Medium);
+ btnLoadNoLabel.ShowLabel = false;
+ buttonGroupNoLabel.AddItem(btnLoadNoLabel);
+
+ // Danger 버튼 (전체 너비)
+ var btnDeleteNoLabel = new UTKButtonItem("btn_delete_no_label", "delete_no_label", "Delete All", UTKMaterialIcons.Delete,
+ UTKButton.ButtonVariant.Danger, UTKButton.ButtonSize.Medium);
+ btnDeleteNoLabel.ShowLabel = false;
+ buttonGroupNoLabel.AddItem(btnDeleteNoLabel);
+
+ entries.Add(buttonGroupNoLabel);
+
+ // Individual Button Items (라벨 표시)
+ entries.Add(new UTKButtonItem("btn_apply", "apply", "Apply Changes", UTKMaterialIcons.Check,
+ UTKButton.ButtonVariant.Primary, UTKButton.ButtonSize.Medium));
+
+ entries.Add(new UTKButtonItem("btn_reset", "reset", "Reset to Default", UTKMaterialIcons.Refresh,
+ UTKButton.ButtonVariant.OutlineNormal, UTKButton.ButtonSize.Medium));
+
+ // Individual Button Items (라벨 없음 - 전체 너비)
+ var btnConfirmNoLabel = new UTKButtonItem("btn_confirm_no_label", "confirm_no_label", "Confirm Action", UTKMaterialIcons.CheckCircle,
+ UTKButton.ButtonVariant.Primary, UTKButton.ButtonSize.Medium);
+ btnConfirmNoLabel.ShowLabel = false;
+ entries.Add(btnConfirmNoLabel);
+
+ var btnCancelNoLabel = new UTKButtonItem("btn_cancel_no_label", "cancel_no_label", "Cancel Operation", UTKMaterialIcons.Cancel,
+ UTKButton.ButtonVariant.OutlineNormal, UTKButton.ButtonSize.Medium);
+ btnCancelNoLabel.ShowLabel = false;
+ entries.Add(btnCancelNoLabel);
+
return entries;
}
diff --git a/Assets/Sample/UIToolkit/UTKStyleGuideSample.Input.cs b/Assets/Sample/UIToolkit/UTKStyleGuideSample.Input.cs
index 7850ab4e..9d4aad14 100644
--- a/Assets/Sample/UIToolkit/UTKStyleGuideSample.Input.cs
+++ b/Assets/Sample/UIToolkit/UTKStyleGuideSample.Input.cs
@@ -187,7 +187,7 @@ doubleField.IsEnabled = false;",
var disabledRow = root.Q("stepper-disabled-row");
if (disabledRow != null)
{
- var stepper = new UTKNumberStepper(0, 100, 30, 1);
+ var stepper = new UTKIntStepper(0, 100, 30, 1);
stepper.style.width = 150;
stepper.SetEnabled(false);
disabledRow.Add(stepper);
@@ -195,18 +195,18 @@ doubleField.IsEnabled = false;",
SetCodeSamples(root,
csharpCode: @"// 기본 사용법
-var stepper = new UTKNumberStepper(minValue: 0, maxValue: 100, value: 50, step: 1);
+var stepper = new UTKIntStepper(minValue: 0, maxValue: 100, value: 50, step: 1);
stepper.OnValueChanged += (newValue) => Debug.Log($""Value: {newValue}"");
// Step 설정
-var stepper5 = new UTKNumberStepper(0, 100, 25, 5); // Step 5씩 증감
+var stepper5 = new UTKIntStepper(0, 100, 25, 5); // Step 5씩 증감
// Wrap Around (최소/최대값에서 순환)
-var wrapStepper = new UTKNumberStepper(0, 100, 0, 10);
+var wrapStepper = new UTKIntStepper(0, 100, 0, 10);
wrapStepper.WrapAround = true; // 0 -> -10 시 100으로 순환
// 비활성화
-var disabledStepper = new UTKNumberStepper(0, 100, 30, 1);
+var disabledStepper = new UTKIntStepper(0, 100, 30, 1);
disabledStepper.SetEnabled(false);
// 속성 변경
@@ -218,19 +218,19 @@ stepper.Step = 2;",
-
+
-
+
-
+
-
+
-
+
");
}
diff --git a/Assets/Sample/UIToolkit/UTKStyleGuideSample.cs b/Assets/Sample/UIToolkit/UTKStyleGuideSample.cs
index 4bfa4864..9f27350b 100644
--- a/Assets/Sample/UIToolkit/UTKStyleGuideSample.cs
+++ b/Assets/Sample/UIToolkit/UTKStyleGuideSample.cs
@@ -67,7 +67,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
["UTKVector4Field"] = "UIToolkit/Sample/Input/UTKVector4FieldSample",
["UTKRectField"] = "UIToolkit/Sample/Input/UTKRectFieldSample",
["UTKBoundsField"] = "UIToolkit/Sample/Input/UTKBoundsFieldSample",
- ["UTKNumberStepper"] = "UIToolkit/Sample/Input/UTKNumberStepperSample",
+ ["UTKIntStepper"] = "UIToolkit/Sample/Input/UTKIntStepperSample",
// Slider
["UTKSlider"] = "UIToolkit/Sample/Slider/UTKSliderSample",
["UTKSliderInt"] = "UIToolkit/Sample/Slider/UTKSliderIntSample",
@@ -111,7 +111,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
{
["Icon"] = new[] { "UTKMaterialIcons", "UTKImageIcons", "UTKImage" },
["Button"] = new[] { "UTKButton", "UTKCheckBox", "UTKToggle", "UTKRadioButton", "UTKToggleButtonGroup" },
- ["Input"] = new[] { "UTKInputField", "UTKIntegerField", "UTKLongField", "UTKFloatField", "UTKDoubleField", "UTKVector2Field", "UTKVector3Field", "UTKVector4Field", "UTKRectField", "UTKBoundsField", "UTKNumberStepper" },
+ ["Input"] = new[] { "UTKInputField", "UTKIntegerField", "UTKLongField", "UTKFloatField", "UTKDoubleField", "UTKVector2Field", "UTKVector3Field", "UTKVector4Field", "UTKRectField", "UTKBoundsField", "UTKIntStepper" },
["Slider"] = new[] { "UTKSlider", "UTKSliderInt", "UTKMinMaxSlider", "UTKProgressBar" },
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown", "UTKMultiSelectDropdown" },
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
@@ -448,7 +448,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
case "UTKDoubleField":
InitializeDoubleFieldSample(root);
break;
- case "UTKNumberStepper":
+ case "UTKIntStepper":
InitializeNumberStepperSample(root);
break;
case "UTKVector2Field":
diff --git a/Assets/Scripts/UVC/UIToolkit/CLAUDE.md b/Assets/Scripts/UVC/UIToolkit/CLAUDE.md
index 84d75665..5c2369bc 100644
--- a/Assets/Scripts/UVC/UIToolkit/CLAUDE.md
+++ b/Assets/Scripts/UVC/UIToolkit/CLAUDE.md
@@ -35,7 +35,7 @@ Assets/Scripts/UVC/UIToolkit/
│ ├── UTKInputField.cs # 텍스트 입력
│ ├── UTKIntegerField.cs # Integer 입력
│ ├── UTKLongField.cs # Long 입력
-│ ├── UTKNumberStepper.cs # 숫자 스테퍼 (+/-)
+│ ├── UTKIntStepper.cs # 숫자 스테퍼 (+/-)
│ ├── UTKRectField.cs # Rect 입력
│ ├── UTKVector2Field.cs # Vector2 입력
│ ├── UTKVector3Field.cs # Vector3 입력
@@ -417,12 +417,12 @@ var vector3Field = new UTKVector3Field("위치");
vector3Field.Value = Vector3.zero;
```
-### UTKNumberStepper
+### UTKIntStepper
증가/감소 버튼이 있는 숫자 입력 필드입니다.
```csharp
-var stepper = new UTKNumberStepper();
+var stepper = new UTKIntStepper();
stepper.Value = 5;
stepper.Min = 0;
stepper.Max = 100;
diff --git a/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs b/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs
new file mode 100644
index 00000000..3a4e4adb
--- /dev/null
+++ b/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs
@@ -0,0 +1,481 @@
+#nullable enable
+using System;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace UVC.UIToolkit
+{
+ ///
+ /// 실수 입력 필드에 위/아래 스테퍼 버튼이 붙은 컴포넌트.
+ /// TextInput 오른쪽에 위, 아래 버튼이 세로로 배치됩니다.
+ /// 키보드 화살표, 마우스 휠, 버튼 클릭으로 값을 조절할 수 있습니다.
+ ///
+ ///
+ /// UTKFloatStepper란?
+ /// 실수를 편리하게 증감할 수 있는 입력 컴포넌트입니다.
+ ///
+ /// - 버튼 클릭: ▲/▼ 버튼으로 값 증감
+ /// - 키보드: ↑/↓ 화살표 키로 값 증감
+ /// - 마우스 휠: 호버 상태에서 휠로 값 조절
+ /// - 직접 입력: 텍스트 필드에 숫자 직접 입력
+ ///
+ /// 주요 기능:
+ ///
+ /// - 최소/최대값 제한 (MinValue, MaxValue)
+ /// - 증감 단위 설정 (Step)
+ /// - 순환 모드 (WrapAround) - 최대에서 최소로, 최소에서 최대로
+ ///
+ ///
+ ///
+ /// C# 코드에서 사용:
+ ///
+ /// // 기본 스테퍼 생성
+ /// var stepper = new UTKFloatStepper();
+ /// stepper.MinValue = 0f;
+ /// stepper.MaxValue = 10f;
+ /// stepper.Value = 5.5f;
+ /// stepper.Step = 0.5f; // 0.5씩 증감
+ ///
+ /// // 값 변경 이벤트
+ /// stepper.OnValueChanged += (value) => {
+ /// Debug.Log($"현재 값: {value}");
+ /// };
+ ///
+ /// // 범위와 초기값을 지정하는 생성자
+ /// var volumeStepper = new UTKFloatStepper(0f, 1f, 0.8f, 0.1f); // min, max, initial, step
+ ///
+ /// // 순환 모드 활성화 (10.0 → 0.0, 0.0 → 10.0)
+ /// stepper.WrapAround = true;
+ ///
+ /// // 프로그래밍 방식으로 값 변경
+ /// stepper.Increment(); // Step만큼 증가
+ /// stepper.Decrement(); // Step만큼 감소
+ /// stepper.SetValue(7.5f); // 직접 설정
+ ///
+ /// // 읽기 전용 (사용자가 수정할 수 없음)
+ /// var readOnlyStepper = new UTKFloatStepper(0f, 10f, 5f, 0.1f);
+ /// readOnlyStepper.IsReadOnly = true;
+ ///
+ /// UXML에서 사용:
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ /// 실제 활용 예시 (볼륨 조절):
+ ///
+ /// // 볼륨 스테퍼 (0.0~1.0)
+ /// var volumeStepper = new UTKFloatStepper(0f, 1f, 0.8f, 0.1f);
+ /// volumeStepper.OnValueChanged += (volume) => {
+ /// AudioListener.volume = volume;
+ /// };
+ ///
+ ///
+ [UxmlElement]
+ public partial class UTKFloatStepper : VisualElement, IDisposable
+ {
+ #region Constants
+ private const string USS_PATH = "UIToolkit/Input/UTKFloatStepper";
+ #endregion
+
+ #region UXML Attributes
+ [UxmlAttribute("value")]
+ public float Value
+ {
+ get => _value;
+ set => SetValue(value);
+ }
+
+ [UxmlAttribute("min-value")]
+ public float MinValue
+ {
+ get => _minValue;
+ set
+ {
+ _minValue = value;
+ ClampValue();
+ }
+ }
+
+ [UxmlAttribute("max-value")]
+ public float MaxValue
+ {
+ get => _maxValue;
+ set
+ {
+ _maxValue = value;
+ ClampValue();
+ }
+ }
+
+ [UxmlAttribute("step")]
+ public float Step
+ {
+ get => _step;
+ set => _step = value > 0 ? value : 0.1f;
+ }
+
+ [UxmlAttribute("wrap-around")]
+ public bool WrapAround
+ {
+ get => _wrapAround;
+ set => _wrapAround = value;
+ }
+
+ /// 읽기 전용 상태. true일 때 사용자가 값을 수정할 수 없음
+ [UxmlAttribute("is-readonly")]
+ public bool IsReadOnly
+ {
+ get => _isReadOnly;
+ set
+ {
+ _isReadOnly = value;
+ UpdateReadOnlyState();
+ EnableInClassList("utk-number-stepper--readonly", value);
+ }
+ }
+ #endregion
+
+ #region Fields
+ private bool _disposed;
+ private bool _isReadOnly;
+ private float _value;
+ private float _minValue = float.MinValue;
+ private float _maxValue = float.MaxValue;
+ private float _step = 1.0f;
+ private bool _wrapAround;
+ private bool _isUpdating;
+ private bool _isHovered;
+
+ private TextField? _textField;
+ private Button? _upButton;
+ private Button? _downButton;
+ #endregion
+
+ #region Events
+ /// 값이 변경될 때 발생
+ public event Action? OnValueChanged;
+
+ /// Tab 키가 눌렸을 때 발생 (다음 요소로 포커스 이동용)
+ public event Action? OnTabPressed;
+
+ /// Shift+Tab 키가 눌렸을 때 발생 (이전 요소로 포커스 이동용)
+ public event Action? OnShiftTabPressed;
+ #endregion
+
+ #region Constructor
+
+ public UTKFloatStepper()
+ {
+ UTKThemeManager.Instance.ApplyThemeToElement(this);
+ LoadStyleSheet();
+ CreateUI();
+ SetupEvents();
+ SubscribeToThemeChanges();
+ }
+
+ public UTKFloatStepper(bool isReadOnly = false): this()
+ {
+ _isReadOnly = isReadOnly;
+ }
+
+ public UTKFloatStepper(float minValue, float maxValue, float initialValue = 0f, float step = 0.1f, bool isReadOnly = false): this()
+ {
+ _isReadOnly = isReadOnly;
+ _minValue = minValue;
+ _maxValue = maxValue;
+ _step = step > 0 ? step : 1.0f;
+ _value = Mathf.Clamp(initialValue, minValue, maxValue);
+ }
+
+ private void LoadStyleSheet()
+ {
+ var uss = Resources.Load(USS_PATH);
+ if (uss != null)
+ {
+ styleSheets.Add(uss);
+ }
+ }
+
+ private void SubscribeToThemeChanges()
+ {
+ UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
+
+ // 패널에서 분리될 때 이벤트 구독 해제
+ RegisterCallback(_ =>
+ {
+ UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
+ });
+ }
+
+ private void OnThemeChanged(UTKTheme theme)
+ {
+ UTKThemeManager.Instance.ApplyThemeToElement(this);
+ }
+ #endregion
+
+ #region Public Methods
+ public void SetValue(float newValue, bool notify = true)
+ {
+ float clampedValue = ClampValueInternal(newValue);
+ if (!Mathf.Approximately(_value, clampedValue))
+ {
+ _value = clampedValue;
+ UpdateDisplay();
+ if (notify)
+ {
+ OnValueChanged?.Invoke(_value);
+ }
+ }
+ }
+
+ public void Increment()
+ {
+ if (_wrapAround && _value + _step > _maxValue)
+ {
+ SetValue(_minValue);
+ }
+ else
+ {
+ SetValue(_value + _step);
+ }
+ }
+
+ public void Decrement()
+ {
+ if (_wrapAround && _value - _step < _minValue)
+ {
+ SetValue(_maxValue);
+ }
+ else
+ {
+ SetValue(_value - _step);
+ }
+ }
+
+ public void SetRange(float min, float max)
+ {
+ _minValue = min;
+ _maxValue = max;
+ ClampValue();
+ }
+
+ /// 컴포넌트의 활성화 상태를 설정합니다.
+ public new void SetEnabled(bool enabled)
+ {
+ base.SetEnabled(enabled);
+ EnableInClassList("utk-number-stepper--disabled", !enabled);
+ }
+
+ /// 텍스트 필드에 포커스를 설정합니다.
+ public new void Focus()
+ {
+ _textField?.Focus();
+ }
+ #endregion
+
+ #region Private Methods - UI Creation
+ private void CreateUI()
+ {
+ AddToClassList("utk-number-stepper");
+
+ // Text Field
+ _textField = new TextField { name = "stepper-input" };
+ _textField.AddToClassList("utk-number-stepper__input");
+ _textField.isReadOnly = _isReadOnly;
+
+ // TextField 내부 input 스타일링
+ _textField.RegisterCallback(_ =>
+ {
+ var input = _textField.Q("unity-text-input");
+ if (input != null)
+ {
+ input.AddToClassList("utk-number-stepper__text-input");
+ }
+ });
+
+ Add(_textField);
+
+ // Button Container (위/아래 버튼을 세로로 배치)
+ var buttonContainer = new VisualElement { name = "stepper-buttons" };
+ buttonContainer.AddToClassList("utk-number-stepper__buttons");
+
+ // Up Button
+ _upButton = new Button { name = "stepper-up", text = UTKMaterialIcons.KeyboardArrowUp };
+ _upButton.AddToClassList("utk-number-stepper__btn");
+ _upButton.AddToClassList("utk-number-stepper__btn--up");
+ _upButton.SetEnabled(!_isReadOnly);
+ UTKMaterialIcons.ApplyIconStyle(_upButton, 14);
+ buttonContainer.Add(_upButton);
+
+ // Down Button
+ _downButton = new Button { name = "stepper-down", text = UTKMaterialIcons.KeyboardArrowDown };
+ _downButton.AddToClassList("utk-number-stepper__btn");
+ _downButton.AddToClassList("utk-number-stepper__btn--down");
+ _downButton.SetEnabled(!_isReadOnly);
+ UTKMaterialIcons.ApplyIconStyle(_downButton, 14);
+ buttonContainer.Add(_downButton);
+
+ Add(buttonContainer);
+
+ UpdateDisplay();
+ }
+
+ private void SetupEvents()
+ {
+ _upButton?.RegisterCallback(OnUpButtonClick);
+ _downButton?.RegisterCallback(OnDownButtonClick);
+
+ _textField?.RegisterCallback>(OnTextFieldChanged);
+ _textField?.RegisterCallback(OnTextFieldKeyDown, TrickleDown.TrickleDown);
+
+ RegisterCallback(OnMouseEnter);
+ RegisterCallback(OnMouseLeave);
+ RegisterCallback(OnWheelEvent);
+ }
+ #endregion
+
+ #region Event Handlers
+ private void OnUpButtonClick(ClickEvent evt) => Increment();
+ private void OnDownButtonClick(ClickEvent evt) => Decrement();
+
+ private void OnTextFieldChanged(ChangeEvent evt)
+ {
+ if (_isUpdating) return;
+
+ if (float.TryParse(evt.newValue, out float parsed))
+ {
+ SetValue(parsed);
+ }
+ else
+ {
+ // 유효하지 않은 입력이면 이전 값으로 복원
+ UpdateDisplay();
+ }
+ }
+
+ private void OnTextFieldKeyDown(KeyDownEvent evt)
+ {
+ if (evt.keyCode == KeyCode.UpArrow)
+ {
+ Increment();
+ evt.StopPropagation();
+ }
+ else if (evt.keyCode == KeyCode.DownArrow)
+ {
+ Decrement();
+ evt.StopPropagation();
+ }
+ else if (evt.keyCode == KeyCode.Tab)
+ {
+ if (evt.shiftKey && OnShiftTabPressed != null)
+ {
+ OnShiftTabPressed.Invoke();
+ evt.StopImmediatePropagation();
+ }
+ else if (!evt.shiftKey && OnTabPressed != null)
+ {
+ OnTabPressed.Invoke();
+ evt.StopImmediatePropagation();
+ }
+ }
+ }
+
+ private void OnMouseEnter(MouseEnterEvent evt) => _isHovered = true;
+ private void OnMouseLeave(MouseLeaveEvent evt) => _isHovered = false;
+
+ private void OnWheelEvent(WheelEvent evt)
+ {
+ if (!_isHovered) return;
+
+ if (evt.delta.y < 0)
+ {
+ Increment();
+ }
+ else if (evt.delta.y > 0)
+ {
+ Decrement();
+ }
+ evt.StopPropagation();
+ }
+ #endregion
+
+ #region Private Methods - Logic
+ private void UpdateDisplay()
+ {
+ if (_textField == null) return;
+
+ _isUpdating = true;
+ _textField.value = _value.ToString();//.ToString("F2");
+ _isUpdating = false;
+ }
+
+ private void ClampValue()
+ {
+ SetValue(_value, notify: false);
+ }
+
+ private float ClampValueInternal(float value)
+ {
+ return Mathf.Clamp(value, _minValue, _maxValue);
+ }
+
+ private void UpdateReadOnlyState()
+ {
+ if (_textField != null)
+ {
+ _textField.isReadOnly = _isReadOnly;
+ }
+
+ if (_upButton != null)
+ {
+ _upButton.SetEnabled(!_isReadOnly);
+ }
+
+ if (_downButton != null)
+ {
+ _downButton.SetEnabled(!_isReadOnly);
+ }
+ }
+ #endregion
+
+ #region IDisposable
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+
+ UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
+
+ // 이벤트 콜백 해제
+ _upButton?.UnregisterCallback(OnUpButtonClick);
+ _downButton?.UnregisterCallback(OnDownButtonClick);
+
+ _textField?.UnregisterCallback>(OnTextFieldChanged);
+ _textField?.UnregisterCallback(OnTextFieldKeyDown, TrickleDown.TrickleDown);
+
+ UnregisterCallback(OnMouseEnter);
+ UnregisterCallback(OnMouseLeave);
+ UnregisterCallback(OnWheelEvent);
+
+ // 이벤트 null 처리
+ OnValueChanged = null;
+ OnTabPressed = null;
+ OnShiftTabPressed = null;
+
+ // UI 참조 정리
+ _textField = null;
+ _upButton = null;
+ _downButton = null;
+ }
+ #endregion
+ }
+}
diff --git a/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs.meta b/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs.meta
new file mode 100644
index 00000000..46c9d79f
--- /dev/null
+++ b/Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: ab487b7f159e4cc4680abfe673a31c16
\ No newline at end of file
diff --git a/Assets/Scripts/UVC/UIToolkit/Input/UTKNumberStepper.cs b/Assets/Scripts/UVC/UIToolkit/Input/UTKIntStepper.cs
similarity index 93%
rename from Assets/Scripts/UVC/UIToolkit/Input/UTKNumberStepper.cs
rename to Assets/Scripts/UVC/UIToolkit/Input/UTKIntStepper.cs
index 13c5592e..67806e9d 100644
--- a/Assets/Scripts/UVC/UIToolkit/Input/UTKNumberStepper.cs
+++ b/Assets/Scripts/UVC/UIToolkit/Input/UTKIntStepper.cs
@@ -11,7 +11,7 @@ namespace UVC.UIToolkit
/// 키보드 화살표, 마우스 휠, 버튼 클릭으로 값을 조절할 수 있습니다.
///
///
- /// UTKNumberStepper란?
+ /// UTKIntStepper란?
/// 숫자를 편리하게 증감할 수 있는 입력 컴포넌트입니다.
///
/// - 버튼 클릭: ▲/▼ 버튼으로 값 증감
@@ -30,7 +30,7 @@ namespace UVC.UIToolkit
/// C# 코드에서 사용:
///
/// // 기본 스테퍼 생성
- /// var stepper = new UTKNumberStepper();
+ /// var stepper = new UTKIntStepper();
/// stepper.MinValue = 0;
/// stepper.MaxValue = 100;
/// stepper.Value = 50;
@@ -42,7 +42,7 @@ namespace UVC.UIToolkit
/// };
///
/// // 범위와 초기값을 지정하는 생성자
- /// var volumeStepper = new UTKNumberStepper(0, 100, 80, 10); // min, max, initial, step
+ /// var volumeStepper = new UTKIntStepper(0, 100, 80, 10); // min, max, initial, step
///
/// // 순환 모드 활성화 (100 → 0, 0 → 100)
/// stepper.WrapAround = true;
@@ -53,27 +53,27 @@ namespace UVC.UIToolkit
/// stepper.SetValue(75); // 직접 설정
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
- /// var readOnlyStepper = new UTKNumberStepper(0, 100, 50, 1);
+ /// var readOnlyStepper = new UTKIntStepper(0, 100, 50, 1);
/// readOnlyStepper.IsReadOnly = true;
///
/// UXML에서 사용:
///
- ///
+ ///
///
///
- ///
+ ///
///
///
- ///
+ ///
///
///
- ///
+ ///
/// ]]>
/// 실제 활용 예시 (월 선택기):
///
/// // 월 선택 스테퍼 (1~12 순환)
- /// var monthStepper = new UTKNumberStepper(1, 12, DateTime.Now.Month, 1);
+ /// var monthStepper = new UTKIntStepper(1, 12, DateTime.Now.Month, 1);
/// monthStepper.WrapAround = true; // 12월 다음 1월, 1월 이전 12월
/// monthStepper.OnValueChanged += (month) => {
/// UpdateCalendar(month);
@@ -81,10 +81,10 @@ namespace UVC.UIToolkit
///
///
[UxmlElement]
- public partial class UTKNumberStepper : VisualElement, IDisposable
+ public partial class UTKIntStepper : VisualElement, IDisposable
{
#region Constants
- private const string USS_PATH = "UIToolkit/Input/UTKNumberStepper";
+ private const string USS_PATH = "UIToolkit/Input/UTKIntStepper";
#endregion
#region UXML Attributes
@@ -174,7 +174,7 @@ namespace UVC.UIToolkit
#region Constructor
- public UTKNumberStepper()
+ public UTKIntStepper()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
LoadStyleSheet();
@@ -183,12 +183,12 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
- public UTKNumberStepper(bool isReadOnly = false): this()
+ public UTKIntStepper(bool isReadOnly = false): this()
{
_isReadOnly = isReadOnly;
}
- public UTKNumberStepper(int minValue, int maxValue, int initialValue = 0, int step = 1, bool isReadOnly = false): this()
+ public UTKIntStepper(int minValue, int maxValue, int initialValue = 0, int step = 1, bool isReadOnly = false): this()
{
_isReadOnly = isReadOnly;
_minValue = minValue;
diff --git a/Assets/Scripts/UVC/UIToolkit/Input/UTKNumberStepper.cs.meta b/Assets/Scripts/UVC/UIToolkit/Input/UTKIntStepper.cs.meta
similarity index 100%
rename from Assets/Scripts/UVC/UIToolkit/Input/UTKNumberStepper.cs.meta
rename to Assets/Scripts/UVC/UIToolkit/Input/UTKIntStepper.cs.meta
diff --git a/Assets/Scripts/UVC/UIToolkit/List/UTKPropertyList.cs b/Assets/Scripts/UVC/UIToolkit/List/UTKPropertyList.cs
index 294940ea..85cdf7bf 100644
--- a/Assets/Scripts/UVC/UIToolkit/List/UTKPropertyList.cs
+++ b/Assets/Scripts/UVC/UIToolkit/List/UTKPropertyList.cs
@@ -64,6 +64,9 @@ namespace UVC.UIToolkit
/// 속성 클릭 이벤트
public event Action? OnPropertyClicked;
+
+ /// 버튼 클릭 이벤트 (액션 이름 전달)
+ public event Action? OnPropertyButtonClicked;
#endregion
#region Constructor
@@ -576,6 +579,12 @@ namespace UVC.UIToolkit
{
view.Unbind();
}
+
+ // IDisposable 구현체인 경우 Dispose 호출
+ if (child is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
}
element.Clear();
@@ -601,11 +610,21 @@ namespace UVC.UIToolkit
groupElement.Add(title);
groupElement.Add(count);
- groupElement.RegisterCallback(_ =>
+ // 그룹 클릭 이벤트 - DetachFromPanelEvent에서 자동 정리됨
+ EventCallback clickCallback = null!;
+ clickCallback = _ =>
{
ToggleGroupExpanded(group.GroupId);
expandIcon.SetMaterialIcon(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
OnGroupExpandedChanged?.Invoke(group, group.IsExpanded);
+ };
+
+ groupElement.RegisterCallback(clickCallback);
+
+ // DetachFromPanelEvent에서 이벤트 해제
+ groupElement.RegisterCallback(evt =>
+ {
+ groupElement.UnregisterCallback(clickCallback);
});
container.Add(groupElement);
@@ -616,7 +635,47 @@ namespace UVC.UIToolkit
// View Factory를 사용하여 View 생성 및 바인딩
var itemView = UTKPropertyItemViewFactory.CreateView(item);
- itemView.RegisterCallback(_ => OnPropertyClicked?.Invoke(item));
+ // 클릭 이벤트 등록 - DetachFromPanelEvent에서 자동 정리됨
+ EventCallback clickCallback = _ => OnPropertyClicked?.Invoke(item);
+ itemView.RegisterCallback(clickCallback);
+
+ // 버튼 아이템인 경우 버튼 클릭 이벤트 구독
+ Action? buttonClickHandler = null;
+ if (itemView is UTKButtonItemView buttonView)
+ {
+ buttonClickHandler = (actionName) =>
+ {
+ OnPropertyButtonClicked?.Invoke(item.Id, actionName);
+ };
+ buttonView.OnButtonClicked += buttonClickHandler;
+ }
+
+ // String 아이템에 ActionButton이 있는 경우 이벤트 구독
+ Action? actionButtonClickHandler = null;
+ if (itemView is UTKStringPropertyItemView stringView)
+ {
+ actionButtonClickHandler = (actionName) =>
+ {
+ OnPropertyButtonClicked?.Invoke(item.Id, actionName);
+ };
+ stringView.OnActionButtonClicked += actionButtonClickHandler;
+ }
+
+ // DetachFromPanelEvent에서 이벤트 해제
+ itemView.RegisterCallback(evt =>
+ {
+ itemView.UnregisterCallback(clickCallback);
+
+ if (itemView is UTKButtonItemView btnView && buttonClickHandler != null)
+ {
+ btnView.OnButtonClicked -= buttonClickHandler;
+ }
+
+ if (itemView is UTKStringPropertyItemView strView && actionButtonClickHandler != null)
+ {
+ strView.OnActionButtonClicked -= actionButtonClickHandler;
+ }
+ });
container.Add(itemView);
}
@@ -720,6 +779,7 @@ namespace UVC.UIToolkit
OnPropertyValueChanged = null;
OnGroupExpandedChanged = null;
OnPropertyClicked = null;
+ OnPropertyButtonClicked = null;
// UI 참조 정리
_treeView = null;
diff --git a/Assets/Scripts/UVC/UIToolkit/Modal/UTKDatePicker.cs b/Assets/Scripts/UVC/UIToolkit/Modal/UTKDatePicker.cs
index 80ab4af5..adb58e01 100644
--- a/Assets/Scripts/UVC/UIToolkit/Modal/UTKDatePicker.cs
+++ b/Assets/Scripts/UVC/UIToolkit/Modal/UTKDatePicker.cs
@@ -188,8 +188,8 @@ namespace UVC.UIToolkit
private Button? _nextYearButton;
private VisualElement? _dayNamesRow;
private VisualElement? _timeRow;
- private UTKNumberStepper? _hourStepper;
- private UTKNumberStepper? _minuteStepper;
+ private UTKIntStepper? _hourStepper;
+ private UTKIntStepper? _minuteStepper;
private UTKButton? _cancelButton;
private UTKButton? _confirmButton;
private VisualElement? _header;
@@ -648,14 +648,14 @@ namespace UVC.UIToolkit
var timeLabel = new Label("Time:");
timeLabel.AddToClassList("utk-date-picker__time-label");
- _hourStepper = new UTKNumberStepper(0, 23, 0, 1) { name = "hour-stepper" };
+ _hourStepper = new UTKIntStepper(0, 23, 0, 1) { name = "hour-stepper" };
_hourStepper.AddToClassList("utk-date-picker__time-stepper");
_hourStepper.WrapAround = true;
var colonLabel = new Label(":");
colonLabel.AddToClassList("utk-date-picker__time-separator");
- _minuteStepper = new UTKNumberStepper(0, 59, 0, 1) { name = "minute-stepper" };
+ _minuteStepper = new UTKIntStepper(0, 59, 0, 1) { name = "minute-stepper" };
_minuteStepper.AddToClassList("utk-date-picker__time-stepper");
_minuteStepper.WrapAround = true;
@@ -709,8 +709,8 @@ namespace UVC.UIToolkit
_nextYearButton ??= this.Q