UTKInput Validation 기능 추가
This commit is contained in:
@@ -55,7 +55,7 @@
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 12px;
|
||||
color: var(--color-base-01);
|
||||
color: var(--color-text-primary);
|
||||
-unity-text-align: middle-center;
|
||||
-unity-font-style: bold;
|
||||
opacity: 0;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
.utk-toggle > .unity-toggle__input > .unity-toggle__checkmark {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-width: 0;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-text-secondary);
|
||||
-unity-background-image-tint-color: transparent;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-boundsfield {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -101,6 +103,35 @@
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-boundsfield--error .unity-float-field > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-boundsfield--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-boundsfield__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
.utk-boundsfield--disabled {
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-boundsfield--disabled > .unity-label {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
@@ -108,4 +139,9 @@
|
||||
.utk-boundsfield--disabled .unity-float-field > .unity-base-text-field__input {
|
||||
background-color: var(--color-btn-disabled);
|
||||
color: var(--color-text-disabled);
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-boundsfield--disabled .unity-float-field > .unity-base-text-field__input:focus {
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-double-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -55,6 +57,28 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-double-field--error > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-double-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-double-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
padding-left: 68px;
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-float-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -55,6 +57,28 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-float-field--error > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-float-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-float-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
padding-left: 68px;
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
.utk-number-stepper {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
height: var(--size-input-height);
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
min-height: var(--size-input-height);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,3 +156,29 @@
|
||||
.utk-number-stepper--disabled .utk-number-stepper__btn:active {
|
||||
background-color: var(--color-btn-disabled);
|
||||
}
|
||||
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-number-stepper--error .utk-number-stepper__text-input {
|
||||
border-top-color: var(--color-border-error);
|
||||
border-bottom-color: var(--color-border-error);
|
||||
border-left-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-number-stepper--error .utk-number-stepper__btn {
|
||||
border-top-color: var(--color-border-error);
|
||||
border-right-color: var(--color-border-error);
|
||||
border-bottom-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-number-stepper__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-input {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -99,6 +101,16 @@
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-input__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
padding-left: 68px;
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Variants
|
||||
=================================== */
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
.utk-number-stepper {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
height: var(--size-input-height);
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
min-height: var(--size-input-height);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,3 +156,28 @@
|
||||
.utk-number-stepper--disabled .utk-number-stepper__btn:active {
|
||||
background-color: var(--color-btn-disabled);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-number-stepper--error .utk-number-stepper__text-input {
|
||||
border-top-color: var(--color-border-error);
|
||||
border-bottom-color: var(--color-border-error);
|
||||
border-left-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-number-stepper--error .utk-number-stepper__btn {
|
||||
border-top-color: var(--color-border-error);
|
||||
border-right-color: var(--color-border-error);
|
||||
border-bottom-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-number-stepper__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-integer-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -55,6 +57,28 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-integer-field--error > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-integer-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-integer-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
padding-left: 68px;
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-long-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -55,6 +57,27 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-long-field--error > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-long-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-long-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
padding-left: 68px;
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-rectfield {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -92,6 +94,35 @@
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-rectfield--error .unity-float-field > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-rectfield--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-rectfield__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
.utk-rectfield--disabled {
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-rectfield--disabled > .unity-label {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
@@ -99,4 +130,9 @@
|
||||
.utk-rectfield--disabled .unity-float-field > .unity-base-text-field__input {
|
||||
background-color: var(--color-btn-disabled);
|
||||
color: var(--color-text-disabled);
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-rectfield--disabled .unity-float-field > .unity-base-text-field__input:focus {
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-vector2-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@@ -98,6 +100,27 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-vector2-field--error .unity-float-field > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-vector2-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-vector2-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-vector3-field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
@@ -93,6 +95,27 @@
|
||||
border-color: var(--color-border-focus);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-vector3-field--error .unity-float-field > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-vector3-field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-vector3-field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
.utk-vector4field {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
@@ -79,6 +81,35 @@
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
/* ===================================
|
||||
Error State
|
||||
=================================== */
|
||||
|
||||
.utk-vector4field--error .unity-float-field > .unity-base-text-field__input {
|
||||
border-color: var(--color-border-error);
|
||||
}
|
||||
|
||||
.utk-vector4field--error > .unity-label {
|
||||
color: var(--color-state-error);
|
||||
}
|
||||
|
||||
/* Error Message Label */
|
||||
.utk-vector4field__error-message {
|
||||
width: 100%;
|
||||
margin-top: var(--space-xs);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-state-error);
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Disabled State
|
||||
=================================== */
|
||||
|
||||
.utk-vector4field--disabled {
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-vector4field--disabled > .unity-label {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
@@ -86,4 +117,9 @@
|
||||
.utk-vector4field--disabled .unity-float-field > .unity-base-text-field__input {
|
||||
background-color: var(--color-btn-disabled);
|
||||
color: var(--color-text-disabled);
|
||||
cursor: arrow;
|
||||
}
|
||||
|
||||
.utk-vector4field--disabled .unity-float-field > .unity-base-text-field__input:focus {
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive Extents -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Extents)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKBoundsField name="bounds-validation" label="Collider" class="utk-sample-bounds-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="bounds-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -22,6 +22,30 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Range -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Range: 0.0 ~ 100.0)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKDoubleField name="double-validation-range" label="Percent" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Only)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKDoubleField name="double-validation-positive" label="Distance" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="double-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -22,6 +22,30 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Range -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Range: 0 ~ 100)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKFloatField name="float-validation-range" label="Speed" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Only)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKFloatField name="float-validation-positive" label="Weight" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="float-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKFloatStepperSample.uss
|
||||
* Float Stepper Sample Specific Styles
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
.utk-sample-stepper {
|
||||
width: 150px;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2dcdd47a630b394ea19ea8b91f61d01
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
|
||||
<Style src="../UTKSampleCommon.uss" />
|
||||
<Style src="UTKFloatStepperSample.uss" />
|
||||
|
||||
<VisualElement class="utk-sample-container">
|
||||
<Label class="utk-sample-desc" text="실수 스테퍼 컴포넌트 (증감 버튼 포함)" />
|
||||
|
||||
<!-- Step 0.1 -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Step 0.1" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKFloatStepper name="stepper-step01" min-value="0" max-value="10" value="5.5" step="0.1" class="utk-sample-stepper" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Step 0.5 -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Step 0.5" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKFloatStepper name="stepper-step05" min-value="0" max-value="10" value="2.5" step="0.5" class="utk-sample-stepper" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Wrap Around -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Wrap Around" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKFloatStepper name="stepper-wrap" min-value="0" max-value="1" value="0.5" step="0.1" wrap-around="true" class="utk-sample-stepper" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Disabled -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Disabled" />
|
||||
<VisualElement class="utk-sample-row" name="float-stepper-disabled-row">
|
||||
<!-- Disabled stepper will be created via C# to call SetEnabled(false) -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Non-Zero -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Non-Zero)" />
|
||||
<VisualElement class="utk-sample-row" name="float-stepper-validation-row">
|
||||
<!-- Validation stepper will be created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="float-stepper-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
<utk:UTKCodeBlock name="code-uxml" title="UXML" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
</UXML>
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 908e94035af6fc5419bdf2246bc903fd
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
@@ -38,6 +38,30 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Email -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Email)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKInputField name="input-validation-email" label="Email" placeholder="example@email.com" class="utk-sample-input-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Required -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Required)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKInputField name="input-validation-required" label="Name" placeholder="이름을 입력하세요" class="utk-sample-input-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -38,6 +38,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Even Number -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Even Number Only)" />
|
||||
<VisualElement class="utk-sample-row" name="stepper-validation-row">
|
||||
<!-- Validation stepper will be created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="stepper-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -22,6 +22,30 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Range -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Range: 1~150)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKIntegerField name="int-validation-range" label="Age" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Only)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKIntegerField name="int-validation-positive" label="Quantity" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="int-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -22,6 +22,30 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Range -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Range: 0 ~ 1,000,000)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKLongField name="long-validation-range" label="File Size" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Only)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKLongField name="long-validation-positive" label="Count" value="0" class="utk-sample-input-field--narrow" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="long-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Positive Size -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Positive Size)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKRectField name="rect-validation" label="Viewport" class="utk-sample-rect-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="rect-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Non-Zero -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Non-Zero)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKVector2Field name="vec2-validation" label="Direction" class="utk-sample-vector2-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="vec2-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Non-Zero -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (Non-Zero)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKVector3Field name="vec3-validation" label="Scale" class="utk-sample-vector3-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="vec3-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -14,6 +14,22 @@
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validation - Normalized Color -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validation (0~1 Range)" />
|
||||
<VisualElement class="utk-sample-row">
|
||||
<utk:UTKVector4Field name="vec4-validation" label="RGBA" class="utk-sample-vector4-field" />
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Validate All Button -->
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="Validate All (강제 검증)" />
|
||||
<VisualElement class="utk-sample-row" name="vec4-validation-button-row">
|
||||
<!-- Button created via C# -->
|
||||
</VisualElement>
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -258,7 +258,8 @@ Textfield 항목 스타일
|
||||
|
||||
.unity-base-text-field {
|
||||
border-radius: 4px;
|
||||
height: 24px;
|
||||
/* height: 24px; */
|
||||
height: auto;
|
||||
width: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -50,20 +50,13 @@
|
||||
--color-blue-10: #062F4A;
|
||||
--color-blue-11: #001F33;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Blue Light (color-blue-light-*)
|
||||
라이트 테마 선택 항목용 밝은 파란색
|
||||
=================================== */
|
||||
--color-blue-light-01: #B3D7FF;
|
||||
--color-blue-light-02: #8EC5FF;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Red (color-red-*)
|
||||
=================================== */
|
||||
--color-red-01: #F14C4C;
|
||||
--color-red-02: #C74E39;
|
||||
--color-red-03: #FF0000;
|
||||
--color-red-04: #264F78;
|
||||
--color-red-04: #782626;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Green (color-green-*)
|
||||
@@ -170,10 +163,10 @@
|
||||
=================================== */
|
||||
--color-calendar-sunday: var(--color-red-01);
|
||||
--color-calendar-saturday: var(--color-blue-02);
|
||||
--color-calendar-today: var(--color-seti-orange);
|
||||
--color-calendar-today: var(--color-green-02);
|
||||
--color-calendar-selected: var(--color-blue-06);
|
||||
--color-calendar-range: rgba(66, 133, 244, 0.25);
|
||||
--color-calendar-range-hover: rgba(66, 133, 244, 0.35);
|
||||
--color-calendar-range: var(--color-blue-06);
|
||||
--color-calendar-range-hover: var(--color-blue-07);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - State (color-state-*)
|
||||
|
||||
@@ -41,67 +41,64 @@
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Blue (color-blue-*)
|
||||
라이트 테마에서는 약간 더 어두운 톤 사용
|
||||
라이트 테마에서는 검정 텍스트가 잘 보이도록 밝은 톤 사용
|
||||
=================================== */
|
||||
--color-blue-01: #0066CC;
|
||||
--color-blue-02: #0078D4;
|
||||
--color-blue-03: #106EBE;
|
||||
--color-blue-04: #005A9E;
|
||||
--color-blue-05: #004578;
|
||||
--color-blue-06: #003F6E;
|
||||
--color-blue-07: #003462;
|
||||
--color-blue-08: #002952;
|
||||
--color-blue-09: #001F3F;
|
||||
--color-blue-10: #001530;
|
||||
--color-blue-11: #000B1A;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Blue Light (color-blue-light-*)
|
||||
라이트 테마 선택 항목용 밝은 파란색
|
||||
=================================== */
|
||||
--color-blue-light-01: #B3D7FF;
|
||||
--color-blue-light-02: #8EC5FF;
|
||||
--color-blue-01: #DCEEFB;
|
||||
--color-blue-02: #B3D7F5;
|
||||
--color-blue-03: #8AC0EF;
|
||||
--color-blue-04: #62A8E8;
|
||||
--color-blue-05: #3A90E0;
|
||||
--color-blue-06: #2979C9;
|
||||
--color-blue-07: #1A66B3;
|
||||
--color-blue-08: #0D539C;
|
||||
--color-blue-09: #044185;
|
||||
--color-blue-10: #00306E;
|
||||
--color-blue-11: #002057;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Red (color-red-*)
|
||||
=================================== */
|
||||
--color-red-01: #D32F2F;
|
||||
--color-red-02: #B71C1C;
|
||||
--color-red-03: #E53935;
|
||||
--color-red-04: #C62828;
|
||||
--color-red-01: #F5A3A3;
|
||||
--color-red-02: #EF7B7B;
|
||||
--color-red-03: #F28C8C;
|
||||
--color-red-04: #E86A6A;
|
||||
--color-red-05: #F14C4C;
|
||||
--color-red-06: #C74E39;
|
||||
--color-red-07: #FF0000;
|
||||
--color-red-08: #782626;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Green (color-green-*)
|
||||
=================================== */
|
||||
--color-green-01: #388E3C;
|
||||
--color-green-02: #2E7D32;
|
||||
--color-green-03: #1B5E20;
|
||||
--color-green-04: #4CAF50;
|
||||
--color-green-05: #43A047;
|
||||
--color-green-01: #A5D6A7;
|
||||
--color-green-02: #81C784;
|
||||
--color-green-03: #66BB6A;
|
||||
--color-green-04: #B8E6B9;
|
||||
--color-green-05: #98D99A;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Purple (color-purple-*)
|
||||
=================================== */
|
||||
--color-purple-01: #7B1FA2;
|
||||
--color-purple-02: #6A1B9A;
|
||||
--color-purple-03: #9C27B0;
|
||||
--color-purple-04: #8E24AA;
|
||||
--color-purple-01: #CE93D8;
|
||||
--color-purple-02: #BA68C8;
|
||||
--color-purple-03: #D1A3DB;
|
||||
--color-purple-04: #C280CE;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Yellow (color-yellow-*)
|
||||
=================================== */
|
||||
--color-yellow-01: #F9A825;
|
||||
--color-yellow-02: #F57F17;
|
||||
--color-yellow-03: #FF8F00;
|
||||
--color-yellow-04: #FF6F00;
|
||||
--color-yellow-05: #FFD600;
|
||||
--color-yellow-01: #FFE082;
|
||||
--color-yellow-02: #FFD54F;
|
||||
--color-yellow-03: #FFCA28;
|
||||
--color-yellow-04: #FFC107;
|
||||
--color-yellow-05: #FFF176;
|
||||
|
||||
/* ===================================
|
||||
Accent Colors - Orange (color-orange-*)
|
||||
=================================== */
|
||||
--color-orange-01: #E65100;
|
||||
--color-orange-02: #EF6C00;
|
||||
--color-orange-03: #F57C00;
|
||||
--color-orange-01: #FFAB91;
|
||||
--color-orange-02: #FF9A76;
|
||||
--color-orange-03: #FFB74D;
|
||||
|
||||
/* ===================================
|
||||
Gray Colors (color-gray-*)
|
||||
@@ -113,17 +110,17 @@
|
||||
/* ===================================
|
||||
Seti Colors (color-seti-*)
|
||||
=================================== */
|
||||
--color-seti-blue: #1976D2;
|
||||
--color-seti-green: #689F38;
|
||||
--color-seti-orange: #E65100;
|
||||
--color-seti-pink: #D81B60;
|
||||
--color-seti-red: #C62828;
|
||||
--color-seti-steel: #455A64;
|
||||
--color-seti-yellow: #F9A825;
|
||||
--color-seti-purple: #7B1FA2;
|
||||
--color-seti-ignore: #78909C;
|
||||
--color-seti-blue: #64B5F6;
|
||||
--color-seti-green: #AED581;
|
||||
--color-seti-orange: #FFAB91;
|
||||
--color-seti-pink: #F48FB1;
|
||||
--color-seti-red: #EF9A9A;
|
||||
--color-seti-steel: #90A4AE;
|
||||
--color-seti-yellow: #FFE082;
|
||||
--color-seti-purple: #CE93D8;
|
||||
--color-seti-ignore: #B0BEC5;
|
||||
--color-seti-white: #ECEFF1;
|
||||
--color-seti-gray: #607D8B;
|
||||
--color-seti-gray: #90A4AE;
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - Text (color-text-*)
|
||||
@@ -143,7 +140,7 @@
|
||||
=================================== */
|
||||
--color-bg-base: var(--color-base-01);
|
||||
--color-bg-secondary: var(--color-base-03);
|
||||
--color-bg-tertiary: var(--color-base-08);
|
||||
--color-bg-tertiary: var(--color-base-06);
|
||||
--color-bg-elevated: var(--color-base-02);
|
||||
--color-bg-modal: var(--color-base-02);
|
||||
--color-bg-panel: var(--color-base-01);
|
||||
@@ -167,35 +164,35 @@
|
||||
--color-btn-hover: var(--color-base-05);
|
||||
--color-btn-pressed: var(--color-base-06);
|
||||
--color-btn-disabled: var(--color-base-03);
|
||||
--color-btn-primary: var(--color-blue-02);
|
||||
--color-btn-primary-hover: var(--color-blue-03);
|
||||
--color-btn-primary: var(--color-blue-03);
|
||||
--color-btn-primary-hover: var(--color-blue-04);
|
||||
--color-btn-secondary: var(--color-base-05);
|
||||
--color-btn-secondary-hover: var(--color-base-06);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - Calendar (color-calendar-*)
|
||||
=================================== */
|
||||
--color-calendar-sunday: var(--color-red-01);
|
||||
--color-calendar-saturday: var(--color-blue-02);
|
||||
--color-calendar-today: var(--color-orange-02);
|
||||
--color-calendar-sunday: var(--color-red-05);
|
||||
--color-calendar-saturday: var(--color-blue-05);
|
||||
--color-calendar-today: var(--color-green-02);
|
||||
--color-calendar-selected: var(--color-blue-02);
|
||||
--color-calendar-range: rgba(66, 133, 244, 0.15);
|
||||
--color-calendar-range-hover: rgba(66, 133, 244, 0.25);
|
||||
--color-calendar-range: var(--color-blue-02);
|
||||
--color-calendar-range-hover: var(--color-blue-03);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - State (color-state-*)
|
||||
=================================== */
|
||||
--color-state-success: var(--color-green-01);
|
||||
--color-state-success: var(--color-green-02);
|
||||
--color-state-warning: var(--color-orange-02);
|
||||
--color-state-error: var(--color-red-01);
|
||||
--color-state-info: var(--color-blue-02);
|
||||
--color-state-error: var(--color-red-03);
|
||||
--color-state-info: var(--color-blue-04);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - Vector (color-vector-*)
|
||||
=================================== */
|
||||
--color-vector-x: rgba(200, 60, 60, 1);
|
||||
--color-vector-y: rgba(60, 160, 60, 1);
|
||||
--color-vector-z: rgba(60, 60, 200, 1);
|
||||
--color-vector-x: rgba(239, 130, 130, 1);
|
||||
--color-vector-y: rgba(130, 210, 130, 1);
|
||||
--color-vector-z: rgba(130, 150, 239, 1);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - Scrollbar (color-scroller-*)
|
||||
@@ -216,8 +213,8 @@
|
||||
=================================== */
|
||||
--color-collection-item: rgba(255, 255, 255, 0);
|
||||
--color-collection-item-hover: var(--color-base-04);
|
||||
--color-collection-item-selected: var(--color-blue-light-01);
|
||||
--color-collection-item-selected-hover: var(--color-blue-light-02);
|
||||
--color-collection-item-selected: var(--color-blue-04);
|
||||
--color-collection-item-selected-hover: var(--color-blue-05);
|
||||
|
||||
/* ===================================
|
||||
Semantic Colors - Tree Checkmark (color-tree-checkmark-*)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<utk:UTKButton name="close-btn" variant="Text" icon-only="true" class="tree-window-close-button" />
|
||||
</ui:VisualElement>
|
||||
<ui:VisualElement name="search-container" class="search-container">
|
||||
<utk:UTKInputField name="search-field" placeholder-text="검색" class="search-field" />
|
||||
<utk:UTKInputField name="search-field" placeholder-text="Search..." class="search-field" />
|
||||
<utk:UTKButton name="clear-btn" class="search-clear-button" variant="Text" icon="Close" icon-size="12" icon-only="true" />
|
||||
</ui:VisualElement>
|
||||
<ui:TreeView name="main-tree-view" view-data-key="model-tree-view" fixed-item-height="20" auto-expand="false" item-template="project://database/Assets/Resources/UIToolkit/List/UTKTreeListItem.uxml" horizontal-scrolling="true" selection-type="Multiple" style="flex-grow: 1;" />
|
||||
|
||||
@@ -96,6 +96,10 @@ namespace UVC.Sample.UIToolkit
|
||||
_propertyWindow.OnPropertyButtonClicked += (id, actionName) =>
|
||||
{
|
||||
Debug.Log($"Button Clicked: {id} - Action: {actionName}");
|
||||
if(id == "string_with_btn" && actionName == "string_action")
|
||||
{
|
||||
_propertyWindow.SetPropertyValue("string_with_btn", "Button clicked!");
|
||||
}
|
||||
};
|
||||
|
||||
// 샘플 데이터 생성
|
||||
|
||||
@@ -15,6 +15,45 @@ public partial class UTKStyleGuideSample
|
||||
|
||||
private void InitializeInputFieldSample(VisualElement root)
|
||||
{
|
||||
// Validation - Email
|
||||
var emailInput = root.Q<UTKInputField>("input-validation-email");
|
||||
if (emailInput != null)
|
||||
{
|
||||
emailInput.ErrorMessage = "올바른 이메일 형식이 아닙니다.";
|
||||
emailInput.Validation = () => emailInput.Value.Contains("@") && emailInput.Value.Contains(".");
|
||||
}
|
||||
|
||||
// Validation - Required
|
||||
var requiredInput = root.Q<UTKInputField>("input-validation-required");
|
||||
if (requiredInput != null)
|
||||
{
|
||||
requiredInput.ErrorMessage = "이름은 필수 항목입니다.";
|
||||
requiredInput.Validation = () => !string.IsNullOrWhiteSpace(requiredInput.Value);
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var emailValid = emailInput?.Validate() ?? true;
|
||||
var nameValid = requiredInput?.Validate() ?? true;
|
||||
var allValid = emailValid && nameValid;
|
||||
Debug.Log($"Validation: {(allValid ? "All Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
emailInput?.ClearError();
|
||||
requiredInput?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 입력 필드
|
||||
var input = new UTKInputField();
|
||||
@@ -27,10 +66,6 @@ var password = new UTKInputField();
|
||||
password.label = ""비밀번호"";
|
||||
password.isPasswordField = true;
|
||||
|
||||
// 검증 오류 표시
|
||||
input.ErrorMessage = ""이름은 필수입니다."";
|
||||
input.ErrorMessage = """"; // 오류 제거
|
||||
|
||||
// 변형 스타일
|
||||
input.Variant = UTKInputField.InputFieldVariant.Outlined;
|
||||
|
||||
@@ -39,13 +74,34 @@ input.OnFocused += () => Debug.Log(""포커스"");
|
||||
input.OnBlurred += () => Debug.Log(""포커스 해제"");
|
||||
input.OnSubmit += (value) => Debug.Log($""제출: {value}"");
|
||||
|
||||
// 비활성화
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
// 1) ErrorMessage + Validation 함수 설정
|
||||
var emailInput = new UTKInputField(""이메일"", ""example@email.com"");
|
||||
emailInput.ErrorMessage = ""올바른 이메일 형식이 아닙니다."";
|
||||
emailInput.Validation = () => emailInput.Value.Contains(""@"");
|
||||
// → Submit(Enter) 또는 FocusOut 시 자동 검증
|
||||
// → 실패: 붉은 외곽선 + 에러 메시지 표시
|
||||
// → 통과: 에러 상태 자동 해제
|
||||
|
||||
// 2) 필수 입력 검증
|
||||
var nameInput = new UTKInputField(""이름"");
|
||||
nameInput.ErrorMessage = ""이름은 필수 항목입니다."";
|
||||
nameInput.Validation = () => !string.IsNullOrWhiteSpace(nameInput.Value);
|
||||
|
||||
// 3) 강제 검증 호출 (폼 제출 버튼 등에서 사용)
|
||||
bool isValid = nameInput.Validate(); // Validation 실행 + UI 업데이트
|
||||
if (!isValid) return;
|
||||
|
||||
// 4) 에러 수동 해제
|
||||
nameInput.ClearError();
|
||||
|
||||
// 5) 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
input.ErrorMessage = ""서버 오류가 발생했습니다."";
|
||||
input.ErrorMessage = """"; // 오류 제거
|
||||
|
||||
// 비활성화 / 읽기 전용
|
||||
input.IsEnabled = false;
|
||||
|
||||
// 읽기 전용
|
||||
input.isReadOnly = true;
|
||||
|
||||
// 여러 줄 입력
|
||||
input.multiline = true;",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
@@ -62,6 +118,9 @@ input.multiline = true;",
|
||||
<!-- 여러 줄 입력 -->
|
||||
<utk:UTKInputField label=""설명"" multiline=""true"" />
|
||||
|
||||
<!-- 에러 메시지 설정 (C#에서 Validation 설정 권장) -->
|
||||
<utk:UTKInputField label=""이메일"" error-message=""올바른 이메일 형식이 아닙니다."" />
|
||||
|
||||
<!-- 비활성화 -->
|
||||
<utk:UTKInputField label=""읽기전용"" is-enabled=""false"" value=""수정 불가"" />
|
||||
|
||||
@@ -70,21 +129,78 @@ input.multiline = true;",
|
||||
|
||||
private void InitializeIntegerFieldSample(VisualElement root)
|
||||
{
|
||||
// Validation - Range (1~150)
|
||||
var rangeField = root.Q<UTKIntegerField>("int-validation-range");
|
||||
if (rangeField != null)
|
||||
{
|
||||
rangeField.ErrorMessage = "나이는 1~150 사이여야 합니다.";
|
||||
rangeField.Validation = () => rangeField.Value >= 1 && rangeField.Value <= 150;
|
||||
}
|
||||
|
||||
// Validation - Positive Only
|
||||
var positiveField = root.Q<UTKIntegerField>("int-validation-positive");
|
||||
if (positiveField != null)
|
||||
{
|
||||
positiveField.ErrorMessage = "수량은 0보다 커야 합니다.";
|
||||
positiveField.Validation = () => positiveField.Value > 0;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("int-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var rangeValid = rangeField?.Validate() ?? true;
|
||||
var positiveValid = positiveField?.Validate() ?? true;
|
||||
var allValid = rangeValid && positiveValid;
|
||||
Debug.Log($"IntegerField Validation: {(allValid ? "All Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
rangeField?.ClearError();
|
||||
positiveField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 정수 필드
|
||||
var intField = new UTKIntegerField(""나이"");
|
||||
intField.Value = 25;
|
||||
intField.OnValueChanged += (value) => Debug.Log($""나이: {value}"");
|
||||
|
||||
// 범위 제한 (이벤트로 처리)
|
||||
intField.OnValueChanged += (value) =>
|
||||
{
|
||||
if (value < 0) intField.Value = 0;
|
||||
if (value > 150) intField.Value = 150;
|
||||
};
|
||||
|
||||
// 비활성화
|
||||
intField.IsEnabled = false;",
|
||||
intField.IsEnabled = false;
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
// 1) ErrorMessage + Validation 함수 설정
|
||||
var ageField = new UTKIntegerField(""나이"", 0);
|
||||
ageField.ErrorMessage = ""나이는 1~150 사이여야 합니다."";
|
||||
ageField.Validation = () => ageField.Value >= 1 && ageField.Value <= 150;
|
||||
// → FocusOut 또는 Enter 시 자동 검증
|
||||
// → 실패: 붉은 외곽선 + 에러 메시지 표시
|
||||
// → 통과: 에러 상태 자동 해제
|
||||
|
||||
// 2) 양수만 허용
|
||||
var qtyField = new UTKIntegerField(""수량"", 0);
|
||||
qtyField.ErrorMessage = ""수량은 0보다 커야 합니다."";
|
||||
qtyField.Validation = () => qtyField.Value > 0;
|
||||
|
||||
// 3) 강제 검증 호출 (폼 제출 버튼 등에서 사용)
|
||||
bool isValid = ageField.Validate(); // Validation 실행 + UI 업데이트
|
||||
if (!isValid) return;
|
||||
|
||||
// 4) 에러 수동 해제
|
||||
ageField.ClearError();
|
||||
|
||||
// 5) 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
intField.ErrorMessage = ""서버 오류가 발생했습니다."";
|
||||
intField.ErrorMessage = """"; // 오류 제거",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -102,6 +218,45 @@ intField.IsEnabled = false;",
|
||||
|
||||
private void InitializeLongFieldSample(VisualElement root)
|
||||
{
|
||||
// Validation - Range (0 ~ 1,000,000)
|
||||
var rangeField = root.Q<UTKLongField>("long-validation-range");
|
||||
if (rangeField != null)
|
||||
{
|
||||
rangeField.ErrorMessage = "파일 크기는 0 ~ 1,000,000 사이여야 합니다.";
|
||||
rangeField.Validation = () => rangeField.Value >= 0 && rangeField.Value <= 1000000;
|
||||
}
|
||||
|
||||
// Validation - Positive Only
|
||||
var positiveField = root.Q<UTKLongField>("long-validation-positive");
|
||||
if (positiveField != null)
|
||||
{
|
||||
positiveField.ErrorMessage = "개수는 0보다 커야 합니다.";
|
||||
positiveField.Validation = () => positiveField.Value > 0;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("long-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var rangeValid = rangeField?.Validate() ?? true;
|
||||
var positiveValid = positiveField?.Validate() ?? true;
|
||||
var allValid = rangeValid && positiveValid;
|
||||
Debug.Log($"LongField Validation: {(allValid ? "All Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
rangeField?.ClearError();
|
||||
positiveField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 Long 필드
|
||||
var longField = new UTKLongField(""파일 크기"");
|
||||
@@ -109,7 +264,19 @@ longField.Value = 1073741824; // 1GB
|
||||
longField.OnValueChanged += (value) => Debug.Log($""크기: {value} bytes"");
|
||||
|
||||
// 비활성화
|
||||
longField.IsEnabled = false;",
|
||||
longField.IsEnabled = false;
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
// 1) ErrorMessage + Validation 함수 설정
|
||||
var sizeField = new UTKLongField(""파일 크기"", 0);
|
||||
sizeField.ErrorMessage = ""파일 크기는 0 ~ 1,000,000 사이여야 합니다."";
|
||||
sizeField.Validation = () => sizeField.Value >= 0 && sizeField.Value <= 1000000;
|
||||
|
||||
// 2) 강제 검증 호출
|
||||
bool isValid = sizeField.Validate();
|
||||
|
||||
// 3) 에러 수동 해제
|
||||
sizeField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -127,21 +294,66 @@ longField.IsEnabled = false;",
|
||||
|
||||
private void InitializeFloatFieldSample(VisualElement root)
|
||||
{
|
||||
// Validation - Range (0 ~ 100)
|
||||
var rangeField = root.Q<UTKFloatField>("float-validation-range");
|
||||
if (rangeField != null)
|
||||
{
|
||||
rangeField.ErrorMessage = "속도는 0 ~ 100 사이여야 합니다.";
|
||||
rangeField.Validation = () => rangeField.Value >= 0f && rangeField.Value <= 100f;
|
||||
}
|
||||
|
||||
// Validation - Positive Only
|
||||
var positiveField = root.Q<UTKFloatField>("float-validation-positive");
|
||||
if (positiveField != null)
|
||||
{
|
||||
positiveField.ErrorMessage = "무게는 0보다 커야 합니다.";
|
||||
positiveField.Validation = () => positiveField.Value > 0f;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("float-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var rangeValid = rangeField?.Validate() ?? true;
|
||||
var positiveValid = positiveField?.Validate() ?? true;
|
||||
var allValid = rangeValid && positiveValid;
|
||||
Debug.Log($"FloatField Validation: {(allValid ? "All Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
rangeField?.ClearError();
|
||||
positiveField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 Float 필드
|
||||
var floatField = new UTKFloatField(""속도"");
|
||||
floatField.Value = 9.8f;
|
||||
floatField.OnValueChanged += (value) => Debug.Log($""속도: {value}"");
|
||||
|
||||
// 범위 제한 (이벤트로 처리)
|
||||
floatField.OnValueChanged += (value) =>
|
||||
{
|
||||
if (value < 0f) floatField.Value = 0f;
|
||||
if (value > 100f) floatField.Value = 100f;
|
||||
};
|
||||
|
||||
// 비활성화
|
||||
floatField.IsEnabled = false;",
|
||||
floatField.IsEnabled = false;
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
// 1) ErrorMessage + Validation 함수 설정
|
||||
var speedField = new UTKFloatField(""속도"", 0f);
|
||||
speedField.ErrorMessage = ""속도는 0 ~ 100 사이여야 합니다."";
|
||||
speedField.Validation = () => speedField.Value >= 0f && speedField.Value <= 100f;
|
||||
// → FocusOut 또는 Enter 시 자동 검증
|
||||
|
||||
// 2) 강제 검증 호출
|
||||
bool isValid = speedField.Validate();
|
||||
|
||||
// 3) 에러 수동 해제
|
||||
speedField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -159,6 +371,45 @@ floatField.IsEnabled = false;",
|
||||
|
||||
private void InitializeDoubleFieldSample(VisualElement root)
|
||||
{
|
||||
// Validation - Range (0.0 ~ 100.0)
|
||||
var rangeField = root.Q<UTKDoubleField>("double-validation-range");
|
||||
if (rangeField != null)
|
||||
{
|
||||
rangeField.ErrorMessage = "값은 0.0 ~ 100.0 사이여야 합니다.";
|
||||
rangeField.Validation = () => rangeField.Value >= 0.0 && rangeField.Value <= 100.0;
|
||||
}
|
||||
|
||||
// Validation - Positive Only
|
||||
var positiveField = root.Q<UTKDoubleField>("double-validation-positive");
|
||||
if (positiveField != null)
|
||||
{
|
||||
positiveField.ErrorMessage = "거리는 0보다 커야 합니다.";
|
||||
positiveField.Validation = () => positiveField.Value > 0.0;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("double-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var rangeValid = rangeField?.Validate() ?? true;
|
||||
var positiveValid = positiveField?.Validate() ?? true;
|
||||
var allValid = rangeValid && positiveValid;
|
||||
Debug.Log($"DoubleField Validation: {(allValid ? "All Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
rangeField?.ClearError();
|
||||
positiveField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 Double 필드
|
||||
var doubleField = new UTKDoubleField(""정밀도"");
|
||||
@@ -166,7 +417,20 @@ doubleField.Value = 3.141592653589793;
|
||||
doubleField.OnValueChanged += (value) => Debug.Log($""값: {value}"");
|
||||
|
||||
// 비활성화
|
||||
doubleField.IsEnabled = false;",
|
||||
doubleField.IsEnabled = false;
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
// 1) ErrorMessage + Validation 함수 설정
|
||||
var percentField = new UTKDoubleField(""퍼센트"", 0);
|
||||
percentField.ErrorMessage = ""값은 0.0 ~ 100.0 사이여야 합니다."";
|
||||
percentField.Validation = () => percentField.Value >= 0.0 && percentField.Value <= 100.0;
|
||||
// → FocusOut 또는 Enter 시 자동 검증
|
||||
|
||||
// 2) 강제 검증 호출
|
||||
bool isValid = percentField.Validate();
|
||||
|
||||
// 3) 에러 수동 해제
|
||||
percentField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -193,6 +457,39 @@ doubleField.IsEnabled = false;",
|
||||
disabledRow.Add(stepper);
|
||||
}
|
||||
|
||||
// Validation - Even Number Only
|
||||
UTKIntStepper? validationStepper = null;
|
||||
var validationRow = root.Q<VisualElement>("stepper-validation-row");
|
||||
if (validationRow != null)
|
||||
{
|
||||
validationStepper = new UTKIntStepper(0, 100, 1, 1);
|
||||
validationStepper.style.width = 150;
|
||||
validationStepper.ErrorMessage = "짝수만 입력 가능합니다.";
|
||||
validationStepper.Validation = () => validationStepper.Value % 2 == 0;
|
||||
validationRow.Add(validationStepper);
|
||||
validationStepper.Validate();
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("stepper-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationStepper?.Validate() ?? true;
|
||||
Debug.Log($"IntStepper Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationStepper?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var stepper = new UTKIntStepper(minValue: 0, maxValue: 100, value: 50, step: 1);
|
||||
@@ -213,7 +510,15 @@ disabledStepper.SetEnabled(false);
|
||||
stepper.Value = 75;
|
||||
stepper.MinValue = 10;
|
||||
stepper.MaxValue = 90;
|
||||
stepper.Step = 2;",
|
||||
stepper.Step = 2;
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var evenStepper = new UTKIntStepper(0, 100, 0, 1);
|
||||
evenStepper.ErrorMessage = ""짝수만 입력 가능합니다."";
|
||||
evenStepper.Validation = () => evenStepper.Value % 2 == 0;
|
||||
|
||||
bool isValid = evenStepper.Validate();
|
||||
evenStepper.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -232,6 +537,105 @@ stepper.Step = 2;",
|
||||
<!-- 커스텀 스타일 -->
|
||||
<utk:UTKIntStepper class=""custom-stepper"" style=""width: 150px;"" />
|
||||
|
||||
</ui:UXML>");
|
||||
}
|
||||
|
||||
private void InitializeFloatStepperSample(VisualElement root)
|
||||
{
|
||||
var disabledRow = root.Q<VisualElement>("float-stepper-disabled-row");
|
||||
if (disabledRow != null)
|
||||
{
|
||||
var stepper = new UTKFloatStepper(0f, 10f, 3.5f, 0.1f);
|
||||
stepper.style.width = 150;
|
||||
stepper.SetEnabled(false);
|
||||
disabledRow.Add(stepper);
|
||||
}
|
||||
|
||||
// Validation - Non-Zero
|
||||
UTKFloatStepper? validationStepper = null;
|
||||
var validationRow = root.Q<VisualElement>("float-stepper-validation-row");
|
||||
if (validationRow != null)
|
||||
{
|
||||
validationStepper = new UTKFloatStepper(0f, 10f, 0f, 0.1f);
|
||||
validationStepper.style.width = 150;
|
||||
validationStepper.ErrorMessage = "값은 0이 될 수 없습니다.";
|
||||
validationStepper.Validation = () => validationStepper.Value != 0f;
|
||||
validationRow.Add(validationStepper);
|
||||
validationStepper.Validate();
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("float-stepper-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationStepper?.Validate() ?? true;
|
||||
Debug.Log($"FloatStepper Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationStepper?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var stepper = new UTKFloatStepper(minValue: 0f, maxValue: 10f, initialValue: 5.5f, step: 0.1f);
|
||||
stepper.OnValueChanged += (newValue) => Debug.Log($""Value: {newValue}"");
|
||||
|
||||
// Step 설정
|
||||
var stepper05 = new UTKFloatStepper(0f, 10f, 2.5f, 0.5f); // Step 0.5씩 증감
|
||||
|
||||
// Wrap Around (최소/최대값에서 순환)
|
||||
var wrapStepper = new UTKFloatStepper(0f, 1f, 0.5f, 0.1f);
|
||||
wrapStepper.WrapAround = true; // 0.0 -> -0.1 시 1.0으로 순환
|
||||
|
||||
// 비활성화
|
||||
var disabledStepper = new UTKFloatStepper(0f, 10f, 3.5f, 0.1f);
|
||||
disabledStepper.SetEnabled(false);
|
||||
|
||||
// 속성 변경
|
||||
stepper.Value = 7.5f;
|
||||
stepper.MinValue = 1f;
|
||||
stepper.MaxValue = 9f;
|
||||
stepper.Step = 0.25f;
|
||||
|
||||
// 프로그래밍 방식으로 값 변경
|
||||
stepper.Increment(); // Step만큼 증가
|
||||
stepper.Decrement(); // Step만큼 감소
|
||||
stepper.SetValue(4.2f); // 직접 설정
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var nonZeroStepper = new UTKFloatStepper(0f, 10f, 0f, 0.1f);
|
||||
nonZeroStepper.ErrorMessage = ""값은 0이 될 수 없습니다."";
|
||||
nonZeroStepper.Validation = () => nonZeroStepper.Value != 0f;
|
||||
|
||||
bool isValid = nonZeroStepper.Validate();
|
||||
nonZeroStepper.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
<!-- Step 0.1 -->
|
||||
<utk:UTKFloatStepper min-value=""0"" max-value=""10"" value=""5.5"" step=""0.1"" />
|
||||
|
||||
<!-- Step 0.5 -->
|
||||
<utk:UTKFloatStepper min-value=""0"" max-value=""10"" value=""2.5"" step=""0.5"" />
|
||||
|
||||
<!-- Wrap Around -->
|
||||
<utk:UTKFloatStepper min-value=""0"" max-value=""1"" value=""0.5"" step=""0.1"" wrap-around=""true"" />
|
||||
|
||||
<!-- 비활성화 -->
|
||||
<utk:UTKFloatStepper min-value=""0"" max-value=""10"" value=""3.5"" step=""0.1"" is-enabled=""false"" />
|
||||
|
||||
<!-- 커스텀 스타일 -->
|
||||
<utk:UTKFloatStepper class=""custom-stepper"" style=""width: 150px;"" />
|
||||
|
||||
</ui:UXML>");
|
||||
}
|
||||
|
||||
@@ -243,6 +647,34 @@ stepper.Step = 2;",
|
||||
field.Value = new Vector2(100, 200);
|
||||
}
|
||||
|
||||
// Validation - Non-Zero
|
||||
var validationField = root.Q<UTKVector2Field>("vec2-validation");
|
||||
if (validationField != null)
|
||||
{
|
||||
validationField.ErrorMessage = "방향 벡터는 (0,0)이 될 수 없습니다.";
|
||||
validationField.Validation = () => validationField.Value != Vector2.zero;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("vec2-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationField?.Validate() ?? true;
|
||||
Debug.Log($"Vector2Field Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var positionField = new UTKVector2Field(""Position"");
|
||||
@@ -255,7 +687,15 @@ field.label = """";
|
||||
|
||||
// 읽기 전용
|
||||
var readOnlyField = new UTKVector2Field(""ReadOnly"");
|
||||
readOnlyField.SetEnabled(false);",
|
||||
readOnlyField.SetEnabled(false);
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var dirField = new UTKVector2Field(""Direction"");
|
||||
dirField.ErrorMessage = ""방향 벡터는 (0,0)이 될 수 없습니다."";
|
||||
dirField.Validation = () => dirField.Value != Vector2.zero;
|
||||
|
||||
bool isValid = dirField.Validate();
|
||||
dirField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -279,6 +719,34 @@ readOnlyField.SetEnabled(false);",
|
||||
field.Value = new Vector3(10, 20, 30);
|
||||
}
|
||||
|
||||
// Validation - Non-Zero
|
||||
var validationField = root.Q<UTKVector3Field>("vec3-validation");
|
||||
if (validationField != null)
|
||||
{
|
||||
validationField.ErrorMessage = "스케일 벡터는 (0,0,0)이 될 수 없습니다.";
|
||||
validationField.Validation = () => validationField.Value != Vector3.zero;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("vec3-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationField?.Validate() ?? true;
|
||||
Debug.Log($"Vector3Field Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var positionField = new UTKVector3Field(""Position"");
|
||||
@@ -291,7 +759,15 @@ rotationField.Value = new Vector3(0, 90, 0);
|
||||
|
||||
// 비활성화
|
||||
var disabledField = new UTKVector3Field(""Disabled"");
|
||||
disabledField.SetEnabled(false);",
|
||||
disabledField.SetEnabled(false);
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var scaleField = new UTKVector3Field(""Scale"");
|
||||
scaleField.ErrorMessage = ""스케일 벡터는 (0,0,0)이 될 수 없습니다."";
|
||||
scaleField.Validation = () => scaleField.Value != Vector3.zero;
|
||||
|
||||
bool isValid = scaleField.Validate();
|
||||
scaleField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -315,6 +791,42 @@ disabledField.SetEnabled(false);",
|
||||
field.Value = new Vector4(1, 0.5f, 0.25f, 1);
|
||||
}
|
||||
|
||||
// Validation - 0~1 Range (RGBA)
|
||||
var validationField = root.Q<UTKVector4Field>("vec4-validation");
|
||||
if (validationField != null)
|
||||
{
|
||||
validationField.Value = new Vector4(1, 0.5f, 0.25f, 1);
|
||||
validationField.ErrorMessage = "RGBA 값은 모두 0~1 사이여야 합니다.";
|
||||
validationField.Validation = () =>
|
||||
{
|
||||
var v = validationField.Value;
|
||||
return v.x >= 0f && v.x <= 1f &&
|
||||
v.y >= 0f && v.y <= 1f &&
|
||||
v.z >= 0f && v.z <= 1f &&
|
||||
v.w >= 0f && v.w <= 1f;
|
||||
};
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("vec4-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationField?.Validate() ?? true;
|
||||
Debug.Log($"Vector4Field Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var colorField = new UTKVector4Field(""Color"");
|
||||
@@ -328,7 +840,20 @@ quaternionField.Value = new Vector4(quat.x, quat.y, quat.z, quat.w);
|
||||
|
||||
// 비활성화
|
||||
var disabledField = new UTKVector4Field(""Disabled"");
|
||||
disabledField.SetEnabled(false);",
|
||||
disabledField.SetEnabled(false);
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var rgbaField = new UTKVector4Field(""RGBA"");
|
||||
rgbaField.ErrorMessage = ""RGBA 값은 모두 0~1 사이여야 합니다."";
|
||||
rgbaField.Validation = () =>
|
||||
{
|
||||
var v = rgbaField.Value;
|
||||
return v.x >= 0f && v.x <= 1f && v.y >= 0f && v.y <= 1f
|
||||
&& v.z >= 0f && v.z <= 1f && v.w >= 0f && v.w <= 1f;
|
||||
};
|
||||
|
||||
bool isValid = rgbaField.Validate();
|
||||
rgbaField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -352,6 +877,35 @@ disabledField.SetEnabled(false);",
|
||||
field.Value = new Rect(10, 20, 100, 50);
|
||||
}
|
||||
|
||||
// Validation - Positive Size
|
||||
var validationField = root.Q<UTKRectField>("rect-validation");
|
||||
if (validationField != null)
|
||||
{
|
||||
validationField.ErrorMessage = "Width와 Height는 0보다 커야 합니다.";
|
||||
validationField.Validation = () =>
|
||||
validationField.Value.width > 0 && validationField.Value.height > 0;
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("rect-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationField?.Validate() ?? true;
|
||||
Debug.Log($"RectField Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var areaField = new UTKRectField(""Area"");
|
||||
@@ -364,7 +918,16 @@ screenArea.Value = new Rect(0, 0, Screen.width, Screen.height);
|
||||
|
||||
// 비활성화
|
||||
var disabledField = new UTKRectField(""Disabled"");
|
||||
disabledField.SetEnabled(false);",
|
||||
disabledField.SetEnabled(false);
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var viewportField = new UTKRectField(""Viewport"");
|
||||
viewportField.ErrorMessage = ""Width와 Height는 0보다 커야 합니다."";
|
||||
viewportField.Validation = () =>
|
||||
viewportField.Value.width > 0 && viewportField.Value.height > 0;
|
||||
|
||||
bool isValid = viewportField.Validate();
|
||||
viewportField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
@@ -388,6 +951,38 @@ disabledField.SetEnabled(false);",
|
||||
field.Value = new Bounds(new Vector3(0, 1, 0), new Vector3(2, 2, 2));
|
||||
}
|
||||
|
||||
// Validation - Positive Extents
|
||||
var validationField = root.Q<UTKBoundsField>("bounds-validation");
|
||||
if (validationField != null)
|
||||
{
|
||||
validationField.ErrorMessage = "Extents는 모두 0보다 커야 합니다.";
|
||||
validationField.Validation = () =>
|
||||
{
|
||||
var ext = validationField.Value.extents;
|
||||
return ext.x > 0f && ext.y > 0f && ext.z > 0f;
|
||||
};
|
||||
}
|
||||
|
||||
// Validate All / Clear Errors 버튼
|
||||
var buttonRow = root.Q<VisualElement>("bounds-validation-button-row");
|
||||
if (buttonRow != null)
|
||||
{
|
||||
var validateBtn = new UTKButton("Validate All", variant: UTKButton.ButtonVariant.Primary);
|
||||
validateBtn.OnClicked += () =>
|
||||
{
|
||||
var isValid = validationField?.Validate() ?? true;
|
||||
Debug.Log($"BoundsField Validation: {(isValid ? "Passed" : "Has Errors")}");
|
||||
};
|
||||
buttonRow.Add(validateBtn);
|
||||
|
||||
var clearBtn = new UTKButton("Clear Errors");
|
||||
clearBtn.OnClicked += () =>
|
||||
{
|
||||
validationField?.ClearError();
|
||||
};
|
||||
buttonRow.Add(clearBtn);
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 기본 사용법
|
||||
var boundsField = new UTKBoundsField(""Bounds"");
|
||||
@@ -402,7 +997,19 @@ collisionField.Value = new Bounds(Vector3.zero, Vector3.one * 5);
|
||||
|
||||
// 비활성화
|
||||
var disabledField = new UTKBoundsField(""Disabled"");
|
||||
disabledField.SetEnabled(false);",
|
||||
disabledField.SetEnabled(false);
|
||||
|
||||
// ── Validation (입력 검증) ──────────────────────
|
||||
var colliderField = new UTKBoundsField(""Collider"");
|
||||
colliderField.ErrorMessage = ""Extents는 모두 0보다 커야 합니다."";
|
||||
colliderField.Validation = () =>
|
||||
{
|
||||
var ext = colliderField.Value.extents;
|
||||
return ext.x > 0f && ext.y > 0f && ext.z > 0f;
|
||||
};
|
||||
|
||||
bool isValid = colliderField.Validate();
|
||||
colliderField.ClearError();",
|
||||
uxmlCode: @"<!-- 네임스페이스 선언 -->
|
||||
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
||||
["UTKRectField"] = "UIToolkit/Sample/Input/UTKRectFieldSample",
|
||||
["UTKBoundsField"] = "UIToolkit/Sample/Input/UTKBoundsFieldSample",
|
||||
["UTKIntStepper"] = "UIToolkit/Sample/Input/UTKIntStepperSample",
|
||||
["UTKFloatStepper"] = "UIToolkit/Sample/Input/UTKFloatStepperSample",
|
||||
// Slider
|
||||
["UTKSlider"] = "UIToolkit/Sample/Slider/UTKSliderSample",
|
||||
["UTKSliderInt"] = "UIToolkit/Sample/Slider/UTKSliderIntSample",
|
||||
@@ -111,7 +112,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
||||
{
|
||||
["Icon"] = new[] { "UTKMaterialIcons", "UTKImageIcons", "UTKImage" },
|
||||
["Button"] = new[] { "UTKButton", "UTKCheckBox", "UTKToggle", "UTKRadioButton", "UTKToggleButtonGroup" },
|
||||
["Input"] = new[] { "UTKInputField", "UTKIntegerField", "UTKLongField", "UTKFloatField", "UTKDoubleField", "UTKVector2Field", "UTKVector3Field", "UTKVector4Field", "UTKRectField", "UTKBoundsField", "UTKIntStepper" },
|
||||
["Input"] = new[] { "UTKInputField", "UTKIntegerField", "UTKLongField", "UTKFloatField", "UTKDoubleField", "UTKVector2Field", "UTKVector3Field", "UTKVector4Field", "UTKRectField", "UTKBoundsField", "UTKIntStepper", "UTKFloatStepper" },
|
||||
["Slider"] = new[] { "UTKSlider", "UTKSliderInt", "UTKMinMaxSlider", "UTKProgressBar" },
|
||||
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown", "UTKMultiSelectDropdown" },
|
||||
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
|
||||
@@ -451,6 +452,9 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
||||
case "UTKIntStepper":
|
||||
InitializeNumberStepperSample(root);
|
||||
break;
|
||||
case "UTKFloatStepper":
|
||||
InitializeFloatStepperSample(root);
|
||||
break;
|
||||
case "UTKVector2Field":
|
||||
InitializeVector2FieldSample(root);
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,10 @@ namespace UVC.UIToolkit
|
||||
/// <para>- Center: 경계 상자의 중심점 (Vector3)</para>
|
||||
/// <para>- Extents: 중심에서 각 축 방향으로의 거리 (Vector3), Size의 절반 값</para>
|
||||
/// <para>- Size: 경계 상자의 전체 크기 (Extents * 2)</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
@@ -52,6 +56,26 @@ namespace UVC.UIToolkit
|
||||
/// readOnlyField.Value = new Bounds(Vector3.zero, Vector3.one);
|
||||
/// readOnlyField.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var boundsField = new UTKBoundsField("경계");
|
||||
/// boundsField.ErrorMessage = "크기는 양수여야 합니다.";
|
||||
/// boundsField.Validation = () => boundsField.Value.size.x > 0 && boundsField.Value.size.y > 0 && boundsField.Value.size.z > 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = boundsField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// boundsField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// boundsField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// boundsField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <!-- 네임스페이스 선언 -->
|
||||
@@ -70,6 +94,9 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <!-- 읽기 전용 -->
|
||||
/// <utk:UTKBoundsField label="고정 경계" is-readonly="true" />
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKBoundsField label="경계" error-message="크기는 양수여야 합니다." />
|
||||
/// </UXML>
|
||||
/// </code>
|
||||
/// <para><b>실제 활용 예시:</b></para>
|
||||
@@ -102,6 +129,9 @@ namespace UVC.UIToolkit
|
||||
private string _xLabel = "X";
|
||||
private string _yLabel = "Y";
|
||||
private string _zLabel = "Z";
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -202,6 +232,27 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-boundsfield--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-boundsfield--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -238,6 +289,7 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<Bounds>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -303,6 +355,73 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-boundsfield--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-boundsfield--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-boundsfield__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -314,8 +433,11 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<Bounds>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>과학 계산, 금융 데이터, 정밀 측정값에 사용</description></item>
|
||||
/// <item><description>float보다 약 2배 정밀도 (유효숫자 15-17자리)</description></item>
|
||||
/// <item><description>일반 게임 로직에는 UTKFloatField로 충분</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// <para><b>float vs double:</b></para>
|
||||
/// <list type="bullet">
|
||||
@@ -44,6 +46,20 @@ namespace UVC.UIToolkit
|
||||
/// double currentValue = doubleField.Value;
|
||||
/// doubleField.Value = 127.9780;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var precisionField = new UTKDoubleField("정밀 값", 0);
|
||||
/// precisionField.ErrorMessage = "값은 0보다 커야 합니다.";
|
||||
/// precisionField.Validation = () => precisionField.Value > 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
///
|
||||
/// // 강제 검증 호출
|
||||
/// bool isValid = precisionField.Validate();
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// precisionField.ClearError();
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 Double 필드 -->
|
||||
@@ -72,6 +88,9 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -87,6 +106,27 @@ namespace UVC.UIToolkit
|
||||
set => this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-double-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -133,6 +173,8 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<double>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -165,6 +207,81 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-double-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-double-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-double-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -176,8 +293,12 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<double>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>가격, 속도, 거리 등 실수 값에 적합</description></item>
|
||||
/// <item><description>더 높은 정밀도가 필요하면 UTKDoubleField 사용</description></item>
|
||||
/// <item><description>정수만 필요하면 UTKIntegerField 사용</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// <para><b>주의:</b> float 리터럴은 숫자 뒤에 'f'를 붙여야 합니다 (예: 3.14f)</para>
|
||||
/// </remarks>
|
||||
@@ -44,6 +46,31 @@ namespace UVC.UIToolkit
|
||||
/// // 비활성화
|
||||
/// floatField.IsEnabled = false;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var speedField = new UTKFloatField("속도", 0f);
|
||||
/// speedField.ErrorMessage = "속도는 0보다 커야 합니다.";
|
||||
/// speedField.Validation = () => speedField.Value > 0f;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 범위 검증
|
||||
/// var temperatureField = new UTKFloatField("온도", 20f);
|
||||
/// temperatureField.ErrorMessage = "온도는 -40 ~ 60 사이여야 합니다.";
|
||||
/// temperatureField.Validation = () => temperatureField.Value >= -40f && temperatureField.Value <= 60f;
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = speedField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// speedField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// floatField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// floatField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 실수 필드 -->
|
||||
@@ -56,6 +83,8 @@ namespace UVC.UIToolkit
|
||||
/// <code>
|
||||
/// // 캐릭터 이동 속도 편집
|
||||
/// var moveSpeedField = new UTKFloatField("이동 속도", character.MoveSpeed);
|
||||
/// moveSpeedField.ErrorMessage = "이동 속도는 0 이상이어야 합니다.";
|
||||
/// moveSpeedField.Validation = () => moveSpeedField.Value >= 0f;
|
||||
/// moveSpeedField.OnValueChanged += (newSpeed) => {
|
||||
/// character.MoveSpeed = Mathf.Clamp(newSpeed, 0f, 100f);
|
||||
/// };
|
||||
@@ -71,6 +100,9 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -86,6 +118,27 @@ namespace UVC.UIToolkit
|
||||
set => this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-float-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -132,6 +185,8 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<float>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -164,6 +219,81 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-float-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-float-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-float-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -175,8 +305,12 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<float>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>증감 단위 설정 (Step)</description></item>
|
||||
/// <item><description>순환 모드 (WrapAround) - 최대에서 최소로, 최소에서 최대로</description></item>
|
||||
/// </list>
|
||||
/// <para><b>Validation (유효성 검사):</b></para>
|
||||
/// <para>
|
||||
/// <see cref="Validation"/> 속성에 검증 함수를 설정하면,
|
||||
/// 포커스 아웃 시 자동으로 유효성 검사를 수행합니다.
|
||||
/// 검증 실패 시 <see cref="ErrorMessage"/>에 설정된 메시지가 표시됩니다.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
@@ -56,6 +62,12 @@ namespace UVC.UIToolkit
|
||||
/// var readOnlyStepper = new UTKFloatStepper(0f, 10f, 5f, 0.1f);
|
||||
/// readOnlyStepper.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation 사용:</b></para>
|
||||
/// <code>
|
||||
/// var volumeStepper = new UTKFloatStepper(0f, 1f, 0.5f, 0.1f);
|
||||
/// volumeStepper.ErrorMessage = "볼륨은 0~1 사이여야 합니다.";
|
||||
/// volumeStepper.Validation = () => volumeStepper.Value >= 0f && volumeStepper.Value <= 1f;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 스테퍼 -->
|
||||
@@ -69,6 +81,9 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <!-- 읽기 전용 -->
|
||||
/// <utk:UTKFloatStepper value="5.0" is-readonly="true" />
|
||||
///
|
||||
/// <!-- 에러 메시지 설정 -->
|
||||
/// <utk:UTKFloatStepper value="0.5" error-message="볼륨은 0~1 사이여야 합니다." />
|
||||
/// ]]></code>
|
||||
/// <para><b>실제 활용 예시 (볼륨 조절):</b></para>
|
||||
/// <code>
|
||||
@@ -142,6 +157,20 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-number-stepper--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-number-stepper--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
@@ -154,10 +183,29 @@ namespace UVC.UIToolkit
|
||||
private bool _wrapAround;
|
||||
private bool _isUpdating;
|
||||
private bool _isHovered;
|
||||
private string _errorMessage = "";
|
||||
|
||||
private TextField? _textField;
|
||||
private Button? _upButton;
|
||||
private Button? _downButton;
|
||||
|
||||
/// <summary>유효성 검사 함수. true 반환 시 유효, false 반환 시 에러 표시</summary>
|
||||
private Func<bool>? _validation;
|
||||
|
||||
/// <summary>에러 메시지 표시용 레이블</summary>
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// 유효성 검사 함수.
|
||||
/// 포커스 아웃 시 자동으로 호출되며, false 반환 시 에러 스타일과 메시지가 표시됩니다.
|
||||
/// </summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -241,6 +289,7 @@ namespace UVC.UIToolkit
|
||||
if (notify)
|
||||
{
|
||||
OnValueChanged?.Invoke(_value);
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,6 +337,18 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
_textField?.Focus();
|
||||
}
|
||||
|
||||
/// <summary>강제로 Validation을 실행하여 에러 상태를 업데이트합니다.</summary>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods - UI Creation
|
||||
@@ -344,6 +405,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
_textField?.RegisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||
_textField?.RegisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||
_textField?.RegisterCallback<FocusOutEvent>(OnTextFieldFocusOut);
|
||||
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||
@@ -397,6 +459,12 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>텍스트 필드 포커스 아웃 시 유효성 검사 실행</summary>
|
||||
private void OnTextFieldFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnMouseEnter(MouseEnterEvent evt) => _isHovered = true;
|
||||
private void OnMouseLeave(MouseLeaveEvent evt) => _isHovered = false;
|
||||
|
||||
@@ -433,9 +501,25 @@ namespace UVC.UIToolkit
|
||||
|
||||
private float ClampValueInternal(float value)
|
||||
{
|
||||
// Step 소수점 자릿수 기준으로 반올림하여 부동소수점 오차 제거
|
||||
// 예: step=0.1 → digits=1, 0.7+0.1=0.8000001 → 0.8
|
||||
int digits = GetDecimalDigits(_step);
|
||||
if (digits > 0)
|
||||
{
|
||||
value = (float)Math.Round(value, digits);
|
||||
}
|
||||
return Mathf.Clamp(value, _minValue, _maxValue);
|
||||
}
|
||||
|
||||
/// <summary>소수점 이하 유효 자릿수를 반환합니다.</summary>
|
||||
private static int GetDecimalDigits(float value)
|
||||
{
|
||||
// 최대 7자리까지 검사 (float 정밀도 한계)
|
||||
string s = value.ToString("G7");
|
||||
int dotIndex = s.IndexOf('.');
|
||||
return dotIndex < 0 ? 0 : s.Length - dotIndex - 1;
|
||||
}
|
||||
|
||||
private void UpdateReadOnlyState()
|
||||
{
|
||||
if (_textField != null)
|
||||
@@ -453,6 +537,47 @@ namespace UVC.UIToolkit
|
||||
_downButton.SetEnabled(!_isReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
EnableInClassList("utk-number-stepper--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableInClassList("utk-number-stepper--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-number-stepper__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -471,6 +596,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
_textField?.UnregisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||
_textField?.UnregisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||
_textField?.UnregisterCallback<FocusOutEvent>(OnTextFieldFocusOut);
|
||||
|
||||
UnregisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||
UnregisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||
@@ -481,6 +607,10 @@ namespace UVC.UIToolkit
|
||||
OnTabPressed = null;
|
||||
OnShiftTabPressed = null;
|
||||
|
||||
// Validation 정리
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
|
||||
// UI 참조 정리
|
||||
_textField = null;
|
||||
_upButton = null;
|
||||
|
||||
@@ -9,6 +9,15 @@ namespace UVC.UIToolkit
|
||||
/// 입력 필드 컴포넌트.
|
||||
/// Unity TextField를 래핑하여 커스텀 스타일을 적용합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>플레이스홀더, 비밀번호, 멀티라인 지원</description></item>
|
||||
/// <item><description>스타일 변형 (Default, Filled, Outlined)</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (Submit/FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
@@ -17,39 +26,62 @@ namespace UVC.UIToolkit
|
||||
/// input.label = "이름";
|
||||
/// input.Placeholder = "이름을 입력하세요";
|
||||
/// input.OnValueChanged += (value) => Debug.Log($"입력값: {value}");
|
||||
///
|
||||
///
|
||||
/// // 비밀번호 입력 필드
|
||||
/// var password = new UTKInputField();
|
||||
/// password.label = "비밀번호";
|
||||
/// password.isPasswordField = true;
|
||||
///
|
||||
/// // 검증 오류 표시
|
||||
/// input.ErrorMessage = "이름은 필수입니다.";
|
||||
/// // 오류 제거
|
||||
/// input.ErrorMessage = "";
|
||||
///
|
||||
///
|
||||
/// // 변형 스타일
|
||||
/// input.Variant = UTKInputField.InputFieldVariant.Outlined;
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var emailInput = new UTKInputField("이메일", "example@email.com");
|
||||
/// emailInput.ErrorMessage = "올바른 이메일 형식이 아닙니다.";
|
||||
/// emailInput.Validation = () => emailInput.Value.Contains("@");
|
||||
/// // → Submit(Enter) 또는 FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 필수 입력 검증
|
||||
/// var nameInput = new UTKInputField("이름");
|
||||
/// nameInput.ErrorMessage = "이름은 필수 항목입니다.";
|
||||
/// nameInput.Validation = () => !string.IsNullOrWhiteSpace(nameInput.Value);
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = nameInput.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// nameInput.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이)
|
||||
/// input.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// input.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
|
||||
/// <!-- 기본 입력 필드 -->
|
||||
/// <utk:UTKInputField label="이름" />
|
||||
///
|
||||
///
|
||||
/// <!-- 플레이스홀더 -->
|
||||
/// <utk:UTKInputField label="이메일" placeholder="example@email.com" />
|
||||
///
|
||||
///
|
||||
/// <!-- 비밀번호 필드 -->
|
||||
/// <utk:UTKInputField label="비밀번호" is-password-field="true" />
|
||||
///
|
||||
///
|
||||
/// <!-- 여러 줄 입력 -->
|
||||
/// <utk:UTKInputField label="설명" multiline="true" />
|
||||
///
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKInputField label="이메일" error-message="올바른 이메일 형식이 아닙니다." />
|
||||
///
|
||||
/// <!-- 비활성화 -->
|
||||
/// <utk:UTKInputField label="읽기전용" is-enabled="false" value="수정 불가" />
|
||||
/// </ui:UXML>
|
||||
/// </code>
|
||||
/// ]]></code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKInputField : TextField, IDisposable
|
||||
@@ -63,6 +95,8 @@ namespace UVC.UIToolkit
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private InputFieldVariant _variant = InputFieldVariant.Default;
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -92,7 +126,7 @@ namespace UVC.UIToolkit
|
||||
set => textEdition.placeholder = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지</summary>
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
@@ -100,10 +134,19 @@ namespace UVC.UIToolkit
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
EnableInClassList("utk-input--error", !string.IsNullOrEmpty(value));
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-input--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. Submit/FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -199,26 +242,9 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<string>>(OnTextValueChanged);
|
||||
|
||||
RegisterCallback<FocusInEvent>(_ =>
|
||||
{
|
||||
EnableInClassList("utk-input--focused", true);
|
||||
OnFocused?.Invoke();
|
||||
});
|
||||
|
||||
RegisterCallback<FocusOutEvent>(_ =>
|
||||
{
|
||||
EnableInClassList("utk-input--focused", false);
|
||||
OnBlurred?.Invoke();
|
||||
});
|
||||
|
||||
RegisterCallback<KeyDownEvent>(evt =>
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return && !multiline)
|
||||
{
|
||||
OnSubmit?.Invoke(value);
|
||||
}
|
||||
}, TrickleDown.TrickleDown);
|
||||
RegisterCallback<FocusInEvent>(OnFocusIn);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -263,6 +289,28 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusIn(FocusInEvent evt)
|
||||
{
|
||||
EnableInClassList("utk-input--focused", true);
|
||||
OnFocused?.Invoke();
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
EnableInClassList("utk-input--focused", false);
|
||||
OnBlurred?.Invoke();
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return && !multiline)
|
||||
{
|
||||
OnSubmit?.Invoke(value);
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
@@ -283,6 +331,15 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택 영역 설정
|
||||
/// </summary>
|
||||
@@ -291,6 +348,57 @@ namespace UVC.UIToolkit
|
||||
textSelection.SelectAll();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-input--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-input--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-input__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
private void UpdateVariant()
|
||||
{
|
||||
RemoveFromClassList("utk-input--default");
|
||||
@@ -318,10 +426,16 @@ namespace UVC.UIToolkit
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
UnregisterCallback<ChangeEvent<string>>(OnTextValueChanged);
|
||||
UnregisterCallback<FocusInEvent>(OnFocusIn);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
|
||||
OnValueChanged = null;
|
||||
OnFocused = null;
|
||||
OnBlurred = null;
|
||||
OnSubmit = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>최소/최대값 제한 (MinValue, MaxValue)</description></item>
|
||||
/// <item><description>증감 단위 설정 (Step)</description></item>
|
||||
/// <item><description>순환 모드 (WrapAround) - 최대에서 최소로, 최소에서 최대로</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
@@ -79,6 +81,15 @@ namespace UVC.UIToolkit
|
||||
/// UpdateCalendar(month);
|
||||
/// };
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// var monthStepper = new UTKIntStepper(1, 12, 1, 1);
|
||||
/// monthStepper.ErrorMessage = "유효하지 않은 월입니다.";
|
||||
/// monthStepper.Validation = () => monthStepper.Value >= 1 && monthStepper.Value <= 12;
|
||||
///
|
||||
/// bool isValid = monthStepper.Validate();
|
||||
/// monthStepper.ClearError();
|
||||
/// </code>
|
||||
/// </example>
|
||||
[UxmlElement]
|
||||
public partial class UTKIntStepper : VisualElement, IDisposable
|
||||
@@ -143,6 +154,20 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-number-stepper--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-number-stepper--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
@@ -156,11 +181,24 @@ namespace UVC.UIToolkit
|
||||
private bool _isUpdating;
|
||||
private bool _isHovered;
|
||||
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
|
||||
private TextField? _textField;
|
||||
private Button? _upButton;
|
||||
private Button? _downButton;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>값이 변경될 때 발생</summary>
|
||||
public event Action<int>? OnValueChanged;
|
||||
@@ -242,6 +280,7 @@ namespace UVC.UIToolkit
|
||||
if (notify)
|
||||
{
|
||||
OnValueChanged?.Invoke(_value);
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,6 +328,18 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
_textField?.Focus();
|
||||
}
|
||||
|
||||
/// <summary>강제로 Validation을 실행하여 에러 상태를 업데이트합니다.</summary>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods - UI Creation
|
||||
@@ -345,6 +396,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
_textField?.RegisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||
_textField?.RegisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||
_textField?.RegisterCallback<FocusOutEvent>(OnTextFieldFocusOut);
|
||||
|
||||
RegisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||
@@ -371,6 +423,11 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTextFieldFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnTextFieldKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.UpArrow)
|
||||
@@ -454,6 +511,47 @@ namespace UVC.UIToolkit
|
||||
_downButton.SetEnabled(!_isReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
EnableInClassList("utk-number-stepper--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableInClassList("utk-number-stepper--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-number-stepper__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -472,6 +570,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
_textField?.UnregisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
|
||||
_textField?.UnregisterCallback<KeyDownEvent>(OnTextFieldKeyDown, TrickleDown.TrickleDown);
|
||||
_textField?.UnregisterCallback<FocusOutEvent>(OnTextFieldFocusOut);
|
||||
|
||||
UnregisterCallback<MouseEnterEvent>(OnMouseEnter);
|
||||
UnregisterCallback<MouseLeaveEvent>(OnMouseLeave);
|
||||
@@ -483,6 +582,8 @@ namespace UVC.UIToolkit
|
||||
OnShiftTabPressed = null;
|
||||
|
||||
// UI 참조 정리
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
_textField = null;
|
||||
_upButton = null;
|
||||
_downButton = null;
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>개수, 수량, 인덱스 등 정수 값에 사용</description></item>
|
||||
/// <item><description>큰 숫자가 필요하면 UTKLongField 사용</description></item>
|
||||
/// <item><description>소수점이 필요하면 UTKFloatField 사용</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
@@ -42,6 +44,31 @@ namespace UVC.UIToolkit
|
||||
/// // 비활성화
|
||||
/// intField.IsEnabled = false;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var ageField = new UTKIntegerField("나이", 0);
|
||||
/// ageField.ErrorMessage = "나이는 1~150 사이여야 합니다.";
|
||||
/// ageField.Validation = () => ageField.Value >= 1 && ageField.Value <= 150;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 양수만 허용
|
||||
/// var quantityField = new UTKIntegerField("수량", 0);
|
||||
/// quantityField.ErrorMessage = "수량은 0보다 커야 합니다.";
|
||||
/// quantityField.Validation = () => quantityField.Value > 0;
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = ageField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// ageField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// intField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// intField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 정수 필드 -->
|
||||
@@ -54,8 +81,10 @@ namespace UVC.UIToolkit
|
||||
/// <code>
|
||||
/// // 인벤토리 아이템 수량 편집
|
||||
/// var quantityField = new UTKIntegerField("보유 수량", item.Quantity);
|
||||
/// quantityField.ErrorMessage = "수량은 0 이상이어야 합니다.";
|
||||
/// quantityField.Validation = () => quantityField.Value >= 0;
|
||||
/// quantityField.OnValueChanged += (newQty) => {
|
||||
/// item.Quantity = Mathf.Max(0, newQty); // 음수 방지
|
||||
/// item.Quantity = newQty;
|
||||
/// UpdateInventoryUI();
|
||||
/// };
|
||||
/// </code>
|
||||
@@ -70,6 +99,9 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -85,6 +117,27 @@ namespace UVC.UIToolkit
|
||||
set => this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-integer-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -131,6 +184,8 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<int>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -163,6 +218,81 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-integer-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-integer-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-integer-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -174,8 +304,12 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<int>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description>파일 크기(바이트), 타임스탬프, 고유 ID 등에 사용</description></item>
|
||||
/// <item><description>int 범위(-21억~21억)를 초과하는 값에 적합</description></item>
|
||||
/// <item><description>일반 정수는 UTKIntegerField 사용 권장 (메모리 효율)</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
@@ -39,6 +41,26 @@ namespace UVC.UIToolkit
|
||||
/// long currentValue = longField.Value;
|
||||
/// longField.Value = 5000000000L; // 50억
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var fileSizeField = new UTKLongField("파일 크기", 0);
|
||||
/// fileSizeField.ErrorMessage = "파일 크기는 0보다 커야 합니다.";
|
||||
/// fileSizeField.Validation = () => fileSizeField.Value > 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = fileSizeField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// fileSizeField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// longField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// longField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 Long 필드 -->
|
||||
@@ -64,6 +86,9 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private bool _isEnabled = true;
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -79,6 +104,27 @@ namespace UVC.UIToolkit
|
||||
set => this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-long-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>활성화 상태</summary>
|
||||
[UxmlAttribute("is-enabled")]
|
||||
public bool IsEnabled
|
||||
@@ -125,6 +171,8 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<long>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -157,6 +205,81 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
|
||||
private void OnKeyDown(KeyDownEvent evt)
|
||||
{
|
||||
if (evt.keyCode == KeyCode.Return)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-long-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-long-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-long-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -168,8 +291,12 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<long>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
UnregisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace UVC.UIToolkit
|
||||
/// <item><description><b>X, Y</b>: 사각형의 왼쪽 상단 모서리 위치</description></item>
|
||||
/// <item><description><b>Width</b>: 사각형의 너비</description></item>
|
||||
/// <item><description><b>Height</b>: 사각형의 높이</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// <para>주로 UI 요소의 위치/크기, 스프라이트 영역, 화면 좌표 등에 사용됩니다.</para>
|
||||
/// </remarks>
|
||||
@@ -47,6 +49,26 @@ namespace UVC.UIToolkit
|
||||
/// readOnlyField.Value = new Rect(10, 10, 200, 100);
|
||||
/// readOnlyField.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var areaField = new UTKRectField("영역");
|
||||
/// areaField.ErrorMessage = "너비와 높이는 0보다 커야 합니다.";
|
||||
/// areaField.Validation = () => areaField.Value.width > 0 && areaField.Value.height > 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = areaField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// areaField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// areaField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// areaField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 Rect 필드 -->
|
||||
@@ -68,6 +90,8 @@ namespace UVC.UIToolkit
|
||||
/// // 스프라이트 UV 영역 편집기
|
||||
/// var uvField = new UTKRectField("UV 영역");
|
||||
/// uvField.Value = sprite.rect;
|
||||
/// uvField.ErrorMessage = "UV 영역은 텍스처 범위를 초과할 수 없습니다.";
|
||||
/// uvField.Validation = () => uvField.Value.xMax <= texture.width && uvField.Value.yMax <= texture.height;
|
||||
/// uvField.OnValueChanged += (newRect) => {
|
||||
/// // 스프라이트 영역 업데이트
|
||||
/// UpdateSpriteRect(sprite, newRect);
|
||||
@@ -89,6 +113,9 @@ namespace UVC.UIToolkit
|
||||
private string _yLabel = "Y";
|
||||
private string _wLabel = "W";
|
||||
private string _hLabel = "H";
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -117,6 +144,27 @@ namespace UVC.UIToolkit
|
||||
set => this.value = value;
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-rectfield--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
|
||||
/// <summary>X 라벨</summary>
|
||||
[UxmlAttribute("x-label")]
|
||||
public string XLabel
|
||||
@@ -221,6 +269,7 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<Rect>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -276,6 +325,73 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-rectfield--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-rectfield--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-rectfield__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -287,8 +403,11 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<Rect>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ namespace UVC.UIToolkit
|
||||
/// <para>- X: 수평(가로) 방향의 값</para>
|
||||
/// <para>- Y: 수직(세로) 방향의 값</para>
|
||||
/// <para>주로 2D 게임의 위치, UI 크기, 텍스처 좌표(UV) 등에 사용됩니다.</para>
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>X, Y 축 라벨 커스터마이징</description></item>
|
||||
/// <item><description>읽기 전용 모드</description></item>
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
@@ -44,6 +51,26 @@ namespace UVC.UIToolkit
|
||||
/// readOnlyField.Value = new Vector2(100, 50);
|
||||
/// readOnlyField.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var sizeField = new UTKVector2Field("크기");
|
||||
/// sizeField.ErrorMessage = "크기는 양수여야 합니다.";
|
||||
/// sizeField.Validation = () => sizeField.Value.x > 0 && sizeField.Value.y > 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = sizeField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// sizeField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이)
|
||||
/// sizeField.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// sizeField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <!-- 네임스페이스 선언 -->
|
||||
@@ -59,6 +86,9 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <!-- 읽기 전용 -->
|
||||
/// <utk:UTKVector2Field label="고정 크기" is-readonly="true" />
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKVector2Field label="크기" error-message="크기는 양수여야 합니다." />
|
||||
/// </UXML>
|
||||
/// </code>
|
||||
/// <para><b>실제 활용 예시:</b></para>
|
||||
@@ -90,6 +120,9 @@ namespace UVC.UIToolkit
|
||||
private bool _isReadOnly = false;
|
||||
private string _xLabel = "X";
|
||||
private string _yLabel = "Y";
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -154,6 +187,27 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-vector2-field--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-vector2-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -198,6 +252,7 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<Vector2>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -251,6 +306,73 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-vector2-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-vector2-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-vector2-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -262,8 +384,12 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<Vector2>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -52,6 +52,26 @@ namespace UVC.UIToolkit
|
||||
/// readOnlyField.Value = new Vector3(1, 2, 3);
|
||||
/// readOnlyField.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var posField = new UTKVector3Field("위치");
|
||||
/// posField.ErrorMessage = "Y값은 0 이상이어야 합니다.";
|
||||
/// posField.Validation = () => posField.Value.y >= 0;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = posField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// posField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// posField.ErrorMessage = "유효하지 않은 좌표입니다.";
|
||||
/// posField.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code><![CDATA[
|
||||
/// <!-- 기본 Vector3 필드 -->
|
||||
@@ -66,6 +86,9 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <!-- 읽기 전용 -->
|
||||
/// <utk:UTKVector3Field label="고정 위치" is-readonly="true" />
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKVector3Field label="위치" error-message="유효하지 않은 좌표입니다." />
|
||||
/// ]]></code>
|
||||
/// <para><b>실제 활용 예시 (Transform 편집기):</b></para>
|
||||
/// <code>
|
||||
@@ -97,6 +120,9 @@ namespace UVC.UIToolkit
|
||||
private string _xLabel = "X";
|
||||
private string _yLabel = "Y";
|
||||
private string _zLabel = "Z";
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -173,6 +199,36 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-vector3-field--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-vector3-field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var posField = new UTKVector3Field("위치");
|
||||
/// posField.ErrorMessage = "Y값은 0 이상이어야 합니다.";
|
||||
/// posField.Validation = () => posField.Value.y >= 0;
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -217,6 +273,7 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<Vector3>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -271,6 +328,73 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-vector3-field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-vector3-field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-vector3-field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -282,8 +406,11 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<Vector3>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ namespace UVC.UIToolkit
|
||||
/// <para>- 색상(RGBA): Red, Green, Blue, Alpha 값 (0~1 범위)</para>
|
||||
/// <para>- 쉐이더 파라미터: 커스텀 쉐이더에 전달하는 4개의 값</para>
|
||||
/// <para>- 사원수(Quaternion)와 유사한 구조로 회전 데이터 저장</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Validation 함수를 통한 입력 검증 (FocusOut 시 자동 호출)</description></item>
|
||||
/// <item><description>에러 상태 시 붉은 외곽선 + 에러 메시지 표시</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
@@ -48,6 +52,26 @@ namespace UVC.UIToolkit
|
||||
/// readOnlyField.Value = new Vector4(1, 0.5f, 0.5f, 1);
|
||||
/// readOnlyField.IsReadOnly = true;
|
||||
/// </code>
|
||||
/// <para><b>Validation (입력 검증):</b></para>
|
||||
/// <code>
|
||||
/// // 검증 함수 설정 (Func<bool>)
|
||||
/// var colorField = new UTKVector4Field("색상");
|
||||
/// colorField.ErrorMessage = "알파 값은 0~1 사이여야 합니다.";
|
||||
/// colorField.Validation = () => colorField.Value.w >= 0 && colorField.Value.w <= 1;
|
||||
/// // → FocusOut 시 자동으로 검증
|
||||
/// // → 실패 시 붉은 외곽선 + 에러 메시지 표시, 통과 시 자동 해제
|
||||
///
|
||||
/// // 강제 검증 호출 (예: 폼 제출 버튼 클릭 시)
|
||||
/// bool isValid = colorField.Validate();
|
||||
/// if (!isValid) return; // 검증 실패
|
||||
///
|
||||
/// // 에러 수동 해제
|
||||
/// colorField.ClearError();
|
||||
///
|
||||
/// // 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
|
||||
/// vec4Field.ErrorMessage = "서버 오류가 발생했습니다.";
|
||||
/// vec4Field.ErrorMessage = ""; // 오류 제거
|
||||
/// </code>
|
||||
/// <para><b>UXML에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// <!-- 네임스페이스 선언 -->
|
||||
@@ -64,6 +88,9 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <!-- 읽기 전용 -->
|
||||
/// <utk:UTKVector4Field label="고정값" is-readonly="true" />
|
||||
///
|
||||
/// <!-- 에러 메시지 (C#에서 Validation 설정 권장) -->
|
||||
/// <utk:UTKVector4Field label="색상" error-message="알파 값은 0~1 사이여야 합니다." />
|
||||
/// </UXML>
|
||||
/// </code>
|
||||
/// <para><b>실제 활용 예시:</b></para>
|
||||
@@ -95,6 +122,9 @@ namespace UVC.UIToolkit
|
||||
private string _yLabel = "Y";
|
||||
private string _zLabel = "Z";
|
||||
private string _wLabel = "W";
|
||||
private string _errorMessage = "";
|
||||
private Func<bool>? _validation;
|
||||
private Label? _errorLabel;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -183,6 +213,27 @@ namespace UVC.UIToolkit
|
||||
EnableInClassList("utk-vector4field--readonly", value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에러 메시지. 비어있지 않으면 에러 상태로 표시</summary>
|
||||
[UxmlAttribute("error-message")]
|
||||
public string ErrorMessage
|
||||
{
|
||||
get => _errorMessage;
|
||||
set
|
||||
{
|
||||
_errorMessage = value;
|
||||
var hasError = !string.IsNullOrEmpty(value);
|
||||
EnableInClassList("utk-vector4field--error", hasError);
|
||||
UpdateErrorLabel(hasError ? value : null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>검증 함수. FocusOut 시 호출되어 false 반환 시 ErrorMessage 표시</summary>
|
||||
public Func<bool>? Validation
|
||||
{
|
||||
get => _validation;
|
||||
set => _validation = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -227,6 +278,7 @@ namespace UVC.UIToolkit
|
||||
private void SetupEvents()
|
||||
{
|
||||
RegisterCallback<ChangeEvent<Vector4>>(OnFieldValueChanged);
|
||||
RegisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
@@ -282,6 +334,73 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
OnValueChanged?.Invoke(evt.newValue);
|
||||
}
|
||||
|
||||
private void OnFocusOut(FocusOutEvent evt)
|
||||
{
|
||||
RunValidation();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 강제로 Validation을 실행하여 에러 상태를 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <returns>Validation이 null이면 true, 아니면 Validation 결과</returns>
|
||||
public bool Validate()
|
||||
{
|
||||
return RunValidation();
|
||||
}
|
||||
|
||||
/// <summary>에러 상태를 수동으로 해제합니다.</summary>
|
||||
public void ClearError()
|
||||
{
|
||||
ErrorMessage = "";
|
||||
}
|
||||
|
||||
private bool RunValidation()
|
||||
{
|
||||
if (_validation == null) return true;
|
||||
|
||||
var isValid = _validation.Invoke();
|
||||
if (isValid)
|
||||
{
|
||||
// 검증 통과 시 에러 상태 해제
|
||||
EnableInClassList("utk-vector4field--error", false);
|
||||
UpdateErrorLabel(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 검증 실패 시 에러 상태 표시
|
||||
EnableInClassList("utk-vector4field--error", true);
|
||||
UpdateErrorLabel(_errorMessage);
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private void UpdateErrorLabel(string? message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
// 에러 라벨 숨기기 (존재하면)
|
||||
if (_errorLabel != null)
|
||||
{
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 라벨 생성 (지연 생성 - 필요할 때만)
|
||||
if (_errorLabel == null)
|
||||
{
|
||||
_errorLabel = new Label();
|
||||
_errorLabel.AddToClassList("utk-vector4field__error-message");
|
||||
_errorLabel.style.display = DisplayStyle.None;
|
||||
Add(_errorLabel);
|
||||
}
|
||||
|
||||
_errorLabel.text = message;
|
||||
_errorLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
@@ -293,8 +412,11 @@ namespace UVC.UIToolkit
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
OnValueChanged = null;
|
||||
UnregisterCallback<ChangeEvent<Vector4>>(OnFieldValueChanged);
|
||||
UnregisterCallback<FocusOutEvent>(OnFocusOut);
|
||||
OnValueChanged = null;
|
||||
_validation = null;
|
||||
_errorLabel = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -976,10 +976,13 @@ namespace UVC.UIToolkit
|
||||
// TreeView 갱신은 ToggleGroupExpanded에서 처리
|
||||
}
|
||||
|
||||
private void OnItemValueChanged(IUTKPropertyItem item, object? oldValue, object? newValue)
|
||||
private void OnItemValueChanged(IUTKPropertyItem item, object? oldValue, object? newValue, bool notify)
|
||||
{
|
||||
var args = new UTKPropertyValueChangedEventArgs(item, oldValue, newValue);
|
||||
OnPropertyValueChanged?.Invoke(args);
|
||||
if (notify)
|
||||
{
|
||||
var args = new UTKPropertyValueChangedEventArgs(item, oldValue, newValue);
|
||||
OnPropertyValueChanged?.Invoke(args);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearch(string newValue)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace UVC.UIToolkit
|
||||
string? GroupId { get; set; }
|
||||
|
||||
/// <summary>값 변경 이벤트</summary>
|
||||
event Action<IUTKPropertyItem, object?, object?>? OnValueChanged;
|
||||
event Action<IUTKPropertyItem, object?, object?, bool>? OnValueChanged;
|
||||
|
||||
/// <summary>상태(ReadOnly 등) 변경 이벤트. View가 구독하여 UI를 갱신합니다.</summary>
|
||||
event Action<IUTKPropertyItem>? OnStateChanged;
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Events
|
||||
/// <summary>값 변경 이벤트 (object 타입)</summary>
|
||||
public event Action<IUTKPropertyItem, object?, object?>? OnValueChanged;
|
||||
public event Action<IUTKPropertyItem, object?, object?, bool>? OnValueChanged;
|
||||
|
||||
/// <summary>값 변경 이벤트 (제네릭 타입)</summary>
|
||||
public event Action<IUTKPropertyItem<T>, T, T>? OnTypedValueChanged;
|
||||
@@ -159,35 +159,34 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (default(T) == null)
|
||||
{
|
||||
if (notifyChangeEvent)
|
||||
{
|
||||
Value = default!;
|
||||
}
|
||||
else
|
||||
if (!Equals(_value, value))
|
||||
{
|
||||
var oldValue = _value;
|
||||
_value = default!;
|
||||
NotifyValueChanged(oldValue, default!, notifyChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value is T typedValue)
|
||||
{
|
||||
if (notifyChangeEvent)
|
||||
{
|
||||
Value = typedValue;
|
||||
}
|
||||
else
|
||||
if (!Equals(_value, typedValue))
|
||||
{
|
||||
var oldValue = _value;
|
||||
_value = typedValue;
|
||||
NotifyValueChanged(oldValue, typedValue, notifyChangeEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (notifyChangeEvent)
|
||||
Value = (T)Convert.ChangeType(value, typeof(T));
|
||||
else
|
||||
_value = (T)Convert.ChangeType(value, typeof(T));
|
||||
var v = (T)Convert.ChangeType(value, typeof(T));
|
||||
if (!Equals(_value, v))
|
||||
{
|
||||
var oldValue = _value;
|
||||
_value = v;
|
||||
NotifyValueChanged(oldValue, v, notifyChangeEvent);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -199,10 +198,10 @@ namespace UVC.UIToolkit
|
||||
|
||||
#region Protected Methods
|
||||
/// <summary>값 변경을 알립니다.</summary>
|
||||
protected void NotifyValueChanged(T oldValue, T newValue)
|
||||
protected void NotifyValueChanged(T oldValue, T newValue, bool notifyChangeEvent = false)
|
||||
{
|
||||
OnTypedValueChanged?.Invoke(this, oldValue, newValue);
|
||||
OnValueChanged?.Invoke(this, oldValue, newValue);
|
||||
OnValueChanged?.Invoke(this, oldValue, newValue, notifyChangeEvent);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace UVC.UIToolkit
|
||||
/// <para><b>사용법 (Data 바인딩):</b></para>
|
||||
/// <code>
|
||||
/// var data = new UTKFloatDropdownPropertyItem("id", "Label", 1.5f,
|
||||
/// new List<string> { "A", "B", "C" }, "A");
|
||||
/// new List<string> { "A", "B", "C" }, "A");
|
||||
/// var view = new UTKFloatDropdownPropertyItemView();
|
||||
/// view.Bind(data);
|
||||
/// </code>
|
||||
|
||||
@@ -128,8 +128,8 @@ namespace UVC.UIToolkit
|
||||
/// private void SubscribeToThemeChanges()
|
||||
/// {
|
||||
/// UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
/// RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
/// RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
/// RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
/// RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
/// }
|
||||
///
|
||||
/// private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
|
||||
@@ -154,8 +154,8 @@ namespace UVC.UIToolkit
|
||||
/// if (_disposed) return;
|
||||
/// _disposed = true;
|
||||
/// UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
/// UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
/// UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
/// UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
/// UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace UVC.UIToolkit
|
||||
/// };
|
||||
///
|
||||
/// // 3. 데이터 로드 (그룹 + 개별 아이템 혼합)
|
||||
/// var entries = new List<IUTKPropertyEntry>();
|
||||
/// var entries = new List<IUTKPropertyEntry>();
|
||||
/// entries.Add(new UTKStringPropertyItem("name", "이름", "기본값"));
|
||||
///
|
||||
/// var group = new UTKPropertyGroup("transform", "Transform");
|
||||
|
||||
Reference in New Issue
Block a user