UTKFloatStepper 추가. UTKFloatPropertyItem, UTKFloatPropertyItemView에 추가
This commit is contained in:
@@ -27,7 +27,9 @@
|
|||||||
"Bash(cmd /c \"echo ^<?xml version=\"\"1.0\"\" encoding=\"\"utf-8\"\"?^>^<ui:UXML xmlns:ui=\"\"UnityEngine.UIElements\"\" xmlns:utk=\"\"UVC.UIToolkit\"\"^>^<utk:UTKLabel name=\"\"label\"\" text=\"\"Label\"\" class=\"\"utk-property-item-view__label\"\" /^>^<ui:VisualElement name=\"\"value-container\"\" class=\"\"utk-property-item-view__value\"\"^>^</ui:VisualElement^>^</ui:UXML^>\")",
|
"Bash(cmd /c \"echo ^<?xml version=\"\"1.0\"\" encoding=\"\"utf-8\"\"?^>^<ui:UXML xmlns:ui=\"\"UnityEngine.UIElements\"\" xmlns:utk=\"\"UVC.UIToolkit\"\"^>^<utk:UTKLabel name=\"\"label\"\" text=\"\"Label\"\" class=\"\"utk-property-item-view__label\"\" /^>^<ui:VisualElement name=\"\"value-container\"\" class=\"\"utk-property-item-view__value\"\"^>^</ui:VisualElement^>^</ui:UXML^>\")",
|
||||||
"Bash(del \"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKBoolPropertyItemView.uxml\")",
|
"Bash(del \"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\Property\\\\Views\\\\UTKBoolPropertyItemView.uxml\")",
|
||||||
"Bash(del:*)",
|
"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": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -64,7 +64,6 @@
|
|||||||
font-size: var(--font-size-body2);
|
font-size: var(--font-size-body2);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
-unity-text-align: middle-center;
|
-unity-text-align: middle-center;
|
||||||
margin-top: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
font-size: var(--font-size-body2);
|
font-size: var(--font-size-body2);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
-unity-font-style: normal;
|
-unity-font-style: normal;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
font-size: var(--font-size-body2);
|
font-size: var(--font-size-body2);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
-unity-font-style: normal;
|
-unity-font-style: normal;
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
|
|||||||
156
Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss
Normal file
156
Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss
Normal file
@@ -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);
|
||||||
|
}
|
||||||
11
Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss.meta
Normal file
11
Assets/Resources/UIToolkit/Input/UTKFloatStepper.uss.meta
Normal file
@@ -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
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* ===================================
|
* ===================================
|
||||||
* UTKNumberStepper.uss
|
* UTKIntStepper.uss
|
||||||
* Number Stepper Component Styles
|
* Number Stepper Component Styles
|
||||||
* ===================================
|
* ===================================
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xmlns:utk="UVC.UIToolkit">
|
||||||
|
<ui:VisualElement name="container" class="utk-property-item-view-container">
|
||||||
|
<utk:UTKLabel name="label" text="Label" class="utk-property-item-view__label" />
|
||||||
|
<ui:VisualElement name="value-container" class="utk-property-item-view__value">
|
||||||
|
<utk:UTKButton name="button-field" class="utk-property-item-view__button" />
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:UXML>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d9772db144ccd284ea63c1b71dae6711
|
||||||
|
ScriptedImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: rgba(255, 255, 255, 0.2);
|
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 {
|
.utk-property-item-view--color-state .utk-property-item-view__picker-btn {
|
||||||
|
|||||||
@@ -12,3 +12,9 @@
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#readonly-field {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
@@ -12,3 +12,9 @@
|
|||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#readonly-field {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
@@ -24,14 +24,27 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.utk-property-item-view--float .utk-property-item-view__stepper {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* UseSlider 상태: Slider + Field 모두 표시 */
|
/* UseSlider 상태: Slider + Field 모두 표시 */
|
||||||
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__slider {
|
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__slider {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__field {
|
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__field {
|
||||||
flex-grow: 0;
|
flex-grow: 0.25;
|
||||||
min-width: 60px;
|
}
|
||||||
|
|
||||||
|
.utk-property-item-view--float.utk-property-item-view--slider .utk-property-item-view__stepper {
|
||||||
|
flex-grow: 0.25;
|
||||||
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ReadOnly 상태: Slider 숨김 */
|
/* ReadOnly 상태: Slider 숨김 */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
.utk-property-item-view--int .utk-property-item-view__value {
|
.utk-property-item-view--int .utk-property-item-view__value {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 기본 상태: Slider, Stepper 숨김, Field만 표시 */
|
/* 기본 상태: Slider, Stepper 숨김, Field만 표시 */
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--int .utk-property-item-view__stepper {
|
.utk-property-item-view--int .utk-property-item-view__stepper {
|
||||||
@@ -27,16 +29,17 @@
|
|||||||
.utk-property-item-view--int .utk-property-item-view__field {
|
.utk-property-item-view--int .utk-property-item-view__field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UseSlider 상태: Slider + Field 모두 표시, Stepper 숨김 */
|
/* UseSlider 상태: Slider + Field 모두 표시, Stepper 숨김 */
|
||||||
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__slider {
|
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__slider {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__field {
|
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__field {
|
||||||
flex-grow: 0;
|
flex-grow: 0.25;
|
||||||
min-width: 60px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__stepper {
|
.utk-property-item-view--int.utk-property-item-view--slider .utk-property-item-view__stepper {
|
||||||
@@ -46,6 +49,8 @@
|
|||||||
/* UseStepper 상태: Stepper만 표시, Field/Slider 숨김 */
|
/* UseStepper 상태: Stepper만 표시, Field/Slider 숨김 */
|
||||||
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__stepper {
|
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__stepper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__field {
|
.utk-property-item-view--int.utk-property-item-view--stepper .utk-property-item-view__field {
|
||||||
@@ -59,10 +64,13 @@
|
|||||||
/* UseSlider + UseStepper 상태: Slider + Stepper 모두 표시, Field 숨김 */
|
/* 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 {
|
.utk-property-item-view--int.utk-property-item-view--slider.utk-property-item-view--stepper .utk-property-item-view__slider {
|
||||||
display: flex;
|
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 {
|
.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 {
|
.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 {
|
.utk-property-item-view--int.utk-property-item-view--readonly .utk-property-item-view__field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
<utk:UTKLabel name="label" text="Label" class="utk-property-item-view__label" />
|
<utk:UTKLabel name="label" text="Label" class="utk-property-item-view__label" />
|
||||||
<ui:VisualElement name="value-container" class="utk-property-item-view__value">
|
<ui:VisualElement name="value-container" class="utk-property-item-view__value">
|
||||||
<utk:UTKIntegerField name="min-field" class="utk-property-item-view__field" />
|
<utk:UTKIntegerField name="min-field" class="utk-property-item-view__field" />
|
||||||
<utk:UTKNumberStepper name="min-stepper" class="utk-property-item-view__stepper" />
|
<utk:UTKIntStepper name="min-stepper" class="utk-property-item-view__stepper" />
|
||||||
<utk:UTKLabel text="~" class="utk-property-item-view__range-separator" />
|
<utk:UTKLabel text="~" class="utk-property-item-view__range-separator" />
|
||||||
<utk:UTKIntegerField name="max-field" class="utk-property-item-view__field" />
|
<utk:UTKIntegerField name="max-field" class="utk-property-item-view__field" />
|
||||||
<utk:UTKNumberStepper name="max-stepper" class="utk-property-item-view__stepper" />
|
<utk:UTKIntStepper name="max-stepper" class="utk-property-item-view__stepper" />
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
</ui:VisualElement>
|
</ui:VisualElement>
|
||||||
</ui:UXML>
|
</ui:UXML>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view--int-range .utk-property-item-view__range-separator {
|
.utk-property-item-view--int-range .utk-property-item-view__range-separator {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
|
||||||
|
<ui:VisualElement name="container" class="utk-property-item-view-container">
|
||||||
|
<utk:UTKLabel name="label" text="Label" class="utk-property-item-view__label" />
|
||||||
|
<ui:VisualElement name="value-container" class="utk-property-item-view__value">
|
||||||
|
<utk:UTKMultiSelectDropdown name="multiselect-dropdown-field" class="utk-property-item-view__multiselect-dropdown" />
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:VisualElement>
|
||||||
|
</ui:UXML>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7bd219f87024f5448d8a67d962d52bf
|
||||||
|
ScriptedImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -26,12 +26,13 @@
|
|||||||
border-bottom-color: var(--color-border);
|
border-bottom-color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.utk-property-item-view:hover {
|
/* .utk-property-item-view:hover {
|
||||||
background-color: var(--color-collection-item-hover);
|
background-color: var(--color-collection-item-hover);
|
||||||
}
|
} */
|
||||||
|
|
||||||
.utk-property-item-view TemplateContainer {
|
.utk-property-item-view TemplateContainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
@@ -82,6 +83,8 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
-unity-text-align: middle-left;
|
-unity-text-align: middle-left;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
@@ -92,6 +95,7 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===================================
|
/* ===================================
|
||||||
@@ -344,3 +348,61 @@
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
margin-top: var(--space-xs);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,3 +16,9 @@
|
|||||||
.utk-property-item-view--radio .utk-property-item-view__radio {
|
.utk-property-item-view--radio .utk-property-item-view__radio {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#readonly-field {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
@@ -10,3 +10,18 @@
|
|||||||
.utk-property-item-view--string .utk-property-item-view__field {
|
.utk-property-item-view--string .utk-property-item-view__field {
|
||||||
flex-grow: 1;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* ===================================
|
* ===================================
|
||||||
* UTKNumberStepperSample.uss
|
* UTKIntStepperSample.uss
|
||||||
* Number Stepper Sample Specific Styles
|
* Number Stepper Sample Specific Styles
|
||||||
* ===================================
|
* ===================================
|
||||||
*/
|
*/
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
|
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
|
||||||
<Style src="../UTKSampleCommon.uss" />
|
<Style src="../UTKSampleCommon.uss" />
|
||||||
<Style src="UTKNumberStepperSample.uss" />
|
<Style src="UTKIntStepperSample.uss" />
|
||||||
|
|
||||||
<VisualElement class="utk-sample-container">
|
<VisualElement class="utk-sample-container">
|
||||||
<Label class="utk-sample-desc" text="숫자 스테퍼 컴포넌트 (증감 버튼 포함)" />
|
<Label class="utk-sample-desc" text="숫자 스테퍼 컴포넌트 (증감 버튼 포함)" />
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<VisualElement class="utk-sample-section">
|
<VisualElement class="utk-sample-section">
|
||||||
<Label class="utk-sample-section__title" text="Step 1" />
|
<Label class="utk-sample-section__title" text="Step 1" />
|
||||||
<VisualElement class="utk-sample-row">
|
<VisualElement class="utk-sample-row">
|
||||||
<utk:UTKNumberStepper name="stepper-step1" min-value="0" max-value="100" value="50" step="1" class="utk-sample-stepper" />
|
<utk:UTKIntStepper name="stepper-step1" min-value="0" max-value="100" value="50" step="1" class="utk-sample-stepper" />
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<VisualElement class="utk-sample-section">
|
<VisualElement class="utk-sample-section">
|
||||||
<Label class="utk-sample-section__title" text="Step 5" />
|
<Label class="utk-sample-section__title" text="Step 5" />
|
||||||
<VisualElement class="utk-sample-row">
|
<VisualElement class="utk-sample-row">
|
||||||
<utk:UTKNumberStepper name="stepper-step5" min-value="0" max-value="100" value="25" step="5" class="utk-sample-stepper" />
|
<utk:UTKIntStepper name="stepper-step5" min-value="0" max-value="100" value="25" step="5" class="utk-sample-stepper" />
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<VisualElement class="utk-sample-section">
|
<VisualElement class="utk-sample-section">
|
||||||
<Label class="utk-sample-section__title" text="Wrap Around" />
|
<Label class="utk-sample-section__title" text="Wrap Around" />
|
||||||
<VisualElement class="utk-sample-row">
|
<VisualElement class="utk-sample-row">
|
||||||
<utk:UTKNumberStepper name="stepper-wrap" min-value="0" max-value="100" value="0" step="10" wrap-around="true" class="utk-sample-stepper" />
|
<utk:UTKIntStepper name="stepper-wrap" min-value="0" max-value="100" value="0" step="10" wrap-around="true" class="utk-sample-stepper" />
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
</VisualElement>
|
</VisualElement>
|
||||||
|
|
||||||
@@ -261,8 +261,9 @@ Textfield 항목 스타일
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
width: auto;
|
width: auto;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Medium');
|
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Medium');
|
||||||
/* --unity-selection-color: rgba(54, 98, 160, 0.651); 선택 색상 */
|
/* --unity-selection-color: rgba(54, 98, 160, 0.651); 선택 색상 */
|
||||||
--unity-cursor-color: var(--color-base-01); /* 캐럿(커서) 색상 흰색 */
|
--unity-cursor-color: var(--color-base-01); /* 캐럿(커서) 색상 흰색 */
|
||||||
|
|||||||
@@ -82,6 +82,16 @@ namespace UVC.Sample.UIToolkit
|
|||||||
Debug.Log($"Property Changed: {args.PropertyId} {args.PropertyName} ({args.PropertyType}) = {args.NewValue}");
|
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();
|
var entries = CreateSampleEntries();
|
||||||
_propertyWindow.LoadMixedProperties(entries);
|
_propertyWindow.LoadMixedProperties(entries);
|
||||||
@@ -94,17 +104,41 @@ namespace UVC.Sample.UIToolkit
|
|||||||
var entries = new List<IUTKPropertyEntry>();
|
var entries = new List<IUTKPropertyEntry>();
|
||||||
|
|
||||||
// ===== Group에 속하지 않은 개별 아이템들 =====
|
// ===== Group에 속하지 않은 개별 아이템들 =====
|
||||||
|
|
||||||
// String (편집 가능)
|
// String (편집 가능)
|
||||||
entries.Add(new UTKStringPropertyItem("string", "String", "Editable text"));
|
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 (읽기 전용)
|
// String (읽기 전용)
|
||||||
var roString = new UTKStringPropertyItem("string_ro", "String (RO)", "Read-only text", isReadOnly: true);
|
var roString = new UTKStringPropertyItem("string_ro", "String (RO)", "Read-only text", isReadOnly: true);
|
||||||
entries.Add(roString);
|
entries.Add(roString);
|
||||||
|
|
||||||
// Bool (편집 가능)
|
// Bool (편집 가능)
|
||||||
entries.Add(new UTKBoolPropertyItem("bool", "Bool", true));
|
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 (읽기 전용)
|
// Bool (읽기 전용)
|
||||||
var roBool = new UTKBoolPropertyItem("bool_ro", "Bool (RO)", true);
|
var roBool = new UTKBoolPropertyItem("bool_ro", "Bool (RO)", true);
|
||||||
roBool.IsReadOnly = true;
|
roBool.IsReadOnly = true;
|
||||||
@@ -112,7 +146,12 @@ namespace UVC.Sample.UIToolkit
|
|||||||
|
|
||||||
// Int (편집 가능)
|
// Int (편집 가능)
|
||||||
entries.Add(new UTKIntPropertyItem("int", "Int", 42, 0, 100));
|
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 (읽기 전용)
|
// Int (읽기 전용)
|
||||||
entries.Add(new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, isReadOnly: true));
|
entries.Add(new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, isReadOnly: true));
|
||||||
|
|
||||||
@@ -134,13 +173,24 @@ namespace UVC.Sample.UIToolkit
|
|||||||
// Int stepper (읽기 전용)
|
// Int stepper (읽기 전용)
|
||||||
entries.Add(new UTKIntPropertyItem("int3_ro", "Int3 (RO)", 99, 0, 100, true, true, isReadOnly: true));
|
entries.Add(new UTKIntPropertyItem("int3_ro", "Int3 (RO)", 99, 0, 100, true, true, isReadOnly: true));
|
||||||
|
|
||||||
// Float (편집 가능)
|
// Float (useSlider=true, useStepper=true)
|
||||||
entries.Add(new UTKFloatPropertyItem("float", "Float", 3.14f, 0f, 10f, true));
|
entries.Add(new UTKFloatPropertyItem("float1", "Float (Slider+Stepper)", 3.14f, 0f, 10f, useSlider: true, useStepper: true));
|
||||||
|
|
||||||
// Float (읽기 전용)
|
// Float (useSlider=true, useStepper=false)
|
||||||
var roFloat = new UTKFloatPropertyItem("float_ro", "Float (RO)", 2.71f, 0f, 10f, true);
|
entries.Add(new UTKFloatPropertyItem("float2", "Float (Slider)", 5.0f, 0f, 10f, useSlider: true, useStepper: false));
|
||||||
roFloat.IsReadOnly = true;
|
|
||||||
entries.Add(roFloat);
|
// 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 (편집 가능)
|
// Vector2 (편집 가능)
|
||||||
entries.Add(new UTKVector2PropertyItem("vec2", "Vector2", new Vector2(1, 2)));
|
entries.Add(new UTKVector2PropertyItem("vec2", "Vector2", new Vector2(1, 2)));
|
||||||
@@ -217,15 +267,27 @@ namespace UVC.Sample.UIToolkit
|
|||||||
entries.Add(roEnum);
|
entries.Add(roEnum);
|
||||||
|
|
||||||
// Dropdown (편집 가능)
|
// Dropdown (편집 가능)
|
||||||
entries.Add(new UTKDropdownPropertyItem("dropdown", "Dropdown",
|
entries.Add(new UTKDropdownPropertyItem("dropdown", "Dropdown",
|
||||||
new List<string> { "Option A", "Option B", "Option C" }, "Option A"));
|
new List<string> { "Option A", "Option B", "Option C" }, "Option A"));
|
||||||
|
|
||||||
// Dropdown (읽기 전용)
|
// Dropdown (읽기 전용)
|
||||||
var roDropdown = new UTKDropdownPropertyItem("dropdown_ro", "Dropdown (RO)",
|
var roDropdown = new UTKDropdownPropertyItem("dropdown_ro", "Dropdown (RO)",
|
||||||
new List<string> { "Option X", "Option Y", "Option Z" }, "Option Y");
|
new List<string> { "Option X", "Option Y", "Option Z" }, "Option Y");
|
||||||
roDropdown.IsReadOnly = true;
|
roDropdown.IsReadOnly = true;
|
||||||
entries.Add(roDropdown);
|
entries.Add(roDropdown);
|
||||||
|
|
||||||
|
// MultiSelectDropdown (편집 가능)
|
||||||
|
entries.Add(new UTKMultiSelectDropdownPropertyItem("multiselect", "MultiSelect",
|
||||||
|
new List<string> { "Tag1", "Tag2", "Tag3", "Tag4" },
|
||||||
|
new List<string> { "Tag1", "Tag3" }));
|
||||||
|
|
||||||
|
// MultiSelectDropdown (읽기 전용)
|
||||||
|
var roMultiSelect = new UTKMultiSelectDropdownPropertyItem("multiselect_ro", "MultiSelect (RO)",
|
||||||
|
new List<string> { "Feature A", "Feature B", "Feature C", "Feature D" },
|
||||||
|
new List<string> { "Feature B", "Feature C" });
|
||||||
|
roMultiSelect.IsReadOnly = true;
|
||||||
|
entries.Add(roMultiSelect);
|
||||||
|
|
||||||
// Radio (편집 가능)
|
// Radio (편집 가능)
|
||||||
entries.Add(new UTKRadioPropertyItem("radio", "Radio",
|
entries.Add(new UTKRadioPropertyItem("radio", "Radio",
|
||||||
new List<string> { "Choice 1", "Choice 2", "Choice 3" }, 0));
|
new List<string> { "Choice 1", "Choice 2", "Choice 3" }, 0));
|
||||||
@@ -261,6 +323,15 @@ namespace UVC.Sample.UIToolkit
|
|||||||
// 기본 속성 그룹 (편집 가능)
|
// 기본 속성 그룹 (편집 가능)
|
||||||
var basicGroup = new UTKPropertyGroup("basic", "Basic Properties (Editable)");
|
var basicGroup = new UTKPropertyGroup("basic", "Basic Properties (Editable)");
|
||||||
basicGroup.AddItem(new UTKStringPropertyItem("name", "Name", "Sample Object"));
|
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 UTKStringPropertyItem("description", "Description", "This is a sample object") { IsMultiline = true });
|
||||||
basicGroup.AddItem(new UTKBoolPropertyItem("active", "Is Active", true));
|
basicGroup.AddItem(new UTKBoolPropertyItem("active", "Is Active", true));
|
||||||
basicGroup.AddItem(new UTKIntPropertyItem("count", "Count", 10, 0, 100, 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 UTKEnumPropertyItem("layer", "Layer", SampleLayer.Default));
|
||||||
selectionGroup.AddItem(new UTKDropdownPropertyItem("tag", "Tag",
|
selectionGroup.AddItem(new UTKDropdownPropertyItem("tag", "Tag",
|
||||||
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Player"));
|
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Player"));
|
||||||
|
selectionGroup.AddItem(new UTKMultiSelectDropdownPropertyItem("categories", "Categories",
|
||||||
|
new List<string> { "Physics", "Rendering", "Audio", "UI", "Network" },
|
||||||
|
new List<string> { "Physics", "Rendering" }));
|
||||||
selectionGroup.AddItem(new UTKRadioPropertyItem("quality", "Quality",
|
selectionGroup.AddItem(new UTKRadioPropertyItem("quality", "Quality",
|
||||||
new List<string> { "Low", "Medium", "High", "Ultra" }, 2));
|
new List<string> { "Low", "Medium", "High", "Ultra" }, 2));
|
||||||
entries.Add(selectionGroup);
|
entries.Add(selectionGroup);
|
||||||
@@ -376,6 +450,11 @@ namespace UVC.Sample.UIToolkit
|
|||||||
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Enemy");
|
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Enemy");
|
||||||
roTag.IsReadOnly = true;
|
roTag.IsReadOnly = true;
|
||||||
selectionGroupRO.AddItem(roTag);
|
selectionGroupRO.AddItem(roTag);
|
||||||
|
var roCategories = new UTKMultiSelectDropdownPropertyItem("categories_ro", "Categories",
|
||||||
|
new List<string> { "Physics", "Rendering", "Audio", "UI", "Network" },
|
||||||
|
new List<string> { "Audio", "UI", "Network" });
|
||||||
|
roCategories.IsReadOnly = true;
|
||||||
|
selectionGroupRO.AddItem(roCategories);
|
||||||
var roQuality = new UTKRadioPropertyItem("quality_ro", "Quality",
|
var roQuality = new UTKRadioPropertyItem("quality_ro", "Quality",
|
||||||
new List<string> { "Low", "Medium", "High", "Ultra" }, 3);
|
new List<string> { "Low", "Medium", "High", "Ultra" }, 3);
|
||||||
roQuality.IsReadOnly = true;
|
roQuality.IsReadOnly = true;
|
||||||
@@ -398,6 +477,75 @@ namespace UVC.Sample.UIToolkit
|
|||||||
rangeGroupRO.AddItem(roDamageRange);
|
rangeGroupRO.AddItem(roDamageRange);
|
||||||
entries.Add(rangeGroupRO);
|
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;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ doubleField.IsEnabled = false;",
|
|||||||
var disabledRow = root.Q<VisualElement>("stepper-disabled-row");
|
var disabledRow = root.Q<VisualElement>("stepper-disabled-row");
|
||||||
if (disabledRow != null)
|
if (disabledRow != null)
|
||||||
{
|
{
|
||||||
var stepper = new UTKNumberStepper(0, 100, 30, 1);
|
var stepper = new UTKIntStepper(0, 100, 30, 1);
|
||||||
stepper.style.width = 150;
|
stepper.style.width = 150;
|
||||||
stepper.SetEnabled(false);
|
stepper.SetEnabled(false);
|
||||||
disabledRow.Add(stepper);
|
disabledRow.Add(stepper);
|
||||||
@@ -195,18 +195,18 @@ doubleField.IsEnabled = false;",
|
|||||||
|
|
||||||
SetCodeSamples(root,
|
SetCodeSamples(root,
|
||||||
csharpCode: @"// 기본 사용법
|
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}"");
|
stepper.OnValueChanged += (newValue) => Debug.Log($""Value: {newValue}"");
|
||||||
|
|
||||||
// Step 설정
|
// Step 설정
|
||||||
var stepper5 = new UTKNumberStepper(0, 100, 25, 5); // Step 5씩 증감
|
var stepper5 = new UTKIntStepper(0, 100, 25, 5); // Step 5씩 증감
|
||||||
|
|
||||||
// Wrap Around (최소/최대값에서 순환)
|
// Wrap Around (최소/최대값에서 순환)
|
||||||
var wrapStepper = new UTKNumberStepper(0, 100, 0, 10);
|
var wrapStepper = new UTKIntStepper(0, 100, 0, 10);
|
||||||
wrapStepper.WrapAround = true; // 0 -> -10 시 100으로 순환
|
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);
|
disabledStepper.SetEnabled(false);
|
||||||
|
|
||||||
// 속성 변경
|
// 속성 변경
|
||||||
@@ -218,19 +218,19 @@ stepper.Step = 2;",
|
|||||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||||
|
|
||||||
<!-- Step 1 -->
|
<!-- Step 1 -->
|
||||||
<utk:UTKNumberStepper min-value=""0"" max-value=""100"" value=""50"" step=""1"" />
|
<utk:UTKIntStepper min-value=""0"" max-value=""100"" value=""50"" step=""1"" />
|
||||||
|
|
||||||
<!-- Step 5 -->
|
<!-- Step 5 -->
|
||||||
<utk:UTKNumberStepper min-value=""0"" max-value=""100"" value=""25"" step=""5"" />
|
<utk:UTKIntStepper min-value=""0"" max-value=""100"" value=""25"" step=""5"" />
|
||||||
|
|
||||||
<!-- Wrap Around -->
|
<!-- Wrap Around -->
|
||||||
<utk:UTKNumberStepper min-value=""0"" max-value=""100"" value=""0"" step=""10"" wrap-around=""true"" />
|
<utk:UTKIntStepper min-value=""0"" max-value=""100"" value=""0"" step=""10"" wrap-around=""true"" />
|
||||||
|
|
||||||
<!-- 비활성화 -->
|
<!-- 비활성화 -->
|
||||||
<utk:UTKNumberStepper min-value=""0"" max-value=""100"" value=""30"" step=""1"" is-enabled=""false"" />
|
<utk:UTKIntStepper min-value=""0"" max-value=""100"" value=""30"" step=""1"" is-enabled=""false"" />
|
||||||
|
|
||||||
<!-- 커스텀 스타일 -->
|
<!-- 커스텀 스타일 -->
|
||||||
<utk:UTKNumberStepper class=""custom-stepper"" style=""width: 150px;"" />
|
<utk:UTKIntStepper class=""custom-stepper"" style=""width: 150px;"" />
|
||||||
|
|
||||||
</ui:UXML>");
|
</ui:UXML>");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
|||||||
["UTKVector4Field"] = "UIToolkit/Sample/Input/UTKVector4FieldSample",
|
["UTKVector4Field"] = "UIToolkit/Sample/Input/UTKVector4FieldSample",
|
||||||
["UTKRectField"] = "UIToolkit/Sample/Input/UTKRectFieldSample",
|
["UTKRectField"] = "UIToolkit/Sample/Input/UTKRectFieldSample",
|
||||||
["UTKBoundsField"] = "UIToolkit/Sample/Input/UTKBoundsFieldSample",
|
["UTKBoundsField"] = "UIToolkit/Sample/Input/UTKBoundsFieldSample",
|
||||||
["UTKNumberStepper"] = "UIToolkit/Sample/Input/UTKNumberStepperSample",
|
["UTKIntStepper"] = "UIToolkit/Sample/Input/UTKIntStepperSample",
|
||||||
// Slider
|
// Slider
|
||||||
["UTKSlider"] = "UIToolkit/Sample/Slider/UTKSliderSample",
|
["UTKSlider"] = "UIToolkit/Sample/Slider/UTKSliderSample",
|
||||||
["UTKSliderInt"] = "UIToolkit/Sample/Slider/UTKSliderIntSample",
|
["UTKSliderInt"] = "UIToolkit/Sample/Slider/UTKSliderIntSample",
|
||||||
@@ -111,7 +111,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
|||||||
{
|
{
|
||||||
["Icon"] = new[] { "UTKMaterialIcons", "UTKImageIcons", "UTKImage" },
|
["Icon"] = new[] { "UTKMaterialIcons", "UTKImageIcons", "UTKImage" },
|
||||||
["Button"] = new[] { "UTKButton", "UTKCheckBox", "UTKToggle", "UTKRadioButton", "UTKToggleButtonGroup" },
|
["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" },
|
["Slider"] = new[] { "UTKSlider", "UTKSliderInt", "UTKMinMaxSlider", "UTKProgressBar" },
|
||||||
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown", "UTKMultiSelectDropdown" },
|
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown", "UTKMultiSelectDropdown" },
|
||||||
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
|
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
|
||||||
@@ -448,7 +448,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
|||||||
case "UTKDoubleField":
|
case "UTKDoubleField":
|
||||||
InitializeDoubleFieldSample(root);
|
InitializeDoubleFieldSample(root);
|
||||||
break;
|
break;
|
||||||
case "UTKNumberStepper":
|
case "UTKIntStepper":
|
||||||
InitializeNumberStepperSample(root);
|
InitializeNumberStepperSample(root);
|
||||||
break;
|
break;
|
||||||
case "UTKVector2Field":
|
case "UTKVector2Field":
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ Assets/Scripts/UVC/UIToolkit/
|
|||||||
│ ├── UTKInputField.cs # 텍스트 입력
|
│ ├── UTKInputField.cs # 텍스트 입력
|
||||||
│ ├── UTKIntegerField.cs # Integer 입력
|
│ ├── UTKIntegerField.cs # Integer 입력
|
||||||
│ ├── UTKLongField.cs # Long 입력
|
│ ├── UTKLongField.cs # Long 입력
|
||||||
│ ├── UTKNumberStepper.cs # 숫자 스테퍼 (+/-)
|
│ ├── UTKIntStepper.cs # 숫자 스테퍼 (+/-)
|
||||||
│ ├── UTKRectField.cs # Rect 입력
|
│ ├── UTKRectField.cs # Rect 입력
|
||||||
│ ├── UTKVector2Field.cs # Vector2 입력
|
│ ├── UTKVector2Field.cs # Vector2 입력
|
||||||
│ ├── UTKVector3Field.cs # Vector3 입력
|
│ ├── UTKVector3Field.cs # Vector3 입력
|
||||||
@@ -417,12 +417,12 @@ var vector3Field = new UTKVector3Field("위치");
|
|||||||
vector3Field.Value = Vector3.zero;
|
vector3Field.Value = Vector3.zero;
|
||||||
```
|
```
|
||||||
|
|
||||||
### UTKNumberStepper
|
### UTKIntStepper
|
||||||
|
|
||||||
증가/감소 버튼이 있는 숫자 입력 필드입니다.
|
증가/감소 버튼이 있는 숫자 입력 필드입니다.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var stepper = new UTKNumberStepper();
|
var stepper = new UTKIntStepper();
|
||||||
stepper.Value = 5;
|
stepper.Value = 5;
|
||||||
stepper.Min = 0;
|
stepper.Min = 0;
|
||||||
stepper.Max = 100;
|
stepper.Max = 100;
|
||||||
|
|||||||
481
Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs
Normal file
481
Assets/Scripts/UVC/UIToolkit/Input/UTKFloatStepper.cs
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace UVC.UIToolkit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 실수 입력 필드에 위/아래 스테퍼 버튼이 붙은 컴포넌트.
|
||||||
|
/// TextInput 오른쪽에 위, 아래 버튼이 세로로 배치됩니다.
|
||||||
|
/// 키보드 화살표, 마우스 휠, 버튼 클릭으로 값을 조절할 수 있습니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para><b>UTKFloatStepper란?</b></para>
|
||||||
|
/// <para>실수를 편리하게 증감할 수 있는 입력 컴포넌트입니다.</para>
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item><description><b>버튼 클릭</b>: ▲/▼ 버튼으로 값 증감</description></item>
|
||||||
|
/// <item><description><b>키보드</b>: ↑/↓ 화살표 키로 값 증감</description></item>
|
||||||
|
/// <item><description><b>마우스 휠</b>: 호버 상태에서 휠로 값 조절</description></item>
|
||||||
|
/// <item><description><b>직접 입력</b>: 텍스트 필드에 숫자 직접 입력</description></item>
|
||||||
|
/// </list>
|
||||||
|
/// <para><b>주요 기능:</b></para>
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item><description>최소/최대값 제한 (MinValue, MaxValue)</description></item>
|
||||||
|
/// <item><description>증감 단위 설정 (Step)</description></item>
|
||||||
|
/// <item><description>순환 모드 (WrapAround) - 최대에서 최소로, 최소에서 최대로</description></item>
|
||||||
|
/// </list>
|
||||||
|
/// </remarks>
|
||||||
|
/// <example>
|
||||||
|
/// <para><b>C# 코드에서 사용:</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// // 기본 스테퍼 생성
|
||||||
|
/// 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;
|
||||||
|
/// </code>
|
||||||
|
/// <para><b>UXML에서 사용:</b></para>
|
||||||
|
/// <code><![CDATA[
|
||||||
|
/// <!-- 기본 스테퍼 -->
|
||||||
|
/// <utk:UTKFloatStepper value="5.5" min-value="0" max-value="10" />
|
||||||
|
///
|
||||||
|
/// <!-- 증감 단위 설정 -->
|
||||||
|
/// <utk:UTKFloatStepper value="1.0" step="0.5" />
|
||||||
|
///
|
||||||
|
/// <!-- 순환 모드 -->
|
||||||
|
/// <utk:UTKFloatStepper value="0.5" min-value="0" max-value="1" wrap-around="true" />
|
||||||
|
///
|
||||||
|
/// <!-- 읽기 전용 -->
|
||||||
|
/// <utk:UTKFloatStepper value="5.0" is-readonly="true" />
|
||||||
|
/// ]]></code>
|
||||||
|
/// <para><b>실제 활용 예시 (볼륨 조절):</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// // 볼륨 스테퍼 (0.0~1.0)
|
||||||
|
/// var volumeStepper = new UTKFloatStepper(0f, 1f, 0.8f, 0.1f);
|
||||||
|
/// volumeStepper.OnValueChanged += (volume) => {
|
||||||
|
/// AudioListener.volume = volume;
|
||||||
|
/// };
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>읽기 전용 상태. true일 때 사용자가 값을 수정할 수 없음</summary>
|
||||||
|
[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
|
||||||
|
/// <summary>값이 변경될 때 발생</summary>
|
||||||
|
public event Action<float>? OnValueChanged;
|
||||||
|
|
||||||
|
/// <summary>Tab 키가 눌렸을 때 발생 (다음 요소로 포커스 이동용)</summary>
|
||||||
|
public event Action? OnTabPressed;
|
||||||
|
|
||||||
|
/// <summary>Shift+Tab 키가 눌렸을 때 발생 (이전 요소로 포커스 이동용)</summary>
|
||||||
|
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<StyleSheet>(USS_PATH);
|
||||||
|
if (uss != null)
|
||||||
|
{
|
||||||
|
styleSheets.Add(uss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubscribeToThemeChanges()
|
||||||
|
{
|
||||||
|
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||||
|
|
||||||
|
// 패널에서 분리될 때 이벤트 구독 해제
|
||||||
|
RegisterCallback<DetachFromPanelEvent>(_ =>
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>컴포넌트의 활성화 상태를 설정합니다.</summary>
|
||||||
|
public new void SetEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
base.SetEnabled(enabled);
|
||||||
|
EnableInClassList("utk-number-stepper--disabled", !enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>텍스트 필드에 포커스를 설정합니다.</summary>
|
||||||
|
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<AttachToPanelEvent>(_ =>
|
||||||
|
{
|
||||||
|
var input = _textField.Q<VisualElement>("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<ClickEvent>(OnUpButtonClick);
|
||||||
|
_downButton?.RegisterCallback<ClickEvent>(OnDownButtonClick);
|
||||||
|
|
||||||
|
_textField?.RegisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||||
|
_textField?.RegisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||||
|
|
||||||
|
RegisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||||
|
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||||
|
RegisterCallback<WheelEvent>(OnWheelEvent);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
private void OnUpButtonClick(ClickEvent evt) => Increment();
|
||||||
|
private void OnDownButtonClick(ClickEvent evt) => Decrement();
|
||||||
|
|
||||||
|
private void OnTextFieldChanged(ChangeEvent<string> 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<ClickEvent>(OnUpButtonClick);
|
||||||
|
_downButton?.UnregisterCallback<ClickEvent>(OnDownButtonClick);
|
||||||
|
|
||||||
|
_textField?.UnregisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||||
|
_textField?.UnregisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||||
|
|
||||||
|
UnregisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||||
|
UnregisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||||
|
UnregisterCallback<WheelEvent>(OnWheelEvent);
|
||||||
|
|
||||||
|
// 이벤트 null 처리
|
||||||
|
OnValueChanged = null;
|
||||||
|
OnTabPressed = null;
|
||||||
|
OnShiftTabPressed = null;
|
||||||
|
|
||||||
|
// UI 참조 정리
|
||||||
|
_textField = null;
|
||||||
|
_upButton = null;
|
||||||
|
_downButton = null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ab487b7f159e4cc4680abfe673a31c16
|
||||||
@@ -11,7 +11,7 @@ namespace UVC.UIToolkit
|
|||||||
/// 키보드 화살표, 마우스 휠, 버튼 클릭으로 값을 조절할 수 있습니다.
|
/// 키보드 화살표, 마우스 휠, 버튼 클릭으로 값을 조절할 수 있습니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para><b>UTKNumberStepper란?</b></para>
|
/// <para><b>UTKIntStepper란?</b></para>
|
||||||
/// <para>숫자를 편리하게 증감할 수 있는 입력 컴포넌트입니다.</para>
|
/// <para>숫자를 편리하게 증감할 수 있는 입력 컴포넌트입니다.</para>
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
/// <item><description><b>버튼 클릭</b>: ▲/▼ 버튼으로 값 증감</description></item>
|
/// <item><description><b>버튼 클릭</b>: ▲/▼ 버튼으로 값 증감</description></item>
|
||||||
@@ -30,7 +30,7 @@ namespace UVC.UIToolkit
|
|||||||
/// <para><b>C# 코드에서 사용:</b></para>
|
/// <para><b>C# 코드에서 사용:</b></para>
|
||||||
/// <code>
|
/// <code>
|
||||||
/// // 기본 스테퍼 생성
|
/// // 기본 스테퍼 생성
|
||||||
/// var stepper = new UTKNumberStepper();
|
/// var stepper = new UTKIntStepper();
|
||||||
/// stepper.MinValue = 0;
|
/// stepper.MinValue = 0;
|
||||||
/// stepper.MaxValue = 100;
|
/// stepper.MaxValue = 100;
|
||||||
/// stepper.Value = 50;
|
/// 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)
|
/// // 순환 모드 활성화 (100 → 0, 0 → 100)
|
||||||
/// stepper.WrapAround = true;
|
/// stepper.WrapAround = true;
|
||||||
@@ -53,27 +53,27 @@ namespace UVC.UIToolkit
|
|||||||
/// stepper.SetValue(75); // 직접 설정
|
/// stepper.SetValue(75); // 직접 설정
|
||||||
///
|
///
|
||||||
/// // 읽기 전용 (사용자가 수정할 수 없음)
|
/// // 읽기 전용 (사용자가 수정할 수 없음)
|
||||||
/// var readOnlyStepper = new UTKNumberStepper(0, 100, 50, 1);
|
/// var readOnlyStepper = new UTKIntStepper(0, 100, 50, 1);
|
||||||
/// readOnlyStepper.IsReadOnly = true;
|
/// readOnlyStepper.IsReadOnly = true;
|
||||||
/// </code>
|
/// </code>
|
||||||
/// <para><b>UXML에서 사용:</b></para>
|
/// <para><b>UXML에서 사용:</b></para>
|
||||||
/// <code><![CDATA[
|
/// <code><![CDATA[
|
||||||
/// <!-- 기본 스테퍼 -->
|
/// <!-- 기본 스테퍼 -->
|
||||||
/// <utk:UTKNumberStepper value="50" min-value="0" max-value="100" />
|
/// <utk:UTKIntStepper value="50" min-value="0" max-value="100" />
|
||||||
///
|
///
|
||||||
/// <!-- 증감 단위 설정 -->
|
/// <!-- 증감 단위 설정 -->
|
||||||
/// <utk:UTKNumberStepper value="10" step="5" />
|
/// <utk:UTKIntStepper value="10" step="5" />
|
||||||
///
|
///
|
||||||
/// <!-- 순환 모드 -->
|
/// <!-- 순환 모드 -->
|
||||||
/// <utk:UTKNumberStepper value="1" min-value="1" max-value="12" wrap-around="true" />
|
/// <utk:UTKIntStepper value="1" min-value="1" max-value="12" wrap-around="true" />
|
||||||
///
|
///
|
||||||
/// <!-- 읽기 전용 -->
|
/// <!-- 읽기 전용 -->
|
||||||
/// <utk:UTKNumberStepper value="50" is-readonly="true" />
|
/// <utk:UTKIntStepper value="50" is-readonly="true" />
|
||||||
/// ]]></code>
|
/// ]]></code>
|
||||||
/// <para><b>실제 활용 예시 (월 선택기):</b></para>
|
/// <para><b>실제 활용 예시 (월 선택기):</b></para>
|
||||||
/// <code>
|
/// <code>
|
||||||
/// // 월 선택 스테퍼 (1~12 순환)
|
/// // 월 선택 스테퍼 (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.WrapAround = true; // 12월 다음 1월, 1월 이전 12월
|
||||||
/// monthStepper.OnValueChanged += (month) => {
|
/// monthStepper.OnValueChanged += (month) => {
|
||||||
/// UpdateCalendar(month);
|
/// UpdateCalendar(month);
|
||||||
@@ -81,10 +81,10 @@ namespace UVC.UIToolkit
|
|||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
[UxmlElement]
|
[UxmlElement]
|
||||||
public partial class UTKNumberStepper : VisualElement, IDisposable
|
public partial class UTKIntStepper : VisualElement, IDisposable
|
||||||
{
|
{
|
||||||
#region Constants
|
#region Constants
|
||||||
private const string USS_PATH = "UIToolkit/Input/UTKNumberStepper";
|
private const string USS_PATH = "UIToolkit/Input/UTKIntStepper";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region UXML Attributes
|
#region UXML Attributes
|
||||||
@@ -174,7 +174,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
|
|
||||||
public UTKNumberStepper()
|
public UTKIntStepper()
|
||||||
{
|
{
|
||||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||||
LoadStyleSheet();
|
LoadStyleSheet();
|
||||||
@@ -183,12 +183,12 @@ namespace UVC.UIToolkit
|
|||||||
SubscribeToThemeChanges();
|
SubscribeToThemeChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UTKNumberStepper(bool isReadOnly = false): this()
|
public UTKIntStepper(bool isReadOnly = false): this()
|
||||||
{
|
{
|
||||||
_isReadOnly = isReadOnly;
|
_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;
|
_isReadOnly = isReadOnly;
|
||||||
_minValue = minValue;
|
_minValue = minValue;
|
||||||
@@ -64,6 +64,9 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
/// <summary>속성 클릭 이벤트</summary>
|
/// <summary>속성 클릭 이벤트</summary>
|
||||||
public event Action<IUTKPropertyItem>? OnPropertyClicked;
|
public event Action<IUTKPropertyItem>? OnPropertyClicked;
|
||||||
|
|
||||||
|
/// <summary>버튼 클릭 이벤트 (액션 이름 전달)</summary>
|
||||||
|
public event Action<string, string>? OnPropertyButtonClicked;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -576,6 +579,12 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
view.Unbind();
|
view.Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDisposable 구현체인 경우 Dispose 호출
|
||||||
|
if (child is IDisposable disposable)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
element.Clear();
|
element.Clear();
|
||||||
@@ -601,11 +610,21 @@ namespace UVC.UIToolkit
|
|||||||
groupElement.Add(title);
|
groupElement.Add(title);
|
||||||
groupElement.Add(count);
|
groupElement.Add(count);
|
||||||
|
|
||||||
groupElement.RegisterCallback<ClickEvent>(_ =>
|
// 그룹 클릭 이벤트 - DetachFromPanelEvent에서 자동 정리됨
|
||||||
|
EventCallback<ClickEvent> clickCallback = null!;
|
||||||
|
clickCallback = _ =>
|
||||||
{
|
{
|
||||||
ToggleGroupExpanded(group.GroupId);
|
ToggleGroupExpanded(group.GroupId);
|
||||||
expandIcon.SetMaterialIcon(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
|
expandIcon.SetMaterialIcon(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
|
||||||
OnGroupExpandedChanged?.Invoke(group, group.IsExpanded);
|
OnGroupExpandedChanged?.Invoke(group, group.IsExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
groupElement.RegisterCallback(clickCallback);
|
||||||
|
|
||||||
|
// DetachFromPanelEvent에서 이벤트 해제
|
||||||
|
groupElement.RegisterCallback<DetachFromPanelEvent>(evt =>
|
||||||
|
{
|
||||||
|
groupElement.UnregisterCallback(clickCallback);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.Add(groupElement);
|
container.Add(groupElement);
|
||||||
@@ -616,7 +635,47 @@ namespace UVC.UIToolkit
|
|||||||
// View Factory를 사용하여 View 생성 및 바인딩
|
// View Factory를 사용하여 View 생성 및 바인딩
|
||||||
var itemView = UTKPropertyItemViewFactory.CreateView(item);
|
var itemView = UTKPropertyItemViewFactory.CreateView(item);
|
||||||
|
|
||||||
itemView.RegisterCallback<ClickEvent>(_ => OnPropertyClicked?.Invoke(item));
|
// 클릭 이벤트 등록 - DetachFromPanelEvent에서 자동 정리됨
|
||||||
|
EventCallback<ClickEvent> clickCallback = _ => OnPropertyClicked?.Invoke(item);
|
||||||
|
itemView.RegisterCallback(clickCallback);
|
||||||
|
|
||||||
|
// 버튼 아이템인 경우 버튼 클릭 이벤트 구독
|
||||||
|
Action<string>? buttonClickHandler = null;
|
||||||
|
if (itemView is UTKButtonItemView buttonView)
|
||||||
|
{
|
||||||
|
buttonClickHandler = (actionName) =>
|
||||||
|
{
|
||||||
|
OnPropertyButtonClicked?.Invoke(item.Id, actionName);
|
||||||
|
};
|
||||||
|
buttonView.OnButtonClicked += buttonClickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String 아이템에 ActionButton이 있는 경우 이벤트 구독
|
||||||
|
Action<string>? actionButtonClickHandler = null;
|
||||||
|
if (itemView is UTKStringPropertyItemView stringView)
|
||||||
|
{
|
||||||
|
actionButtonClickHandler = (actionName) =>
|
||||||
|
{
|
||||||
|
OnPropertyButtonClicked?.Invoke(item.Id, actionName);
|
||||||
|
};
|
||||||
|
stringView.OnActionButtonClicked += actionButtonClickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachFromPanelEvent에서 이벤트 해제
|
||||||
|
itemView.RegisterCallback<DetachFromPanelEvent>(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);
|
container.Add(itemView);
|
||||||
}
|
}
|
||||||
@@ -720,6 +779,7 @@ namespace UVC.UIToolkit
|
|||||||
OnPropertyValueChanged = null;
|
OnPropertyValueChanged = null;
|
||||||
OnGroupExpandedChanged = null;
|
OnGroupExpandedChanged = null;
|
||||||
OnPropertyClicked = null;
|
OnPropertyClicked = null;
|
||||||
|
OnPropertyButtonClicked = null;
|
||||||
|
|
||||||
// UI 참조 정리
|
// UI 참조 정리
|
||||||
_treeView = null;
|
_treeView = null;
|
||||||
|
|||||||
@@ -188,8 +188,8 @@ namespace UVC.UIToolkit
|
|||||||
private Button? _nextYearButton;
|
private Button? _nextYearButton;
|
||||||
private VisualElement? _dayNamesRow;
|
private VisualElement? _dayNamesRow;
|
||||||
private VisualElement? _timeRow;
|
private VisualElement? _timeRow;
|
||||||
private UTKNumberStepper? _hourStepper;
|
private UTKIntStepper? _hourStepper;
|
||||||
private UTKNumberStepper? _minuteStepper;
|
private UTKIntStepper? _minuteStepper;
|
||||||
private UTKButton? _cancelButton;
|
private UTKButton? _cancelButton;
|
||||||
private UTKButton? _confirmButton;
|
private UTKButton? _confirmButton;
|
||||||
private VisualElement? _header;
|
private VisualElement? _header;
|
||||||
@@ -648,14 +648,14 @@ namespace UVC.UIToolkit
|
|||||||
var timeLabel = new Label("Time:");
|
var timeLabel = new Label("Time:");
|
||||||
timeLabel.AddToClassList("utk-date-picker__time-label");
|
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.AddToClassList("utk-date-picker__time-stepper");
|
||||||
_hourStepper.WrapAround = true;
|
_hourStepper.WrapAround = true;
|
||||||
|
|
||||||
var colonLabel = new Label(":");
|
var colonLabel = new Label(":");
|
||||||
colonLabel.AddToClassList("utk-date-picker__time-separator");
|
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.AddToClassList("utk-date-picker__time-stepper");
|
||||||
_minuteStepper.WrapAround = true;
|
_minuteStepper.WrapAround = true;
|
||||||
|
|
||||||
@@ -709,8 +709,8 @@ namespace UVC.UIToolkit
|
|||||||
_nextYearButton ??= this.Q<Button>("next-year");
|
_nextYearButton ??= this.Q<Button>("next-year");
|
||||||
_dayNamesRow ??= this.Q<VisualElement>("day-names");
|
_dayNamesRow ??= this.Q<VisualElement>("day-names");
|
||||||
_timeRow ??= this.Q<VisualElement>("time-row");
|
_timeRow ??= this.Q<VisualElement>("time-row");
|
||||||
_hourStepper ??= this.Q<UTKNumberStepper>("hour-stepper");
|
_hourStepper ??= this.Q<UTKIntStepper>("hour-stepper");
|
||||||
_minuteStepper ??= this.Q<UTKNumberStepper>("minute-stepper");
|
_minuteStepper ??= this.Q<UTKIntStepper>("minute-stepper");
|
||||||
_cancelButton ??= this.Q<UTKButton>("cancel-btn");
|
_cancelButton ??= this.Q<UTKButton>("cancel-btn");
|
||||||
_confirmButton ??= this.Q<UTKButton>("confirm-btn");
|
_confirmButton ??= this.Q<UTKButton>("confirm-btn");
|
||||||
_rangeInfoLabel ??= this.Q<Label>("range-info");
|
_rangeInfoLabel ??= this.Q<Label>("range-info");
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ namespace UVC.UIToolkit
|
|||||||
/// <summary>읽기 전용 여부</summary>
|
/// <summary>읽기 전용 여부</summary>
|
||||||
bool IsReadOnly { get; set; }
|
bool IsReadOnly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>라벨 표시 여부 (false면 value가 전체 너비 사용)</summary>
|
||||||
|
bool ShowLabel { get; set; }
|
||||||
|
|
||||||
/// <summary>속성 타입</summary>
|
/// <summary>속성 타입</summary>
|
||||||
UTKPropertyType PropertyType { get; }
|
UTKPropertyType PropertyType { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ namespace UVC.UIToolkit
|
|||||||
/// <summary>문자열 목록 드롭다운</summary>
|
/// <summary>문자열 목록 드롭다운</summary>
|
||||||
DropdownList,
|
DropdownList,
|
||||||
|
|
||||||
|
/// <summary>다중 선택 드롭다운</summary>
|
||||||
|
MultiSelectDropdownList,
|
||||||
|
|
||||||
/// <summary>라디오 버튼 그룹</summary>
|
/// <summary>라디오 버튼 그룹</summary>
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
|
|
||||||
@@ -57,6 +60,9 @@ namespace UVC.UIToolkit
|
|||||||
DateTimeRange,
|
DateTimeRange,
|
||||||
|
|
||||||
/// <summary>상태 + 색상 복합 타입</summary>
|
/// <summary>상태 + 색상 복합 타입</summary>
|
||||||
ColorState
|
ColorState,
|
||||||
|
|
||||||
|
/// <summary>버튼 (액션 트리거)</summary>
|
||||||
|
Button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace UVC.UIToolkit
|
|||||||
private T _value;
|
private T _value;
|
||||||
private bool _isReadOnly;
|
private bool _isReadOnly;
|
||||||
private bool _isVisible = true;
|
private bool _isVisible = true;
|
||||||
|
private bool _showLabel = true;
|
||||||
private string? _description;
|
private string? _description;
|
||||||
private string? _tooltip;
|
private string? _tooltip;
|
||||||
private string? _groupId;
|
private string? _groupId;
|
||||||
@@ -103,6 +104,13 @@ namespace UVC.UIToolkit
|
|||||||
get => _groupId;
|
get => _groupId;
|
||||||
set => _groupId = value;
|
set => _groupId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>라벨 표시 여부 (false면 value가 전체 너비 사용)</summary>
|
||||||
|
public bool ShowLabel
|
||||||
|
{
|
||||||
|
get => _showLabel;
|
||||||
|
set => _showLabel = value;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|||||||
119
Assets/Scripts/UVC/UIToolkit/Property/Items/UTKButtonItem.cs
Normal file
119
Assets/Scripts/UVC/UIToolkit/Property/Items/UTKButtonItem.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UVC.UIToolkit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 버튼 속성 Item 클래스입니다.
|
||||||
|
/// UTKButton 설정을 포함하며, 클릭 시 Action 이름을 전달합니다.
|
||||||
|
/// </summary>
|
||||||
|
public class UTKButtonItem : UTKPropertyItemBase<string>
|
||||||
|
{
|
||||||
|
#region Properties
|
||||||
|
public override UTKPropertyType PropertyType => UTKPropertyType.Button;
|
||||||
|
|
||||||
|
/// <summary>버튼 텍스트</summary>
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>버튼 아이콘</summary>
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
/// <summary>아이콘 크기</summary>
|
||||||
|
public int IconSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>버튼 스타일 변형</summary>
|
||||||
|
public UTKButton.ButtonVariant Variant { get; set; }
|
||||||
|
|
||||||
|
/// <summary>버튼 크기</summary>
|
||||||
|
public UTKButton.ButtonSize Size { get; set; }
|
||||||
|
|
||||||
|
/// <summary>커스텀 배경 색상</summary>
|
||||||
|
public Color? BackgroundColor { get; set; }
|
||||||
|
|
||||||
|
/// <summary>외곽선 굵기</summary>
|
||||||
|
public int BorderWidth { get; set; }
|
||||||
|
|
||||||
|
/// <summary>아이콘만 표시 모드</summary>
|
||||||
|
public bool IconOnly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>라벨 표시 여부 (false면 버튼이 전체 너비 사용)</summary>
|
||||||
|
public bool ShowLabel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>액션 이름 (버튼 클릭 시 전달되는 고유 이름)</summary>
|
||||||
|
public string ActionName { get; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
/// <summary>
|
||||||
|
/// 기본 버튼 아이템을 생성합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">고유 ID</param>
|
||||||
|
/// <param name="actionName">액션 이름 (클릭 시 전달)</param>
|
||||||
|
/// <param name="text">버튼 텍스트</param>
|
||||||
|
/// <param name="icon">버튼 아이콘</param>
|
||||||
|
/// <param name="variant">버튼 스타일</param>
|
||||||
|
/// <param name="size">버튼 크기</param>
|
||||||
|
public UTKButtonItem(
|
||||||
|
string id,
|
||||||
|
string actionName,
|
||||||
|
string text = "",
|
||||||
|
string icon = "",
|
||||||
|
UTKButton.ButtonVariant variant = UTKButton.ButtonVariant.Normal,
|
||||||
|
UTKButton.ButtonSize size = UTKButton.ButtonSize.Medium)
|
||||||
|
: base(id, actionName, actionName)
|
||||||
|
{
|
||||||
|
ActionName = actionName;
|
||||||
|
Text = text;
|
||||||
|
Icon = icon;
|
||||||
|
IconSize = 12;
|
||||||
|
Variant = variant;
|
||||||
|
Size = size;
|
||||||
|
BackgroundColor = null;
|
||||||
|
BorderWidth = -1;
|
||||||
|
IconOnly = false;
|
||||||
|
ShowLabel = true; // 기본적으로 라벨 표시
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 전체 설정을 포함한 버튼 아이템을 생성합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">고유 ID</param>
|
||||||
|
/// <param name="actionName">액션 이름</param>
|
||||||
|
/// <param name="text">버튼 텍스트</param>
|
||||||
|
/// <param name="icon">버튼 아이콘</param>
|
||||||
|
/// <param name="iconSize">아이콘 크기</param>
|
||||||
|
/// <param name="variant">버튼 스타일</param>
|
||||||
|
/// <param name="size">버튼 크기</param>
|
||||||
|
/// <param name="backgroundColor">배경 색상</param>
|
||||||
|
/// <param name="borderWidth">외곽선 굵기</param>
|
||||||
|
/// <param name="iconOnly">아이콘만 표시</param>
|
||||||
|
/// <param name="showLabel">라벨 표시 여부</param>
|
||||||
|
public UTKButtonItem(
|
||||||
|
string id,
|
||||||
|
string actionName,
|
||||||
|
string text,
|
||||||
|
string icon,
|
||||||
|
int iconSize,
|
||||||
|
UTKButton.ButtonVariant variant,
|
||||||
|
UTKButton.ButtonSize size,
|
||||||
|
Color? backgroundColor,
|
||||||
|
int borderWidth,
|
||||||
|
bool iconOnly,
|
||||||
|
bool showLabel = true)
|
||||||
|
: base(id, actionName, actionName)
|
||||||
|
{
|
||||||
|
ActionName = actionName;
|
||||||
|
Text = text;
|
||||||
|
Icon = icon;
|
||||||
|
IconSize = iconSize;
|
||||||
|
Variant = variant;
|
||||||
|
Size = size;
|
||||||
|
BackgroundColor = backgroundColor;
|
||||||
|
BorderWidth = borderWidth;
|
||||||
|
IconOnly = iconOnly;
|
||||||
|
ShowLabel = showLabel;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 180e39abb0136364c90d52fbd5b63fd6
|
||||||
@@ -10,8 +10,10 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
private bool _useSlider;
|
private bool _useSlider;
|
||||||
|
private bool _useStepper;
|
||||||
private float _minValue;
|
private float _minValue;
|
||||||
private float _maxValue = 1f;
|
private float _maxValue = 1f;
|
||||||
|
private float _step = 1.0f;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@@ -25,6 +27,20 @@ namespace UVC.UIToolkit
|
|||||||
set => _useSlider = value;
|
set => _useSlider = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>스테퍼(증감 버튼) 사용 여부</summary>
|
||||||
|
public bool UseStepper
|
||||||
|
{
|
||||||
|
get => _useStepper;
|
||||||
|
set => _useStepper = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>스테퍼 증감 단위 (기본값: 0.1)</summary>
|
||||||
|
public float Step
|
||||||
|
{
|
||||||
|
get => _step;
|
||||||
|
set => _step = value > 0 ? value : 0.1f;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>최소값 (슬라이더 모드)</summary>
|
/// <summary>최소값 (슬라이더 모드)</summary>
|
||||||
public float MinValue
|
public float MinValue
|
||||||
{
|
{
|
||||||
@@ -63,13 +79,15 @@ namespace UVC.UIToolkit
|
|||||||
/// <param name="minValue">최소값</param>
|
/// <param name="minValue">최소값</param>
|
||||||
/// <param name="maxValue">최대값</param>
|
/// <param name="maxValue">최대값</param>
|
||||||
/// <param name="useSlider">슬라이더 사용 여부</param>
|
/// <param name="useSlider">슬라이더 사용 여부</param>
|
||||||
|
/// <param name="useStepper">스테퍼 사용 여부</param>
|
||||||
/// <param name="isReadOnly">읽기 전용 여부</param>
|
/// <param name="isReadOnly">읽기 전용 여부</param>
|
||||||
public UTKFloatPropertyItem(string id, string name, float initialValue, float minValue = float.MinValue, float maxValue = float.MaxValue, bool useSlider = true, bool isReadOnly = false)
|
public UTKFloatPropertyItem(string id, string name, float initialValue, float minValue = float.MinValue, float maxValue = float.MaxValue, bool useSlider = true, bool useStepper = false, bool isReadOnly = false)
|
||||||
: base(id, name, initialValue)
|
: base(id, name, initialValue)
|
||||||
{
|
{
|
||||||
_minValue = minValue;
|
_minValue = minValue;
|
||||||
_maxValue = maxValue;
|
_maxValue = maxValue;
|
||||||
_useSlider = useSlider;
|
_useSlider = useSlider;
|
||||||
|
_useStepper = useStepper;
|
||||||
IsReadOnly = isReadOnly;
|
IsReadOnly = isReadOnly;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace UVC.UIToolkit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 다중 선택 드롭다운 속성 데이터 클래스입니다.
|
||||||
|
/// UI는 UTKMultiSelectDropdownPropertyItemView에서 담당합니다.
|
||||||
|
/// </summary>
|
||||||
|
public class UTKMultiSelectDropdownPropertyItem : UTKPropertyItemBase<List<string>>
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
private List<string> _choices;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
/// <summary>속성 타입</summary>
|
||||||
|
public override UTKPropertyType PropertyType => UTKPropertyType.MultiSelectDropdownList;
|
||||||
|
|
||||||
|
/// <summary>선택 가능한 항목 목록</summary>
|
||||||
|
public List<string> Choices
|
||||||
|
{
|
||||||
|
get => _choices;
|
||||||
|
set => _choices = value ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>선택된 인덱스 목록</summary>
|
||||||
|
public List<int> SelectedIndices
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Value == null || Value.Count == 0)
|
||||||
|
return new List<int>();
|
||||||
|
|
||||||
|
return Value
|
||||||
|
.Select(v => _choices.IndexOf(v))
|
||||||
|
.Where(i => i >= 0)
|
||||||
|
.OrderBy(i => i)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
/// <summary>
|
||||||
|
/// 다중 선택 드롭다운 속성을 생성합니다.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">고유 ID</param>
|
||||||
|
/// <param name="name">표시 이름</param>
|
||||||
|
/// <param name="choices">선택 항목 목록</param>
|
||||||
|
/// <param name="initialValues">초기 선택 값 목록</param>
|
||||||
|
/// <param name="isReadOnly">읽기 전용 여부</param>
|
||||||
|
public UTKMultiSelectDropdownPropertyItem(
|
||||||
|
string id,
|
||||||
|
string name,
|
||||||
|
List<string> choices,
|
||||||
|
List<string>? initialValues = null,
|
||||||
|
bool isReadOnly = false)
|
||||||
|
: base(id, name, initialValues ?? new List<string>())
|
||||||
|
{
|
||||||
|
_choices = choices ?? new List<string>();
|
||||||
|
|
||||||
|
// initialValues가 유효한지 확인하고 필터링
|
||||||
|
if (initialValues != null && initialValues.Count > 0)
|
||||||
|
{
|
||||||
|
var validValues = initialValues.Where(v => _choices.Contains(v)).ToList();
|
||||||
|
Value = validValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsReadOnly = isReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 다중 선택 드롭다운 속성을 생성합니다 (인덱스 기반).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">고유 ID</param>
|
||||||
|
/// <param name="name">표시 이름</param>
|
||||||
|
/// <param name="choices">선택 항목 목록</param>
|
||||||
|
/// <param name="selectedIndices">초기 선택 인덱스 목록</param>
|
||||||
|
/// <param name="isReadOnly">읽기 전용 여부</param>
|
||||||
|
public UTKMultiSelectDropdownPropertyItem(
|
||||||
|
string id,
|
||||||
|
string name,
|
||||||
|
IEnumerable<string> choices,
|
||||||
|
IEnumerable<int>? selectedIndices = null,
|
||||||
|
bool isReadOnly = false)
|
||||||
|
: base(id, name, new List<string>())
|
||||||
|
{
|
||||||
|
_choices = choices?.ToList() ?? new List<string>();
|
||||||
|
|
||||||
|
if (selectedIndices != null)
|
||||||
|
{
|
||||||
|
var validValues = selectedIndices
|
||||||
|
.Where(i => i >= 0 && i < _choices.Count)
|
||||||
|
.Select(i => _choices[i])
|
||||||
|
.ToList();
|
||||||
|
Value = validValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsReadOnly = isReadOnly;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
/// <summary>선택 항목 추가</summary>
|
||||||
|
public void AddChoice(string choice)
|
||||||
|
{
|
||||||
|
if (!_choices.Contains(choice))
|
||||||
|
{
|
||||||
|
_choices.Add(choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>선택 항목 제거</summary>
|
||||||
|
public bool RemoveChoice(string choice)
|
||||||
|
{
|
||||||
|
bool removed = _choices.Remove(choice);
|
||||||
|
if (removed && Value != null && Value.Contains(choice))
|
||||||
|
{
|
||||||
|
// 값 목록에서도 제거
|
||||||
|
var newValue = Value.Where(v => v != choice).ToList();
|
||||||
|
Value = newValue;
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>인덱스로 선택 설정</summary>
|
||||||
|
public void SetSelectedIndices(List<int> indices)
|
||||||
|
{
|
||||||
|
var validValues = indices
|
||||||
|
.Where(i => i >= 0 && i < _choices.Count)
|
||||||
|
.Select(i => _choices[i])
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
Value = validValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>값으로 선택 설정</summary>
|
||||||
|
public void SetSelectedValues(List<string> values)
|
||||||
|
{
|
||||||
|
var validValues = values
|
||||||
|
.Where(v => _choices.Contains(v))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
Value = validValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모든 항목 선택</summary>
|
||||||
|
public void SelectAll()
|
||||||
|
{
|
||||||
|
Value = new List<string>(_choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모든 선택 해제</summary>
|
||||||
|
public void ClearSelection()
|
||||||
|
{
|
||||||
|
Value = new List<string>();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8ffce5db6e1fd4748b4e6c1d72812fb7
|
||||||
@@ -30,6 +30,9 @@ namespace UVC.UIToolkit
|
|||||||
get => _maxLength;
|
get => _maxLength;
|
||||||
set => _maxLength = value;
|
set => _maxLength = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>액션 버튼 설정 (null이면 버튼 미표시)</summary>
|
||||||
|
public UTKButtonItem? ActionButton { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -42,12 +45,14 @@ namespace UVC.UIToolkit
|
|||||||
/// <param name="isMultiline">멀티라인 모드</param>
|
/// <param name="isMultiline">멀티라인 모드</param>
|
||||||
/// <param name="maxLength">최대 문자 길이</param>
|
/// <param name="maxLength">최대 문자 길이</param>
|
||||||
/// <param name="isReadOnly">읽기 전용 여부</param>
|
/// <param name="isReadOnly">읽기 전용 여부</param>
|
||||||
public UTKStringPropertyItem(string id, string name, string initialValue = "", bool isMultiline = false, int maxLength = -1, bool isReadOnly = false)
|
/// <param name="actionButton">액션 버튼</param>
|
||||||
|
public UTKStringPropertyItem(string id, string name, string initialValue = "", bool isMultiline = false, int maxLength = -1, bool isReadOnly = false, UTKButtonItem? actionButton = null)
|
||||||
: base(id, name, initialValue ?? string.Empty)
|
: base(id, name, initialValue ?? string.Empty)
|
||||||
{
|
{
|
||||||
IsReadOnly = isReadOnly;
|
IsReadOnly = isReadOnly;
|
||||||
_isMultiline = isMultiline;
|
_isMultiline = isMultiline;
|
||||||
_maxLength = maxLength;
|
_maxLength = maxLength;
|
||||||
|
ActionButton = actionButton;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
222
Assets/Scripts/UVC/UIToolkit/Property/Views/UTKButtonItemView.cs
Normal file
222
Assets/Scripts/UVC/UIToolkit/Property/Views/UTKButtonItemView.cs
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace UVC.UIToolkit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 버튼 속성 View 클래스입니다.
|
||||||
|
/// UTKButton을 표시하고 클릭 시 Action 이름을 이벤트로 전달합니다.
|
||||||
|
/// </summary>
|
||||||
|
[UxmlElement]
|
||||||
|
public partial class UTKButtonItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
private UTKButton? _button;
|
||||||
|
private IUTKPropertyItem<string>? _boundData;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
protected override string ViewTypeName => "UTKButtonItemView";
|
||||||
|
|
||||||
|
/// <summary>값 변경 이벤트 (IUTKPropertyItemView 인터페이스 구현)</summary>
|
||||||
|
public event Action<string>? OnValueChanged;
|
||||||
|
|
||||||
|
/// <summary>버튼 클릭 이벤트</summary>
|
||||||
|
public event Action<string>? OnButtonClicked;
|
||||||
|
|
||||||
|
/// <summary>액션 이름 (현재 Value)</summary>
|
||||||
|
public string Value
|
||||||
|
{
|
||||||
|
get => _boundData?.Value ?? "";
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_boundData != null)
|
||||||
|
{
|
||||||
|
_boundData.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
public UTKButtonItemView() : base()
|
||||||
|
{
|
||||||
|
InitializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UTKButtonItemView(UTKButtonItem data) : this()
|
||||||
|
{
|
||||||
|
Bind(data);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
private void InitializeUI()
|
||||||
|
{
|
||||||
|
AddToClassList("utk-property-item-view");
|
||||||
|
AddToClassList("utk-property-item-view--button");
|
||||||
|
|
||||||
|
if (!CreateUIFromUxml())
|
||||||
|
{
|
||||||
|
CreateUIFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryUIElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueryUIElements()
|
||||||
|
{
|
||||||
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
_button = this.Q<UTKButton>("button-field");
|
||||||
|
if (_button == null)
|
||||||
|
{
|
||||||
|
_button = new UTKButton { name = "button-field" };
|
||||||
|
_button.AddToClassList("utk-property-item-view__button");
|
||||||
|
_valueContainer.Add(_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 버튼 클릭 이벤트 등록
|
||||||
|
_button.OnClicked += OnButtonClickedInternal;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Override Methods
|
||||||
|
protected override void CreateValueUI(VisualElement container)
|
||||||
|
{
|
||||||
|
// Fallback: UXML 로드 실패 시 코드로 생성
|
||||||
|
_button = new UTKButton { name = "button-field" };
|
||||||
|
_button.AddToClassList("utk-property-item-view__button");
|
||||||
|
container.Add(_button);
|
||||||
|
|
||||||
|
_button.OnClicked += OnButtonClickedInternal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RefreshUI()
|
||||||
|
{
|
||||||
|
// 버튼은 정적이므로 별도 갱신 불필요
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
||||||
|
{
|
||||||
|
// 버튼은 ReadOnly 개념이 적용되지 않으므로 IsEnabled로 제어
|
||||||
|
if (_button != null)
|
||||||
|
{
|
||||||
|
_button.IsEnabled = !isReadOnly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handling
|
||||||
|
private void OnButtonClickedInternal()
|
||||||
|
{
|
||||||
|
if (_boundData is UTKButtonItem buttonItem)
|
||||||
|
{
|
||||||
|
OnButtonClicked?.Invoke(buttonItem.ActionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Binding
|
||||||
|
public void Bind(IUTKPropertyItem data)
|
||||||
|
{
|
||||||
|
if (data is IUTKPropertyItem<string> stringData)
|
||||||
|
{
|
||||||
|
Bind(stringData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[UTKButtonItemView] Cannot bind to non-string data: {data.GetType().Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bind(IUTKPropertyItem<string> data)
|
||||||
|
{
|
||||||
|
Unbind();
|
||||||
|
|
||||||
|
_boundData = data;
|
||||||
|
|
||||||
|
// 라벨 텍스트 설정
|
||||||
|
if (_labelElement != null)
|
||||||
|
{
|
||||||
|
_labelElement.Text = data.DisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTKButtonItem 설정 적용
|
||||||
|
if (data is UTKButtonItem buttonItem && _button != null)
|
||||||
|
{
|
||||||
|
_button.Text = buttonItem.Text;
|
||||||
|
_button.Icon = buttonItem.Icon;
|
||||||
|
_button.IconSize = buttonItem.IconSize;
|
||||||
|
_button.Variant = buttonItem.Variant;
|
||||||
|
_button.Size = buttonItem.Size;
|
||||||
|
_button.IconOnly = buttonItem.IconOnly;
|
||||||
|
|
||||||
|
if (buttonItem.BackgroundColor.HasValue)
|
||||||
|
{
|
||||||
|
_button.BackgroundColor = buttonItem.BackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonItem.BorderWidth >= 0)
|
||||||
|
{
|
||||||
|
_button.BorderWidth = buttonItem.BorderWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
_button.IsEnabled = !buttonItem.IsReadOnly;
|
||||||
|
|
||||||
|
// 라벨 표시 여부 제어
|
||||||
|
if (_labelElement != null)
|
||||||
|
{
|
||||||
|
if (buttonItem.ShowLabel)
|
||||||
|
{
|
||||||
|
_labelElement.style.display = DisplayStyle.Flex;
|
||||||
|
RemoveFromClassList("utk-property-item-view--button-no-label");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_labelElement.style.display = DisplayStyle.None;
|
||||||
|
AddToClassList("utk-property-item-view--button-no-label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsReadOnly = data.IsReadOnly;
|
||||||
|
IsVisible = data.IsVisible;
|
||||||
|
TooltipText = data.Tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unbind()
|
||||||
|
{
|
||||||
|
if (_boundData != null)
|
||||||
|
{
|
||||||
|
_boundData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Dispose
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
if (_button != null)
|
||||||
|
{
|
||||||
|
_button.OnClicked -= OnButtonClickedInternal;
|
||||||
|
_button.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Unbind();
|
||||||
|
OnValueChanged = null;
|
||||||
|
OnButtonClicked = null;
|
||||||
|
_button = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1b8d1cca2a2e54e4ea3536f4de0e1938
|
||||||
@@ -10,12 +10,14 @@ namespace UVC.UIToolkit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dropdown 속성 View 클래스입니다.
|
/// Dropdown 속성 View 클래스입니다.
|
||||||
/// UTKDropdown을 사용하여 문자열 목록에서 선택합니다.
|
/// UTKDropdown을 사용하여 문자열 목록에서 선택합니다.
|
||||||
|
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UxmlElement]
|
[UxmlElement]
|
||||||
public partial class UTKDropdownPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
|
public partial class UTKDropdownPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<string>
|
||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
private UTKDropdown? _dropdown;
|
private UTKDropdown? _dropdown;
|
||||||
|
private UTKInputField? _readOnlyField;
|
||||||
|
|
||||||
private string _value = string.Empty;
|
private string _value = string.Empty;
|
||||||
private List<string> _choices = new();
|
private List<string> _choices = new();
|
||||||
@@ -125,23 +127,48 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
private void QueryUIElements()
|
private void QueryUIElements()
|
||||||
{
|
{
|
||||||
_dropdown = this.Q<UTKDropdown>("dropdown-field");
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
if (IsReadOnly)
|
||||||
if (_valueContainer != null && _dropdown == null)
|
|
||||||
{
|
{
|
||||||
_dropdown = new UTKDropdown { name = "dropdown-field" };
|
// ReadOnly: UTKInputField 사용
|
||||||
_dropdown.AddToClassList("utk-property-item-view__dropdown");
|
_readOnlyField = this.Q<UTKInputField>("readonly-field");
|
||||||
_valueContainer.Add(_dropdown);
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = _value;
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
|
||||||
|
// Dropdown 숨기기
|
||||||
|
if (_dropdown != null)
|
||||||
|
{
|
||||||
|
_dropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 초기 값 설정
|
|
||||||
if (_dropdown != null)
|
|
||||||
{
|
{
|
||||||
|
// Editable: UTKDropdown 사용
|
||||||
|
_dropdown = this.Q<UTKDropdown>("dropdown-field");
|
||||||
|
if (_dropdown == null)
|
||||||
|
{
|
||||||
|
_dropdown = new UTKDropdown { name = "dropdown-field" };
|
||||||
|
_dropdown.AddToClassList("utk-property-item-view__dropdown");
|
||||||
|
_valueContainer.Add(_dropdown);
|
||||||
|
}
|
||||||
_dropdown.SetOptions(_choices);
|
_dropdown.SetOptions(_choices);
|
||||||
int selectedIndex = _choices.IndexOf(_value);
|
int selectedIndex = _choices.IndexOf(_value);
|
||||||
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
||||||
_dropdown.IsEnabled = !IsReadOnly;
|
_dropdown.IsEnabled = true;
|
||||||
|
_dropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
// ReadOnly 필드 숨기기
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,9 +202,46 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
||||||
{
|
{
|
||||||
if (_dropdown != null)
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
if (isReadOnly)
|
||||||
{
|
{
|
||||||
_dropdown.IsEnabled = !isReadOnly;
|
// Dropdown → InputField 전환
|
||||||
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = _value;
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
_readOnlyField.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_dropdown != null)
|
||||||
|
{
|
||||||
|
_dropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// InputField → Dropdown 전환
|
||||||
|
if (_dropdown == null)
|
||||||
|
{
|
||||||
|
_dropdown = new UTKDropdown { name = "dropdown-field" };
|
||||||
|
_dropdown.AddToClassList("utk-property-item-view__dropdown");
|
||||||
|
_valueContainer.Add(_dropdown);
|
||||||
|
_dropdown.SetOptions(_choices);
|
||||||
|
RegisterEvents();
|
||||||
|
}
|
||||||
|
int selectedIndex = _choices.IndexOf(_value);
|
||||||
|
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
|
||||||
|
_dropdown.IsEnabled = true;
|
||||||
|
_dropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -201,7 +265,11 @@ namespace UVC.UIToolkit
|
|||||||
#region Value Update
|
#region Value Update
|
||||||
private void UpdateValueUI()
|
private void UpdateValueUI()
|
||||||
{
|
{
|
||||||
if (_dropdown != null && _dropdown.SelectedValue != _value)
|
if (IsReadOnly && _readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.Value = _value;
|
||||||
|
}
|
||||||
|
else if (_dropdown != null && _dropdown.SelectedValue != _value)
|
||||||
{
|
{
|
||||||
_dropdown.SetSelectedValue(_value, notify: false);
|
_dropdown.SetSelectedValue(_value, notify: false);
|
||||||
}
|
}
|
||||||
@@ -310,6 +378,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
OnValueChanged = null;
|
OnValueChanged = null;
|
||||||
_dropdown = null;
|
_dropdown = null;
|
||||||
|
_readOnlyField = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ namespace UVC.UIToolkit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enum 속성 View 클래스입니다.
|
/// Enum 속성 View 클래스입니다.
|
||||||
/// UTKEnumDropDown을 사용하여 열거형 값을 표시/편집합니다.
|
/// UTKEnumDropDown을 사용하여 열거형 값을 표시/편집합니다.
|
||||||
|
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UxmlElement]
|
[UxmlElement]
|
||||||
public partial class UTKEnumPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<Enum>
|
public partial class UTKEnumPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<Enum>
|
||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
private UTKEnumDropDown? _enumDropdown;
|
private UTKEnumDropDown? _enumDropdown;
|
||||||
|
private UTKInputField? _readOnlyField;
|
||||||
|
|
||||||
private Enum? _value;
|
private Enum? _value;
|
||||||
private Type? _enumType;
|
private Type? _enumType;
|
||||||
@@ -103,24 +105,49 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
private void QueryUIElements()
|
private void QueryUIElements()
|
||||||
{
|
{
|
||||||
_enumDropdown = this.Q<UTKEnumDropDown>("enum-dropdown");
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
if (IsReadOnly)
|
||||||
if (_valueContainer != null && _enumDropdown == null)
|
|
||||||
{
|
{
|
||||||
_enumDropdown = new UTKEnumDropDown { name = "enum-dropdown" };
|
// ReadOnly: UTKInputField 사용
|
||||||
_enumDropdown.AddToClassList("utk-property-item-view__dropdown");
|
_readOnlyField = this.Q<UTKInputField>("readonly-field");
|
||||||
_valueContainer.Add(_enumDropdown);
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = _value?.ToString() ?? "";
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
|
||||||
|
// Dropdown 숨기기
|
||||||
|
if (_enumDropdown != null)
|
||||||
|
{
|
||||||
|
_enumDropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 초기 값 설정
|
|
||||||
if (_enumDropdown != null)
|
|
||||||
{
|
{
|
||||||
|
// Editable: UTKEnumDropDown 사용
|
||||||
|
_enumDropdown = this.Q<UTKEnumDropDown>("enum-dropdown");
|
||||||
|
if (_enumDropdown == null)
|
||||||
|
{
|
||||||
|
_enumDropdown = new UTKEnumDropDown { name = "enum-dropdown" };
|
||||||
|
_enumDropdown.AddToClassList("utk-property-item-view__dropdown");
|
||||||
|
_valueContainer.Add(_enumDropdown);
|
||||||
|
}
|
||||||
if (_value != null)
|
if (_value != null)
|
||||||
{
|
{
|
||||||
_enumDropdown.Init(_value);
|
_enumDropdown.Init(_value);
|
||||||
}
|
}
|
||||||
_enumDropdown.IsEnabled = !IsReadOnly;
|
_enumDropdown.IsEnabled = true;
|
||||||
|
_enumDropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
// ReadOnly 필드 숨기기
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,9 +181,47 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
||||||
{
|
{
|
||||||
if (_enumDropdown != null)
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
if (isReadOnly)
|
||||||
{
|
{
|
||||||
_enumDropdown.IsEnabled = !isReadOnly;
|
// Dropdown → InputField 전환
|
||||||
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = _value?.ToString() ?? "";
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
_readOnlyField.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_enumDropdown != null)
|
||||||
|
{
|
||||||
|
_enumDropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// InputField → Dropdown 전환
|
||||||
|
if (_enumDropdown == null)
|
||||||
|
{
|
||||||
|
_enumDropdown = new UTKEnumDropDown { name = "enum-dropdown" };
|
||||||
|
_enumDropdown.AddToClassList("utk-property-item-view__dropdown");
|
||||||
|
_valueContainer.Add(_enumDropdown);
|
||||||
|
if (_value != null)
|
||||||
|
{
|
||||||
|
_enumDropdown.Init(_value);
|
||||||
|
}
|
||||||
|
RegisterEvents();
|
||||||
|
}
|
||||||
|
_enumDropdown.IsEnabled = true;
|
||||||
|
_enumDropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -180,7 +245,11 @@ namespace UVC.UIToolkit
|
|||||||
#region Value Update
|
#region Value Update
|
||||||
private void UpdateValueUI()
|
private void UpdateValueUI()
|
||||||
{
|
{
|
||||||
if (_enumDropdown != null && _value != null)
|
if (IsReadOnly && _readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.Value = _value?.ToString() ?? "";
|
||||||
|
}
|
||||||
|
else if (_enumDropdown != null && _value != null)
|
||||||
{
|
{
|
||||||
if (_enumDropdown.Value != _value)
|
if (_enumDropdown.Value != _value)
|
||||||
{
|
{
|
||||||
@@ -258,6 +327,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
OnValueChanged = null;
|
OnValueChanged = null;
|
||||||
_enumDropdown = null;
|
_enumDropdown = null;
|
||||||
|
_readOnlyField = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|||||||
@@ -37,11 +37,14 @@ namespace UVC.UIToolkit
|
|||||||
#region Fields
|
#region Fields
|
||||||
private UTKFloatField? _floatField;
|
private UTKFloatField? _floatField;
|
||||||
private UTKSlider? _slider;
|
private UTKSlider? _slider;
|
||||||
|
private UTKFloatStepper? _stepper;
|
||||||
|
|
||||||
private float _value;
|
private float _value;
|
||||||
private float _minValue;
|
private float _minValue;
|
||||||
private float _maxValue = 1f;
|
private float _maxValue = 1f;
|
||||||
|
private float _step = 0.1f;
|
||||||
private bool _useSlider;
|
private bool _useSlider;
|
||||||
|
private bool _useStepper;
|
||||||
|
|
||||||
private IUTKPropertyItem<float>? _boundData;
|
private IUTKPropertyItem<float>? _boundData;
|
||||||
#endregion
|
#endregion
|
||||||
@@ -112,10 +115,35 @@ namespace UVC.UIToolkit
|
|||||||
if (_useSlider != value)
|
if (_useSlider != value)
|
||||||
{
|
{
|
||||||
_useSlider = value;
|
_useSlider = value;
|
||||||
UpdateSliderClass();
|
if (value) _useStepper = false;
|
||||||
|
UpdateModeClass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>스테퍼(증감 버튼) 사용 여부</summary>
|
||||||
|
[UxmlAttribute("use-stepper")]
|
||||||
|
public bool UseStepper
|
||||||
|
{
|
||||||
|
get => _useStepper;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_useStepper != value)
|
||||||
|
{
|
||||||
|
_useStepper = value;
|
||||||
|
if (value) _useSlider = false;
|
||||||
|
UpdateModeClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>스테퍼 증감 단위</summary>
|
||||||
|
[UxmlAttribute("step")]
|
||||||
|
public float Step
|
||||||
|
{
|
||||||
|
get => _step;
|
||||||
|
set => _step = value > 0 ? value : 0.1f;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
@@ -141,7 +169,9 @@ namespace UVC.UIToolkit
|
|||||||
_value = data.Value;
|
_value = data.Value;
|
||||||
_minValue = data.MinValue;
|
_minValue = data.MinValue;
|
||||||
_maxValue = data.MaxValue;
|
_maxValue = data.MaxValue;
|
||||||
|
_step = data.Step;
|
||||||
_useSlider = data.UseSlider;
|
_useSlider = data.UseSlider;
|
||||||
|
_useStepper = data.UseStepper;
|
||||||
Label = data.Name;
|
Label = data.Name;
|
||||||
_isReadOnly = data.IsReadOnly;
|
_isReadOnly = data.IsReadOnly;
|
||||||
InitializeUI();
|
InitializeUI();
|
||||||
@@ -176,8 +206,8 @@ namespace UVC.UIToolkit
|
|||||||
// 이벤트 등록
|
// 이벤트 등록
|
||||||
RegisterEvents();
|
RegisterEvents();
|
||||||
|
|
||||||
// 슬라이더 클래스 업데이트
|
// 모드 클래스 업데이트
|
||||||
UpdateSliderClass();
|
UpdateModeClass();
|
||||||
|
|
||||||
UpdateValueUI();
|
UpdateValueUI();
|
||||||
UpdateReadOnlyState();
|
UpdateReadOnlyState();
|
||||||
@@ -187,6 +217,7 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider = this.Q<UTKSlider>("slider-field");
|
_slider = this.Q<UTKSlider>("slider-field");
|
||||||
_floatField = this.Q<UTKFloatField>("value-field");
|
_floatField = this.Q<UTKFloatField>("value-field");
|
||||||
|
_stepper = this.Q<UTKFloatStepper>("stepper-field");
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
// Fallback: UXML에서 못 찾으면 생성
|
||||||
if (_valueContainer != null)
|
if (_valueContainer != null)
|
||||||
@@ -207,6 +238,16 @@ namespace UVC.UIToolkit
|
|||||||
_floatField.AddToClassList("utk-property-item-view__field");
|
_floatField.AddToClassList("utk-property-item-view__field");
|
||||||
_valueContainer.Add(_floatField);
|
_valueContainer.Add(_floatField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper == null)
|
||||||
|
{
|
||||||
|
_stepper = new UTKFloatStepper(_minValue, _maxValue, _value, _step, IsReadOnly)
|
||||||
|
{
|
||||||
|
name = "stepper-field"
|
||||||
|
};
|
||||||
|
_stepper.AddToClassList("utk-property-item-view__stepper");
|
||||||
|
_valueContainer.Add(_stepper);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 초기 값 설정
|
// 초기 값 설정
|
||||||
@@ -221,10 +262,28 @@ namespace UVC.UIToolkit
|
|||||||
_floatField.SetValueWithoutNotify(_value);
|
_floatField.SetValueWithoutNotify(_value);
|
||||||
_floatField.isReadOnly = IsReadOnly;
|
_floatField.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()
|
||||||
{
|
{
|
||||||
|
// ReadOnly 상태에서는 무조건 FloatField만 표시
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
RemoveFromClassList("utk-property-item-view--slider");
|
||||||
|
RemoveFromClassList("utk-property-item-view--stepper");
|
||||||
|
UpdateControlVisibility();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 슬라이더 클래스
|
||||||
if (_useSlider)
|
if (_useSlider)
|
||||||
{
|
{
|
||||||
AddToClassList("utk-property-item-view--slider");
|
AddToClassList("utk-property-item-view--slider");
|
||||||
@@ -233,6 +292,52 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
RemoveFromClassList("utk-property-item-view--slider");
|
RemoveFromClassList("utk-property-item-view--slider");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 스테퍼 클래스
|
||||||
|
if (_useStepper)
|
||||||
|
{
|
||||||
|
AddToClassList("utk-property-item-view--stepper");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveFromClassList("utk-property-item-view--stepper");
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateControlVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>컨트롤 가시성을 업데이트합니다.</summary>
|
||||||
|
private void UpdateControlVisibility()
|
||||||
|
{
|
||||||
|
bool isReadOnlyMode = IsReadOnly;
|
||||||
|
bool showSlider = !isReadOnlyMode && _useSlider;
|
||||||
|
bool showStepper = !isReadOnlyMode && _useStepper;
|
||||||
|
bool showFloatField = !showStepper; // 스테퍼 모드가 아니면 항상 FloatField 표시
|
||||||
|
|
||||||
|
if (_slider != null)
|
||||||
|
{
|
||||||
|
_slider.style.display = showSlider ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stepper != null)
|
||||||
|
{
|
||||||
|
_stepper.style.display = showStepper ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_floatField != null)
|
||||||
|
{
|
||||||
|
_floatField.style.display = showFloatField ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
|
||||||
|
// 슬라이더 모드일 때 필드 너비 조정
|
||||||
|
if (showSlider)
|
||||||
|
{
|
||||||
|
_floatField.AddToClassList("utk-property-item-view__field--with-slider");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_floatField.RemoveFromClassList("utk-property-item-view__field--with-slider");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -258,6 +363,14 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider.IsEnabled = !isReadOnly;
|
_slider.IsEnabled = !isReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null)
|
||||||
|
{
|
||||||
|
_stepper.IsReadOnly = isReadOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadOnly 상태 변경 시 모드 클래스 업데이트
|
||||||
|
UpdateModeClass();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -273,6 +386,11 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider.OnValueChanged += OnSliderChanged;
|
_slider.OnValueChanged += OnSliderChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null)
|
||||||
|
{
|
||||||
|
_stepper.OnValueChanged += OnStepperChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UnregisterEvents()
|
private void UnregisterEvents()
|
||||||
@@ -286,6 +404,11 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider.OnValueChanged -= OnSliderChanged;
|
_slider.OnValueChanged -= OnSliderChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null)
|
||||||
|
{
|
||||||
|
_stepper.OnValueChanged -= OnStepperChanged;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFloatFieldChanged(float newValue)
|
private void OnFloatFieldChanged(float newValue)
|
||||||
@@ -322,6 +445,11 @@ namespace UVC.UIToolkit
|
|||||||
_floatField.SetValueWithoutNotify(newValue);
|
_floatField.SetValueWithoutNotify(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null && !Mathf.Approximately(_stepper.Value, newValue))
|
||||||
|
{
|
||||||
|
_stepper.SetValue(newValue, false);
|
||||||
|
}
|
||||||
|
|
||||||
// 값 설정 (이벤트 발생)
|
// 값 설정 (이벤트 발생)
|
||||||
if (!Mathf.Approximately(_value, newValue))
|
if (!Mathf.Approximately(_value, newValue))
|
||||||
{
|
{
|
||||||
@@ -334,6 +462,20 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStepperChanged(float newValue)
|
||||||
|
{
|
||||||
|
if (!Mathf.Approximately(_value, newValue))
|
||||||
|
{
|
||||||
|
_value = newValue;
|
||||||
|
OnValueChanged?.Invoke(newValue);
|
||||||
|
|
||||||
|
if (_boundData != null && !Mathf.Approximately(_boundData.Value, newValue))
|
||||||
|
{
|
||||||
|
_boundData.Value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Value Update
|
#region Value Update
|
||||||
@@ -348,6 +490,11 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider.SetValueWithoutNotify(_value);
|
_slider.SetValueWithoutNotify(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null && !Mathf.Approximately(_stepper.Value, _value))
|
||||||
|
{
|
||||||
|
_stepper.SetValue(_value, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -378,16 +525,28 @@ namespace UVC.UIToolkit
|
|||||||
IsVisible = data.IsVisible;
|
IsVisible = data.IsVisible;
|
||||||
TooltipText = data.Tooltip;
|
TooltipText = data.Tooltip;
|
||||||
|
|
||||||
// 슬라이더 관련 속성 동기화
|
// 슬라이더 및 스테퍼 관련 속성 동기화
|
||||||
if (data is UTKFloatPropertyItem floatItem)
|
if (data is UTKFloatPropertyItem floatItem)
|
||||||
{
|
{
|
||||||
_minValue = floatItem.MinValue;
|
_minValue = floatItem.MinValue;
|
||||||
_maxValue = floatItem.MaxValue;
|
_maxValue = floatItem.MaxValue;
|
||||||
|
_step = floatItem.Step;
|
||||||
|
|
||||||
|
// 모드 변경 확인
|
||||||
|
bool modeChanged = false;
|
||||||
if (_useSlider != floatItem.UseSlider)
|
if (_useSlider != floatItem.UseSlider)
|
||||||
{
|
{
|
||||||
_useSlider = floatItem.UseSlider;
|
_useSlider = floatItem.UseSlider;
|
||||||
UpdateSliderClass();
|
modeChanged = true;
|
||||||
|
}
|
||||||
|
if (_useStepper != floatItem.UseStepper)
|
||||||
|
{
|
||||||
|
_useStepper = floatItem.UseStepper;
|
||||||
|
modeChanged = true;
|
||||||
|
}
|
||||||
|
if (modeChanged)
|
||||||
|
{
|
||||||
|
UpdateModeClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 슬라이더 범위 업데이트
|
// 슬라이더 범위 업데이트
|
||||||
@@ -396,6 +555,14 @@ namespace UVC.UIToolkit
|
|||||||
_slider.lowValue = _minValue;
|
_slider.lowValue = _minValue;
|
||||||
_slider.highValue = _maxValue;
|
_slider.highValue = _maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 스테퍼 설정 업데이트
|
||||||
|
if (_stepper != null)
|
||||||
|
{
|
||||||
|
_stepper.MinValue = _minValue;
|
||||||
|
_stepper.MaxValue = _maxValue;
|
||||||
|
_stepper.Step = _step;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 데이터 변경 이벤트 구독
|
// 데이터 변경 이벤트 구독
|
||||||
@@ -438,6 +605,8 @@ namespace UVC.UIToolkit
|
|||||||
OnValueChanged = null;
|
OnValueChanged = null;
|
||||||
_floatField = null;
|
_floatField = null;
|
||||||
_slider = null;
|
_slider = null;
|
||||||
|
_stepper?.Dispose();
|
||||||
|
_stepper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Int 속성 View 클래스입니다.
|
/// Int 속성 View 클래스입니다.
|
||||||
/// UTKIntegerField, UTKSliderInt, 또는 UTKNumberStepper를 사용하여 int 값을 표시/편집합니다.
|
/// UTKIntegerField, UTKSliderInt, 또는 UTKIntStepper를 사용하여 int 값을 표시/편집합니다.
|
||||||
///
|
///
|
||||||
/// <para><b>사용법 (단독 사용):</b></para>
|
/// <para><b>사용법 (단독 사용):</b></para>
|
||||||
/// <code>
|
/// <code>
|
||||||
@@ -36,7 +36,7 @@ namespace UVC.UIToolkit
|
|||||||
#region Fields
|
#region Fields
|
||||||
private UTKIntegerField? _intField;
|
private UTKIntegerField? _intField;
|
||||||
private UTKSliderInt? _slider;
|
private UTKSliderInt? _slider;
|
||||||
private UTKNumberStepper? _stepper;
|
private UTKIntStepper? _stepper;
|
||||||
|
|
||||||
private int _value;
|
private int _value;
|
||||||
private int _minValue;
|
private int _minValue;
|
||||||
@@ -219,7 +219,7 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_slider = this.Q<UTKSliderInt>("slider-field");
|
_slider = this.Q<UTKSliderInt>("slider-field");
|
||||||
_intField = this.Q<UTKIntegerField>("value-field");
|
_intField = this.Q<UTKIntegerField>("value-field");
|
||||||
_stepper = this.Q<UTKNumberStepper>("stepper-field");
|
_stepper = this.Q<UTKIntStepper>("stepper-field");
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
// Fallback: UXML에서 못 찾으면 생성
|
||||||
if (_valueContainer != null)
|
if (_valueContainer != null)
|
||||||
@@ -243,7 +243,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
if (_stepper == null)
|
if (_stepper == null)
|
||||||
{
|
{
|
||||||
_stepper = new UTKNumberStepper(_minValue, _maxValue, _value, _step, IsReadOnly)
|
_stepper = new UTKIntStepper(_minValue, _maxValue, _value, _step, IsReadOnly)
|
||||||
{
|
{
|
||||||
name = "stepper-field"
|
name = "stepper-field"
|
||||||
};
|
};
|
||||||
@@ -347,7 +347,7 @@ namespace UVC.UIToolkit
|
|||||||
bool isReadOnlyMode = IsReadOnly;
|
bool isReadOnlyMode = IsReadOnly;
|
||||||
bool showSlider = !isReadOnlyMode && _useSlider;
|
bool showSlider = !isReadOnlyMode && _useSlider;
|
||||||
bool showStepper = !isReadOnlyMode && _useStepper;
|
bool showStepper = !isReadOnlyMode && _useStepper;
|
||||||
bool showIntField = isReadOnlyMode || (!_useSlider && !_useStepper);
|
bool showIntField = !showStepper; // 스테퍼 모드가 아니면 항상 IntField 표시
|
||||||
|
|
||||||
if (_slider != null)
|
if (_slider != null)
|
||||||
{
|
{
|
||||||
@@ -362,6 +362,16 @@ namespace UVC.UIToolkit
|
|||||||
if (_intField != null)
|
if (_intField != null)
|
||||||
{
|
{
|
||||||
_intField.style.display = showIntField ? DisplayStyle.Flex : DisplayStyle.None;
|
_intField.style.display = showIntField ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
|
||||||
|
// 슬라이더 모드일 때 필드 너비 조정
|
||||||
|
if (showSlider)
|
||||||
|
{
|
||||||
|
_intField.AddToClassList("utk-property-item-view__field--with-slider");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_intField.RemoveFromClassList("utk-property-item-view__field--with-slider");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -436,6 +446,11 @@ namespace UVC.UIToolkit
|
|||||||
_intField.SetValueWithoutNotify(newValue);
|
_intField.SetValueWithoutNotify(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_stepper != null && _stepper.Value != newValue)
|
||||||
|
{
|
||||||
|
_stepper.SetValue(newValue, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (_value != newValue)
|
if (_value != newValue)
|
||||||
{
|
{
|
||||||
_value = newValue;
|
_value = newValue;
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ namespace UVC.UIToolkit
|
|||||||
#region Fields
|
#region Fields
|
||||||
private UTKIntegerField? _minField;
|
private UTKIntegerField? _minField;
|
||||||
private UTKIntegerField? _maxField;
|
private UTKIntegerField? _maxField;
|
||||||
private UTKNumberStepper? _minStepper;
|
private UTKIntStepper? _minStepper;
|
||||||
private UTKNumberStepper? _maxStepper;
|
private UTKIntStepper? _maxStepper;
|
||||||
|
|
||||||
private UTKIntRange _value;
|
private UTKIntRange _value;
|
||||||
private bool _useStepper;
|
private bool _useStepper;
|
||||||
@@ -150,8 +150,8 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
_minField = this.Q<UTKIntegerField>("min-field");
|
_minField = this.Q<UTKIntegerField>("min-field");
|
||||||
_maxField = this.Q<UTKIntegerField>("max-field");
|
_maxField = this.Q<UTKIntegerField>("max-field");
|
||||||
_minStepper = this.Q<UTKNumberStepper>("min-stepper");
|
_minStepper = this.Q<UTKIntStepper>("min-stepper");
|
||||||
_maxStepper = this.Q<UTKNumberStepper>("max-stepper");
|
_maxStepper = this.Q<UTKIntStepper>("max-stepper");
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
// Fallback: UXML에서 못 찾으면 생성
|
||||||
if (_valueContainer != null)
|
if (_valueContainer != null)
|
||||||
@@ -182,7 +182,7 @@ namespace UVC.UIToolkit
|
|||||||
// NumberStepper 생성
|
// NumberStepper 생성
|
||||||
if (_minStepper == null)
|
if (_minStepper == null)
|
||||||
{
|
{
|
||||||
_minStepper = new UTKNumberStepper(_stepperMinValue, _stepperMaxValue, _value.Min, _stepperStep, IsReadOnly)
|
_minStepper = new UTKIntStepper(_stepperMinValue, _stepperMaxValue, _value.Min, _stepperStep, IsReadOnly)
|
||||||
{
|
{
|
||||||
name = "min-stepper"
|
name = "min-stepper"
|
||||||
};
|
};
|
||||||
@@ -193,7 +193,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
if (_maxStepper == null)
|
if (_maxStepper == null)
|
||||||
{
|
{
|
||||||
_maxStepper = new UTKNumberStepper(_stepperMinValue, _stepperMaxValue, _value.Max, _stepperStep, IsReadOnly)
|
_maxStepper = new UTKIntStepper(_stepperMinValue, _stepperMaxValue, _value.Max, _stepperStep, IsReadOnly)
|
||||||
{
|
{
|
||||||
name = "max-stepper"
|
name = "max-stepper"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,448 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace UVC.UIToolkit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MultiSelectDropdown 속성 View 클래스입니다.
|
||||||
|
/// UTKMultiSelectDropdown을 사용하여 문자열 목록에서 다중 선택합니다.
|
||||||
|
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
|
||||||
|
/// </summary>
|
||||||
|
[UxmlElement]
|
||||||
|
public partial class UTKMultiSelectDropdownPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<List<string>>
|
||||||
|
{
|
||||||
|
#region Fields
|
||||||
|
private UTKMultiSelectDropdown? _multiSelectDropdown;
|
||||||
|
private UTKInputField? _readOnlyField;
|
||||||
|
|
||||||
|
private List<string> _value = new();
|
||||||
|
private List<string> _choices = new();
|
||||||
|
|
||||||
|
private IUTKPropertyItem<List<string>>? _boundData;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
protected override string ViewTypeName => "UTKMultiSelectDropdownPropertyItemView";
|
||||||
|
|
||||||
|
/// <summary>현재 값 (선택된 항목 목록)</summary>
|
||||||
|
public List<string> Value
|
||||||
|
{
|
||||||
|
get => _value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var newValue = value ?? new List<string>();
|
||||||
|
|
||||||
|
// 리스트 내용 비교
|
||||||
|
if (!AreListsEqual(_value, newValue))
|
||||||
|
{
|
||||||
|
_value = new List<string>(newValue);
|
||||||
|
UpdateValueUI();
|
||||||
|
OnValueChanged?.Invoke(_value);
|
||||||
|
|
||||||
|
if (_boundData != null && !AreListsEqual(_boundData.Value, _value))
|
||||||
|
{
|
||||||
|
_boundData.Value = new List<string>(_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>선택 가능한 항목 목록</summary>
|
||||||
|
public List<string> Choices
|
||||||
|
{
|
||||||
|
get => _choices;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_choices = value ?? new List<string>();
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
public event Action<List<string>>? OnValueChanged;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
public UTKMultiSelectDropdownPropertyItemView() : base()
|
||||||
|
{
|
||||||
|
InitializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UTKMultiSelectDropdownPropertyItemView(UTKMultiSelectDropdownPropertyItem data) : base()
|
||||||
|
{
|
||||||
|
_choices = data.Choices;
|
||||||
|
_value = data.Value != null ? new List<string>(data.Value) : new List<string>();
|
||||||
|
Label = data.Name;
|
||||||
|
_isReadOnly = data.IsReadOnly;
|
||||||
|
|
||||||
|
InitializeUI();
|
||||||
|
Bind(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UTKMultiSelectDropdownPropertyItemView(
|
||||||
|
string label,
|
||||||
|
List<string> choices,
|
||||||
|
List<string>? initialValues = null) : base()
|
||||||
|
{
|
||||||
|
_choices = choices ?? new List<string>();
|
||||||
|
_value = initialValues != null ? new List<string>(initialValues) : new List<string>();
|
||||||
|
Label = label;
|
||||||
|
InitializeUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UTKMultiSelectDropdownPropertyItemView(
|
||||||
|
string label,
|
||||||
|
IEnumerable<string> choices,
|
||||||
|
IEnumerable<int>? selectedIndices = null) : base()
|
||||||
|
{
|
||||||
|
_choices = choices?.ToList() ?? new List<string>();
|
||||||
|
|
||||||
|
if (selectedIndices != null)
|
||||||
|
{
|
||||||
|
_value = selectedIndices
|
||||||
|
.Where(i => i >= 0 && i < _choices.Count)
|
||||||
|
.Select(i => _choices[i])
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Label = label;
|
||||||
|
InitializeUI();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
private void InitializeUI()
|
||||||
|
{
|
||||||
|
AddToClassList("utk-property-item-view");
|
||||||
|
AddToClassList("utk-property-item-view--multiselect-dropdown");
|
||||||
|
|
||||||
|
if (!CreateUIFromUxml())
|
||||||
|
{
|
||||||
|
CreateUIFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// UXML에서 요소 가져오기
|
||||||
|
QueryUIElements();
|
||||||
|
|
||||||
|
// 이벤트 등록
|
||||||
|
RegisterEvents();
|
||||||
|
|
||||||
|
UpdateValueUI();
|
||||||
|
UpdateReadOnlyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueryUIElements()
|
||||||
|
{
|
||||||
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
// ReadOnly: UTKInputField 사용
|
||||||
|
_readOnlyField = this.Q<UTKInputField>("readonly-field");
|
||||||
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = string.Join(", ", _value);
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
|
||||||
|
// Dropdown 숨기기
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Editable: UTKMultiSelectDropdown 사용
|
||||||
|
_multiSelectDropdown = this.Q<UTKMultiSelectDropdown>("multiselect-dropdown-field");
|
||||||
|
if (_multiSelectDropdown == null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
|
||||||
|
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
|
||||||
|
_valueContainer.Add(_multiSelectDropdown);
|
||||||
|
}
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
|
||||||
|
_multiSelectDropdown.IsEnabled = true;
|
||||||
|
_multiSelectDropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
// ReadOnly 필드 숨기기
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterEvents()
|
||||||
|
{
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.OnSelectionChanged += OnMultiSelectDropdownChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterEvents()
|
||||||
|
{
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.OnSelectionChanged -= OnMultiSelectDropdownChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Override Methods
|
||||||
|
protected override void CreateValueUI(VisualElement container)
|
||||||
|
{
|
||||||
|
// Fallback: UXML 로드 실패 시 코드로 생성
|
||||||
|
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
|
||||||
|
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
|
||||||
|
container.Add(_multiSelectDropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RefreshUI()
|
||||||
|
{
|
||||||
|
UpdateValueUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
||||||
|
{
|
||||||
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
if (isReadOnly)
|
||||||
|
{
|
||||||
|
// Dropdown → InputField 전환
|
||||||
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = string.Join(", ", _value);
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
_readOnlyField.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// InputField → Dropdown 전환
|
||||||
|
if (_multiSelectDropdown == null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown = new UTKMultiSelectDropdown { name = "multiselect-dropdown-field" };
|
||||||
|
_multiSelectDropdown.AddToClassList("utk-property-item-view__multiselect-dropdown");
|
||||||
|
_valueContainer.Add(_multiSelectDropdown);
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
RegisterEvents();
|
||||||
|
}
|
||||||
|
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
|
||||||
|
_multiSelectDropdown.IsEnabled = true;
|
||||||
|
_multiSelectDropdown.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handling
|
||||||
|
private void OnMultiSelectDropdownChanged(List<int> indices, List<string> values)
|
||||||
|
{
|
||||||
|
if (!AreListsEqual(_value, values))
|
||||||
|
{
|
||||||
|
_value = new List<string>(values);
|
||||||
|
OnValueChanged?.Invoke(_value);
|
||||||
|
|
||||||
|
if (_boundData != null && !AreListsEqual(_boundData.Value, _value))
|
||||||
|
{
|
||||||
|
_boundData.Value = new List<string>(_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Value Update
|
||||||
|
private void UpdateValueUI()
|
||||||
|
{
|
||||||
|
if (IsReadOnly && _readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.Value = string.Join(", ", _value);
|
||||||
|
}
|
||||||
|
else if (_multiSelectDropdown != null && !AreListsEqual(_multiSelectDropdown.SelectedValues, _value))
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetSelectedValues(_value, notify: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
/// <summary>두 리스트의 내용이 같은지 비교 (순서 무관)</summary>
|
||||||
|
private bool AreListsEqual(List<string>? list1, List<string>? list2)
|
||||||
|
{
|
||||||
|
if (list1 == null && list2 == null) return true;
|
||||||
|
if (list1 == null || list2 == null) return false;
|
||||||
|
if (list1.Count != list2.Count) return false;
|
||||||
|
|
||||||
|
var set1 = new HashSet<string>(list1);
|
||||||
|
var set2 = new HashSet<string>(list2);
|
||||||
|
return set1.SetEquals(set2);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
/// <summary>선택 항목 추가</summary>
|
||||||
|
public void AddChoice(string choice)
|
||||||
|
{
|
||||||
|
if (!_choices.Contains(choice))
|
||||||
|
{
|
||||||
|
_choices.Add(choice);
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>선택 항목 제거</summary>
|
||||||
|
public bool RemoveChoice(string choice)
|
||||||
|
{
|
||||||
|
bool removed = _choices.Remove(choice);
|
||||||
|
if (removed && _multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
|
||||||
|
if (_value.Contains(choice))
|
||||||
|
{
|
||||||
|
var newValue = _value.Where(v => v != choice).ToList();
|
||||||
|
Value = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>인덱스로 선택 설정</summary>
|
||||||
|
public void SetSelectedIndices(List<int> indices, bool notify = true)
|
||||||
|
{
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetSelectedIndices(indices, notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>값으로 선택 설정</summary>
|
||||||
|
public void SetSelectedValues(List<string> values, bool notify = true)
|
||||||
|
{
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetSelectedValues(values, notify);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모든 항목 선택</summary>
|
||||||
|
public void SelectAll()
|
||||||
|
{
|
||||||
|
_multiSelectDropdown?.SelectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모든 선택 해제</summary>
|
||||||
|
public void ClearSelection()
|
||||||
|
{
|
||||||
|
_multiSelectDropdown?.ClearSelection();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Binding
|
||||||
|
public void Bind(IUTKPropertyItem data)
|
||||||
|
{
|
||||||
|
if (data is IUTKPropertyItem<List<string>> listData)
|
||||||
|
{
|
||||||
|
Bind(listData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[UTKMultiSelectDropdownPropertyItemView] Cannot bind to non-List<string> data: {data.GetType().Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Bind(IUTKPropertyItem<List<string>> data)
|
||||||
|
{
|
||||||
|
Unbind();
|
||||||
|
|
||||||
|
_boundData = data;
|
||||||
|
|
||||||
|
Label = data.Name;
|
||||||
|
_value = data.Value != null ? new List<string>(data.Value) : new List<string>();
|
||||||
|
IsReadOnly = data.IsReadOnly;
|
||||||
|
IsVisible = data.IsVisible;
|
||||||
|
TooltipText = data.Tooltip;
|
||||||
|
|
||||||
|
if (data is UTKMultiSelectDropdownPropertyItem multiSelectItem)
|
||||||
|
{
|
||||||
|
_choices = multiSelectItem.Choices;
|
||||||
|
if (_multiSelectDropdown != null)
|
||||||
|
{
|
||||||
|
_multiSelectDropdown.SetOptions(_choices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.OnTypedValueChanged += OnDataValueChanged;
|
||||||
|
|
||||||
|
UpdateValueUI();
|
||||||
|
UpdateReadOnlyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unbind()
|
||||||
|
{
|
||||||
|
if (_boundData != null)
|
||||||
|
{
|
||||||
|
_boundData.OnTypedValueChanged -= OnDataValueChanged;
|
||||||
|
_boundData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataValueChanged(IUTKPropertyItem<List<string>> item, List<string> oldValue, List<string> newValue)
|
||||||
|
{
|
||||||
|
if (!AreListsEqual(_value, newValue))
|
||||||
|
{
|
||||||
|
_value = newValue != null ? new List<string>(newValue) : new List<string>();
|
||||||
|
UpdateValueUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Dispose
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
UnregisterEvents();
|
||||||
|
Unbind();
|
||||||
|
|
||||||
|
OnValueChanged = null;
|
||||||
|
_multiSelectDropdown = null;
|
||||||
|
_readOnlyField = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bcad826251c165b429f843262a7835bc
|
||||||
@@ -35,6 +35,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
protected const string USS_CLASS_READONLY = "utk-property-item-view--readonly";
|
protected const string USS_CLASS_READONLY = "utk-property-item-view--readonly";
|
||||||
protected const string USS_CLASS_HIDDEN = "utk-property-item-view--hidden";
|
protected const string USS_CLASS_HIDDEN = "utk-property-item-view--hidden";
|
||||||
|
protected const string USS_CLASS_NO_LABEL = "utk-property-item-view--no-label";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Static Cache
|
#region Static Cache
|
||||||
@@ -51,6 +52,7 @@ namespace UVC.UIToolkit
|
|||||||
protected string _label = string.Empty;
|
protected string _label = string.Empty;
|
||||||
protected bool _isReadOnly = false;
|
protected bool _isReadOnly = false;
|
||||||
protected bool _isVisible = true;
|
protected bool _isVisible = true;
|
||||||
|
protected bool _showLabel = true;
|
||||||
protected string? _tooltipText;
|
protected string? _tooltipText;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -111,6 +113,21 @@ namespace UVC.UIToolkit
|
|||||||
UpdateTooltip();
|
UpdateTooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>라벨 표시 여부 (false면 value가 전체 너비 사용)</summary>
|
||||||
|
[UxmlAttribute("show-label")]
|
||||||
|
public bool ShowLabel
|
||||||
|
{
|
||||||
|
get => _showLabel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_showLabel != value)
|
||||||
|
{
|
||||||
|
_showLabel = value;
|
||||||
|
UpdateShowLabelState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -241,6 +258,27 @@ namespace UVC.UIToolkit
|
|||||||
this.ClearTooltip();
|
this.ClearTooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>라벨 표시 상태 업데이트</summary>
|
||||||
|
protected virtual void UpdateShowLabelState()
|
||||||
|
{
|
||||||
|
if (_showLabel)
|
||||||
|
{
|
||||||
|
RemoveFromClassList(USS_CLASS_NO_LABEL);
|
||||||
|
if (_labelElement != null)
|
||||||
|
{
|
||||||
|
_labelElement.style.display = DisplayStyle.Flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddToClassList(USS_CLASS_NO_LABEL);
|
||||||
|
if (_labelElement != null)
|
||||||
|
{
|
||||||
|
_labelElement.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Theme
|
#region Theme
|
||||||
@@ -339,6 +377,21 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Helper Methods
|
||||||
|
/// <summary>
|
||||||
|
/// PropertyItem의 공통 속성을 View에 적용합니다.
|
||||||
|
/// 하위 클래스의 Bind 메서드에서 호출하세요.
|
||||||
|
/// </summary>
|
||||||
|
protected void ApplyCommonProperties(IUTKPropertyItem item)
|
||||||
|
{
|
||||||
|
Label = item.Name;
|
||||||
|
IsReadOnly = item.IsReadOnly;
|
||||||
|
IsVisible = item.IsVisible;
|
||||||
|
TooltipText = item.Tooltip;
|
||||||
|
ShowLabel = item.ShowLabel;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -76,12 +76,14 @@ namespace UVC.UIToolkit
|
|||||||
UTKPropertyType.DateTime => new UTKDateTimePropertyItemView(),
|
UTKPropertyType.DateTime => new UTKDateTimePropertyItemView(),
|
||||||
UTKPropertyType.Enum => CreateEnumView(data),
|
UTKPropertyType.Enum => CreateEnumView(data),
|
||||||
UTKPropertyType.DropdownList => CreateDropdownView(data),
|
UTKPropertyType.DropdownList => CreateDropdownView(data),
|
||||||
|
UTKPropertyType.MultiSelectDropdownList => CreateMultiSelectDropdownView(data),
|
||||||
UTKPropertyType.RadioGroup => CreateRadioView(data),
|
UTKPropertyType.RadioGroup => CreateRadioView(data),
|
||||||
UTKPropertyType.IntRange => new UTKIntRangePropertyItemView(),
|
UTKPropertyType.IntRange => new UTKIntRangePropertyItemView(),
|
||||||
UTKPropertyType.FloatRange => new UTKFloatRangePropertyItemView(),
|
UTKPropertyType.FloatRange => new UTKFloatRangePropertyItemView(),
|
||||||
UTKPropertyType.DateRange => new UTKDateRangePropertyItemView(),
|
UTKPropertyType.DateRange => new UTKDateRangePropertyItemView(),
|
||||||
UTKPropertyType.DateTimeRange => new UTKDateTimeRangePropertyItemView(),
|
UTKPropertyType.DateTimeRange => new UTKDateTimeRangePropertyItemView(),
|
||||||
UTKPropertyType.ColorState => new UTKColorStatePropertyItemView(),
|
UTKPropertyType.ColorState => new UTKColorStatePropertyItemView(),
|
||||||
|
UTKPropertyType.Button => CreateButtonView(data),
|
||||||
_ => throw new ArgumentException($"Unknown property type: {data.PropertyType}")
|
_ => throw new ArgumentException($"Unknown property type: {data.PropertyType}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -112,12 +114,14 @@ namespace UVC.UIToolkit
|
|||||||
UTKPropertyType.DateTime => new UTKDateTimePropertyItemView(),
|
UTKPropertyType.DateTime => new UTKDateTimePropertyItemView(),
|
||||||
UTKPropertyType.Enum => new UTKEnumPropertyItemView(),
|
UTKPropertyType.Enum => new UTKEnumPropertyItemView(),
|
||||||
UTKPropertyType.DropdownList => new UTKDropdownPropertyItemView(),
|
UTKPropertyType.DropdownList => new UTKDropdownPropertyItemView(),
|
||||||
|
UTKPropertyType.MultiSelectDropdownList => new UTKMultiSelectDropdownPropertyItemView(),
|
||||||
UTKPropertyType.RadioGroup => new UTKRadioPropertyItemView(),
|
UTKPropertyType.RadioGroup => new UTKRadioPropertyItemView(),
|
||||||
UTKPropertyType.IntRange => new UTKIntRangePropertyItemView(),
|
UTKPropertyType.IntRange => new UTKIntRangePropertyItemView(),
|
||||||
UTKPropertyType.FloatRange => new UTKFloatRangePropertyItemView(),
|
UTKPropertyType.FloatRange => new UTKFloatRangePropertyItemView(),
|
||||||
UTKPropertyType.DateRange => new UTKDateRangePropertyItemView(),
|
UTKPropertyType.DateRange => new UTKDateRangePropertyItemView(),
|
||||||
UTKPropertyType.DateTimeRange => new UTKDateTimeRangePropertyItemView(),
|
UTKPropertyType.DateTimeRange => new UTKDateTimeRangePropertyItemView(),
|
||||||
UTKPropertyType.ColorState => new UTKColorStatePropertyItemView(),
|
UTKPropertyType.ColorState => new UTKColorStatePropertyItemView(),
|
||||||
|
UTKPropertyType.Button => new UTKButtonItemView(),
|
||||||
_ => throw new ArgumentException($"Unknown property type: {propertyType}")
|
_ => throw new ArgumentException($"Unknown property type: {propertyType}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -210,6 +214,26 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
return new UTKRadioPropertyItemView();
|
return new UTKRadioPropertyItemView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IUTKPropertyItemView CreateMultiSelectDropdownView(IUTKPropertyItem data)
|
||||||
|
{
|
||||||
|
// UTKMultiSelectDropdownPropertyItem의 Choices로 MultiSelectDropdown 초기화
|
||||||
|
if (data is UTKMultiSelectDropdownPropertyItem multiSelectItem)
|
||||||
|
{
|
||||||
|
return new UTKMultiSelectDropdownPropertyItemView("", multiSelectItem.Choices);
|
||||||
|
}
|
||||||
|
return new UTKMultiSelectDropdownPropertyItemView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IUTKPropertyItemView CreateButtonView(IUTKPropertyItem data)
|
||||||
|
{
|
||||||
|
// UTKButtonItem으로 ButtonView 초기화
|
||||||
|
if (data is UTKButtonItem buttonItem)
|
||||||
|
{
|
||||||
|
return new UTKButtonItemView(buttonItem);
|
||||||
|
}
|
||||||
|
return new UTKButtonItemView();
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace UVC.UIToolkit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Radio 속성 View 클래스입니다.
|
/// Radio 속성 View 클래스입니다.
|
||||||
/// UTKRadioButton 그룹을 사용하여 단일 선택을 제공합니다.
|
/// UTKRadioButton 그룹을 사용하여 단일 선택을 제공합니다.
|
||||||
|
/// ReadOnly 상태에서는 UTKInputField로 표시됩니다.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[UxmlElement]
|
[UxmlElement]
|
||||||
public partial class UTKRadioPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<int>
|
public partial class UTKRadioPropertyItemView : UTKPropertyItemViewBase, IUTKPropertyItemView<int>
|
||||||
@@ -17,6 +18,7 @@ namespace UVC.UIToolkit
|
|||||||
#region Fields
|
#region Fields
|
||||||
private VisualElement? _radioContainer;
|
private VisualElement? _radioContainer;
|
||||||
private List<UTKRadioButton> _radioButtons = new();
|
private List<UTKRadioButton> _radioButtons = new();
|
||||||
|
private UTKInputField? _readOnlyField;
|
||||||
|
|
||||||
private int _value;
|
private int _value;
|
||||||
private List<string> _choices = new();
|
private List<string> _choices = new();
|
||||||
@@ -129,14 +131,45 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
private void QueryUIElements()
|
private void QueryUIElements()
|
||||||
{
|
{
|
||||||
_radioContainer = this.Q<VisualElement>("radio-container");
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
// Fallback: UXML에서 못 찾으면 생성
|
if (IsReadOnly)
|
||||||
if (_valueContainer != null && _radioContainer == null)
|
|
||||||
{
|
{
|
||||||
_radioContainer = new VisualElement { name = "radio-container" };
|
// ReadOnly: UTKInputField 사용
|
||||||
_radioContainer.AddToClassList("utk-property-item-view__radio-container");
|
_readOnlyField = this.Q<UTKInputField>("readonly-field");
|
||||||
_valueContainer.Add(_radioContainer);
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = SelectedText ?? "";
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
|
||||||
|
// Radio 컨테이너 숨기기
|
||||||
|
_radioContainer = this.Q<VisualElement>("radio-container");
|
||||||
|
if (_radioContainer != null)
|
||||||
|
{
|
||||||
|
_radioContainer.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Editable: Radio 버튼 사용
|
||||||
|
_radioContainer = this.Q<VisualElement>("radio-container");
|
||||||
|
if (_radioContainer == null)
|
||||||
|
{
|
||||||
|
_radioContainer = new VisualElement { name = "radio-container" };
|
||||||
|
_radioContainer.AddToClassList("utk-property-item-view__radio-container");
|
||||||
|
_valueContainer.Add(_radioContainer);
|
||||||
|
}
|
||||||
|
_radioContainer.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
// ReadOnly 필드 숨기기
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -154,9 +187,48 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
protected override void OnReadOnlyStateChanged(bool isReadOnly)
|
||||||
{
|
{
|
||||||
foreach (var radio in _radioButtons)
|
if (_valueContainer == null) return;
|
||||||
|
|
||||||
|
if (isReadOnly)
|
||||||
{
|
{
|
||||||
radio.IsEnabled = !isReadOnly;
|
// Radio → InputField 전환
|
||||||
|
if (_readOnlyField == null)
|
||||||
|
{
|
||||||
|
_readOnlyField = new UTKInputField { name = "readonly-field" };
|
||||||
|
_readOnlyField.AddToClassList("utk-property-item-view__readonly-field");
|
||||||
|
_valueContainer.Add(_readOnlyField);
|
||||||
|
}
|
||||||
|
_readOnlyField.Value = SelectedText ?? "";
|
||||||
|
_readOnlyField.isReadOnly = true;
|
||||||
|
_readOnlyField.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
if (_radioContainer != null)
|
||||||
|
{
|
||||||
|
_radioContainer.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// InputField → Radio 전환
|
||||||
|
if (_radioContainer == null)
|
||||||
|
{
|
||||||
|
_radioContainer = new VisualElement { name = "radio-container" };
|
||||||
|
_radioContainer.AddToClassList("utk-property-item-view__radio-container");
|
||||||
|
_valueContainer.Add(_radioContainer);
|
||||||
|
CreateRadioButtons();
|
||||||
|
}
|
||||||
|
_radioContainer.style.display = DisplayStyle.Flex;
|
||||||
|
|
||||||
|
// 라디오 버튼 활성화
|
||||||
|
foreach (var radio in _radioButtons)
|
||||||
|
{
|
||||||
|
radio.IsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_readOnlyField != null)
|
||||||
|
{
|
||||||
|
_readOnlyField.style.display = DisplayStyle.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -223,9 +295,16 @@ namespace UVC.UIToolkit
|
|||||||
#region Value Update
|
#region Value Update
|
||||||
private void UpdateSelection()
|
private void UpdateSelection()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _radioButtons.Count; i++)
|
if (IsReadOnly && _readOnlyField != null)
|
||||||
{
|
{
|
||||||
_radioButtons[i].SetChecked(i == _value, false);
|
_readOnlyField.Value = SelectedText ?? "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _radioButtons.Count; i++)
|
||||||
|
{
|
||||||
|
_radioButtons[i].SetChecked(i == _value, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -310,6 +389,7 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
OnValueChanged = null;
|
OnValueChanged = null;
|
||||||
_radioContainer = null;
|
_radioContainer = null;
|
||||||
|
_readOnlyField = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ namespace UVC.UIToolkit
|
|||||||
{
|
{
|
||||||
#region Fields
|
#region Fields
|
||||||
private UTKInputField? _inputField;
|
private UTKInputField? _inputField;
|
||||||
|
private UTKButton? _actionButton;
|
||||||
|
private Action? _actionButtonClickHandler; // 이벤트 핸들러 참조 보관
|
||||||
|
|
||||||
private string _value = string.Empty;
|
private string _value = string.Empty;
|
||||||
private bool _isMultiline = false;
|
private bool _isMultiline = false;
|
||||||
@@ -93,6 +95,9 @@ namespace UVC.UIToolkit
|
|||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
public event Action<string>? OnValueChanged;
|
public event Action<string>? OnValueChanged;
|
||||||
|
|
||||||
|
/// <summary>액션 버튼 클릭 이벤트 (actionName 전달)</summary>
|
||||||
|
public event Action<string>? OnActionButtonClicked;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@@ -232,6 +237,67 @@ namespace UVC.UIToolkit
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Action Button
|
||||||
|
private void CreateActionButton(UTKButtonItem buttonItem)
|
||||||
|
{
|
||||||
|
// 기존 버튼 제거
|
||||||
|
RemoveActionButton();
|
||||||
|
|
||||||
|
// 새 버튼 생성
|
||||||
|
_actionButton = new UTKButton
|
||||||
|
{
|
||||||
|
name = buttonItem.Name,
|
||||||
|
Text = buttonItem.Text,
|
||||||
|
Icon = buttonItem.Icon,
|
||||||
|
IconSize = buttonItem.IconSize,
|
||||||
|
Variant = buttonItem.Variant,
|
||||||
|
Size = buttonItem.Size,
|
||||||
|
IconOnly = buttonItem.IconOnly
|
||||||
|
};
|
||||||
|
if(buttonItem.IconOnly == false){
|
||||||
|
_actionButton.AddToClassList("utk-property-item-view__action-button");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_actionButton.AddToClassList("utk-property-item-view__action-button-icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonItem.BackgroundColor.HasValue)
|
||||||
|
{
|
||||||
|
_actionButton.BackgroundColor = buttonItem.BackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonItem.BorderWidth >= 0)
|
||||||
|
{
|
||||||
|
_actionButton.BorderWidth = buttonItem.BorderWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 버튼 클릭 이벤트 등록 (핸들러 참조 보관)
|
||||||
|
_actionButtonClickHandler = () => OnActionButtonClicked?.Invoke(buttonItem.ActionName);
|
||||||
|
_actionButton.OnClicked += _actionButtonClickHandler;
|
||||||
|
|
||||||
|
// value-container에 추가 (InputField 다음)
|
||||||
|
_valueContainer?.Add(_actionButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveActionButton()
|
||||||
|
{
|
||||||
|
if (_actionButton != null)
|
||||||
|
{
|
||||||
|
// 보관된 핸들러로 이벤트 해제
|
||||||
|
if (_actionButtonClickHandler != null)
|
||||||
|
{
|
||||||
|
_actionButton.OnClicked -= _actionButtonClickHandler;
|
||||||
|
_actionButtonClickHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actionButton.RemoveFromHierarchy();
|
||||||
|
_actionButton.Dispose();
|
||||||
|
_actionButton = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Data Binding
|
#region Data Binding
|
||||||
public void Bind(IUTKPropertyItem data)
|
public void Bind(IUTKPropertyItem data)
|
||||||
{
|
{
|
||||||
@@ -256,7 +322,7 @@ namespace UVC.UIToolkit
|
|||||||
IsReadOnly = data.IsReadOnly;
|
IsReadOnly = data.IsReadOnly;
|
||||||
IsVisible = data.IsVisible;
|
IsVisible = data.IsVisible;
|
||||||
TooltipText = data.Tooltip;
|
TooltipText = data.Tooltip;
|
||||||
|
|
||||||
if (data is UTKStringPropertyItem stringItem)
|
if (data is UTKStringPropertyItem stringItem)
|
||||||
{
|
{
|
||||||
_isMultiline = stringItem.IsMultiline;
|
_isMultiline = stringItem.IsMultiline;
|
||||||
@@ -266,6 +332,16 @@ namespace UVC.UIToolkit
|
|||||||
_inputField.multiline = _isMultiline;
|
_inputField.multiline = _isMultiline;
|
||||||
_inputField.maxLength = _maxLength;
|
_inputField.maxLength = _maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionButton이 있으면 버튼 생성
|
||||||
|
if (stringItem.ActionButton != null && _valueContainer != null)
|
||||||
|
{
|
||||||
|
CreateActionButton(stringItem.ActionButton);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveActionButton();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.OnTypedValueChanged += OnDataValueChanged;
|
data.OnTypedValueChanged += OnDataValueChanged;
|
||||||
@@ -281,6 +357,9 @@ namespace UVC.UIToolkit
|
|||||||
_boundData.OnTypedValueChanged -= OnDataValueChanged;
|
_boundData.OnTypedValueChanged -= OnDataValueChanged;
|
||||||
_boundData = null;
|
_boundData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActionButton 제거
|
||||||
|
RemoveActionButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDataValueChanged(IUTKPropertyItem<string> item, string oldValue, string newValue)
|
private void OnDataValueChanged(IUTKPropertyItem<string> item, string oldValue, string newValue)
|
||||||
@@ -303,7 +382,14 @@ namespace UVC.UIToolkit
|
|||||||
UnregisterEvents();
|
UnregisterEvents();
|
||||||
Unbind();
|
Unbind();
|
||||||
|
|
||||||
|
// UTKInputField Dispose 호출
|
||||||
|
if (_inputField is IDisposable disposableField)
|
||||||
|
{
|
||||||
|
disposableField.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
OnValueChanged = null;
|
OnValueChanged = null;
|
||||||
|
OnActionButtonClicked = null;
|
||||||
_inputField = null;
|
_inputField = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,13 @@ namespace UVC.UIToolkit
|
|||||||
add => PropertyList.OnPropertyClicked += value;
|
add => PropertyList.OnPropertyClicked += value;
|
||||||
remove => PropertyList.OnPropertyClicked -= value;
|
remove => PropertyList.OnPropertyClicked -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>버튼 클릭 이벤트 (PropertyList 위임)</summary>
|
||||||
|
public event Action<string, string>? OnPropertyButtonClicked
|
||||||
|
{
|
||||||
|
add => PropertyList.OnPropertyButtonClicked += value;
|
||||||
|
remove => PropertyList.OnPropertyButtonClicked -= value;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"com.unity.nuget.newtonsoft-json": "3.0.2"
|
"com.unity.nuget.newtonsoft-json": "3.0.2"
|
||||||
},
|
},
|
||||||
"hash": "ed802c11c07647966f2e0784fbcb3c64c5fce927"
|
"hash": "50c6ed4ff239e6ae21d0884ff68f52f738cf247a"
|
||||||
},
|
},
|
||||||
"com.cysharp.unitask": {
|
"com.cysharp.unitask": {
|
||||||
"version": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask",
|
"version": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask",
|
||||||
|
|||||||
Reference in New Issue
Block a user