UTKProperyWindow 개발 중

This commit is contained in:
logonkhi
2026-02-03 20:43:36 +09:00
parent 297ca29082
commit 8181eae4c6
74 changed files with 1268 additions and 385 deletions

View File

@@ -1 +0,0 @@
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"Split","MtRendering":false,"GraphicsJobs":false,"GpuSkinning":true,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"IL2CPP","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"StandaloneWindows64","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.2.12f1","Branch":"6000.2/staging","Changeset":"e89d5df0e333","Date":1762305399},"Dependencies":["com.cysharp.unitask@2.5.10","com.github-glitchenzo.nugetforunity@4.5.0","com.unity.2d.sprite@1.0.0","com.unity.ai.navigation@2.0.9","com.unity.cloud.gltfast@6.14.1","com.unity.ide.rider@3.0.38","com.unity.ide.visualstudio@2.0.25","com.unity.inputsystem@1.14.2","com.unity.multiplayer.center@1.0.0","com.unity.nuget.newtonsoft-json@3.2.1","com.unity.render-pipelines.core@17.2.0","com.unity.render-pipelines.universal@17.2.0","com.unity.test-framework@1.6.0","com.unity.timeline@1.8.9","com.unity.ugui@2.0.0","com.unity.visualscripting@1.9.8","com.unity.modules.accessibility@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.ext.nunit@2.0.5","com.unity.shadergraph@17.2.0","com.unity.render-pipelines.universal-config@17.0.3","com.unity.burst@1.8.25","com.unity.mathematics@1.3.2","com.unity.collections@2.6.2","com.unity.rendering.light-transport@1.0.1","com.unity.searcher@4.9.3","com.unity.nuget.mono-cecil@1.11.5","com.unity.test-framework.performance@3.2.0"],"Results":[]}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: a8692d97c02ffec48bc8f9f568e868a5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1 +0,0 @@
{"MeasurementCount":-1}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: cba8cf29ff1a3af4c951dc8785008b52
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -52,6 +52,10 @@
margin-right: var(--space-s);
}
.utk-vector2-field #unity-y-input {
margin-right: 0;
}
/* Unity USS does not support :last-child pseudo-class */
/* .utk-vector2-field .unity-float-field:last-child {
margin-right: 0;

View File

@@ -52,6 +52,10 @@
margin-right: var(--space-s);
}
.utk-vector3-field #unity-z-input {
margin-right: 0;
}
/* Unity USS does not support :last-child pseudo-class */
/* .utk-vector3-field .unity-float-field:last-child {
margin-right: 0;

View File

@@ -44,6 +44,10 @@
margin-right: var(--space-xs);
}
.utk-vector4field #unity-w-input {
margin-right: 0;
}
/* Unity USS does not support :last-child pseudo-class */
/* .utk-vector4field .unity-float-field:last-child {
margin-right: 0;

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--color">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<VisualElement name="color-preview" class="utk-property-item__color-preview" />
<utk:UTKInputField name="hex-field" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<utk:UTKInputField name="hex-field" style="flex-grow: 1;" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="min-width: 24px; width: 24px;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--colorstate">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<utk:UTKLabel name="state-label" size="Body2" class="utk-property-item__state-label" />
<VisualElement name="color-preview" class="utk-property-item__color-preview" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--date">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKInputField name="date-field" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<utk:UTKInputField name="date-field" style="flex-grow: 1;" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,13 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--daterange">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKInputField name="start-field" />
<utk:UTKButton name="start-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKInputField name="end-field" />
<utk:UTKButton name="end-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<VisualElement name="VisualElement" class="utk-property-item__value" style="flex-direction: column; flex-grow: 1; align-items: stretch;">
<VisualElement style="flex-grow: 1; flex-direction: row;">
<utk:UTKInputField name="start-field" style="flex-grow: 1;" />
<utk:UTKButton name="start-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
<utk:UTKLabel text="~" size="Body2" text-alignment="Center" class="utk-property-item__range-separator" />
<VisualElement style="flex-grow: 1; flex-direction: row;">
<utk:UTKInputField name="end-field" style="flex-grow: 1;" />
<utk:UTKButton name="end-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--datetime">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKInputField name="datetime-field" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<utk:UTKInputField name="datetime-field" style="flex-grow: 1;" />
<utk:UTKButton name="picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,13 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--datetimerange">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKInputField name="start-field" />
<utk:UTKButton name="start-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKInputField name="end-field" />
<utk:UTKButton name="end-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" />
<VisualElement class="utk-property-item__value" style="flex-grow: 1; flex-direction: column; align-items: stretch;">
<VisualElement style="flex-grow: 1; flex-direction: row;">
<utk:UTKInputField name="start-field" style="flex-grow: 1;" />
<utk:UTKButton name="start-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
<utk:UTKLabel text="~" size="Body2" text-alignment="Center" class="utk-property-item__range-separator" style="-unity-text-align: middle-center;" />
<VisualElement style="flex-grow: 1; flex-direction: row;">
<utk:UTKInputField name="end-field" style="flex-grow: 1;" />
<utk:UTKButton name="end-picker-btn" text="..." variant="Secondary" class="utk-property-item__picker-btn" style="width: 24px; min-width: 24px;" />
</VisualElement>
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--dropdownlist">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKDropdown name="dropdown-field" />
<utk:UTKDropdown name="dropdown-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--enum">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKEnumDropDown name="enum-dropdown" />
<utk:UTKEnumDropDown name="enum-dropdown" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--float">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKFloatField name="value-field" />
<utk:UTKFloatField name="value-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--float utk-property-item--slider">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<utk:UTKSlider name="slider-field" class="utk-property-item__slider" />
<utk:UTKFloatField name="value-field" class="utk-property-item__number-field" />
</VisualElement>

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--floatrange">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKFloatField name="min-field" />
<VisualElement class="utk-property-item__value" style="flex-grow: 1; flex-direction: row;">
<utk:UTKFloatField name="min-field" style="flex-grow: 1;" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKFloatField name="max-field" />
<utk:UTKFloatField name="max-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--int">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKIntegerField name="value-field" />
<utk:UTKIntegerField name="value-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--int utk-property-item--slider">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<VisualElement class="utk-property-item__value" style="flex-grow: 1;">
<utk:UTKSliderInt name="slider-field" class="utk-property-item__slider" />
<utk:UTKIntegerField name="value-field" class="utk-property-item__number-field" />
</VisualElement>

View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--intrange">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKIntegerField name="min-field" />
<VisualElement class="utk-property-item__value" style="flex-grow: 1; flex-direction: row;">
<utk:UTKIntegerField name="min-field" style="flex-grow: 1;" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKIntegerField name="max-field" />
<utk:UTKIntegerField name="max-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--string">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKInputField name="value-field" />
<utk:UTKInputField name="value-field" style="flex-grow: 1;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--vector2">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKVector2Field name="value-field" class="utk-property-item__vector2-field" />
<utk:UTKVector2Field name="value-field" class="utk-property-item__vector2-field" style="flex-grow: 1; margin-right: 0;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement name="item-root" class="utk-property-item utk-property-item--vector3">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKVector3Field name="value-field" class="utk-property-item__vector3-field" />
<utk:UTKVector3Field name="value-field" class="utk-property-item__vector3-field" style="flex-grow: 1; margin-right: 0;" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -11,7 +11,7 @@
.utk-property-list__search-container {
flex-direction: row;
padding: var(--space-m);
padding: var(--space-m) 0;
}
/* UTKInputField 검색 필드 스타일 */
@@ -56,7 +56,10 @@
flex-direction: row;
align-items: center;
min-height: 28px;
padding: 3px var(--space-m);
padding-top: 3px;
padding-bottom: 3px;
padding-left: var(--space-m);
padding-right: 0;
border-bottom-width: var(--border-width);
border-bottom-color: var(--color-border);
}
@@ -212,6 +215,7 @@
=================================== */
.utk-property-item__picker-btn {
width: 24px;
min-width: 24px;
height: 24px;
margin-left: var(--space-m);
background-color: var(--color-btn-normal);
@@ -229,11 +233,12 @@
Color Preview
=================================== */
.utk-property-item__color-preview {
width: 60px;
width: 22px;
height: 22px;
border-width: var(--border-width);
border-color: var(--color-border);
border-radius: var(--radius-s);
margin-right: var(--space-s);
}
/* ===================================

View File

@@ -3,7 +3,7 @@
<VisualElement name="window-root" class="utk-property-window">
<VisualElement name="header" class="utk-property-window__header">
<utk:UTKLabel name="title" class="utk-property-window__title" />
<utk:UTKButton name="close-btn" class="utk-property-window__close-btn" />
<utk:UTKButton name="close-btn" class="utk-property-window__close-btn" variant="Text" icon-only="true" />
</VisualElement>
<utk:UTKPropertyList name="content" class="utk-property-window__content" />
</VisualElement>

View File

@@ -1,29 +1,37 @@
/* UTKPropertyWindow.uss - 윈도우 스타일 (테마 시스템 적용) */
/*
* UTKPropertyWindowUss.uss
*
* UTKPropertyWindow 컴포넌트의 스타일 정의입니다.
* 테마 지원: var(--color-*) 변수 사용
*
* 다른 Window 컴포넌트(UTKTreeListWindow, UTKAccordionListWindow)와
* 동일한 스타일 가이드를 따릅니다.
*/
/* ============================================
윈도우 루트 (Window Root)
============================================ */
.utk-property-window {
position: absolute;
background-color: var(--color-bg-panel);
border-radius: 8px;
border-width: 1px;
border-color: var(--color-border);
min-width: 400px;
min-height: 200px;
/* box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); */ /* Unity USS does not support box-shadow */
flex-grow: 1;
height: 100%;
min-width: 390px;
width: 390px;
padding: 10px 20px 25px 20px;
}
/* ============================================
헤더 (Header)
============================================ */
.utk-property-window__header {
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 32px;
padding-left: 10px;
padding-right: 5px;
background-color: var(--color-bg-modal);
border-top-left-radius: 8px;
border-top-right-radius: 8px;
border-bottom-width: 1px;
border-bottom-color: var(--color-border);
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
margin-bottom: 10px;
height: 24px;
flex-shrink: 0;
}
/* UTKLabel 타이틀 스타일 */
@@ -33,42 +41,38 @@
.utk-property-window__title .utk-label__text {
color: var(--color-text-primary);
font-size: var(--font-size-body2);
-unity-font-style: bold;
font-size: var(--font-size-label3);
-unity-font-definition: resource('Fonts/Pretendard/Pretendard-Medium');
-unity-font-style: normal;
margin: 0;
padding: 0;
-unity-text-align: middle-left;
}
/* UTKButton 닫기 버튼 스타일 */
/* ============================================
닫기 버튼 (Close Button) - UTKButton 스타일 오버라이드
============================================ */
.utk-property-window__close-btn {
width: 24px;
height: 24px;
min-width: 24px;
width: 22px;
height: 22px;
min-width: 22px;
min-height: 22px;
border-width: 0;
padding: 0;
margin: 0;
border-radius: var(--radius-s);
align-self: center;
display: none; /* 기본 숨김, 필요시 flex로 변경 */
}
.utk-property-window__close-btn .utk-button__icon {
color: var(--color-text-secondary);
}
.utk-property-window__close-btn:hover {
background-color: var(--color-btn-hover);
}
.utk-property-window__close-btn:hover .utk-button__icon {
color: var(--color-text-primary);
}
.utk-property-window__close-btn:active {
background-color: rgba(255, 0, 0, 0.3);
}
/* ============================================
콘텐츠 (Content)
============================================ */
.utk-property-window__content {
flex-grow: 1;
}
/* PropertyList 내부 스타일 오버라이드 */
.utk-property-window .utk-property-list {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
#unity-content-viewport {
padding-right: 4px; /* 스크롤바 여유 공간 */
}

View File

@@ -24,6 +24,8 @@
- UTKComponents.uss에서 전역 스타일 정의됨
=================================== */
/* 여기서 변수 선언 하는 이유는 Dropdown의 팝업에 테마가 적용되지 않기에
별도로 지정하기 위함입니다. */
.unity-base-dropdown * {
--color-base-01: #FFFFFF;
--color-base-02: #F8F8F8;

View File

@@ -61,7 +61,7 @@
Component Sizes (size-*)
=================================== */
--size-btn-height: 24px;
--size-btn-min-width: 80px;
--size-btn-min-width: 64px;
--size-input-height: 24px;
--size-icon-btn: 20px;
--size-nav-btn: 24px;

View File

@@ -93,8 +93,157 @@ namespace UVC.Sample.UIToolkit
{
var entries = new List<IUTKPropertyEntry>();
// 기본 속성 그룹
var basicGroup = new UTKPropertyGroup("basic", "Basic Properties");
// ===== Group에 속하지 않은 개별 아이템들 =====
// String (편집 가능)
entries.Add(new UTKStringPropertyItem("string", "String", "Editable text"));
// String (읽기 전용)
var roString = new UTKStringPropertyItem("string_ro", "String (RO)", "Read-only text", isReadOnly: true);
entries.Add(roString);
// Bool (편집 가능)
entries.Add(new UTKBoolPropertyItem("bool", "Bool", true));
// Bool (읽기 전용)
var roBool = new UTKBoolPropertyItem("bool_ro", "Bool (RO)", true);
roBool.IsReadOnly = true;
entries.Add(roBool);
// Int (편집 가능)
entries.Add(new UTKIntPropertyItem("int", "Int", 42, 0, 100, true));
// Int (읽기 전용)
var roInt = new UTKIntPropertyItem("int_ro", "Int (RO)", 99, 0, 100, true);
roInt.IsReadOnly = true;
entries.Add(roInt);
// Float (편집 가능)
entries.Add(new UTKFloatPropertyItem("float", "Float", 3.14f, 0f, 10f, true));
// Float (읽기 전용)
var roFloat = new UTKFloatPropertyItem("float_ro", "Float (RO)", 2.71f, 0f, 10f, true);
roFloat.IsReadOnly = true;
entries.Add(roFloat);
// Vector2 (편집 가능)
entries.Add(new UTKVector2PropertyItem("vec2", "Vector2", new Vector2(1, 2)));
// Vector2 (읽기 전용)
var roVec2 = new UTKVector2PropertyItem("vec2_ro", "Vector2 (RO)", new Vector2(3, 4));
roVec2.IsReadOnly = true;
entries.Add(roVec2);
// Vector3 (편집 가능)
entries.Add(new UTKVector3PropertyItem("vec3", "Vector3", new Vector3(1, 2, 3)));
// Vector3 (읽기 전용)
var roVec3 = new UTKVector3PropertyItem("vec3_ro", "Vector3 (RO)", new Vector3(4, 5, 6));
roVec3.IsReadOnly = true;
entries.Add(roVec3);
// Color (편집 가능)
entries.Add(new UTKColorPropertyItem("color", "Color", Color.red));
// Color (읽기 전용)
var roColor = new UTKColorPropertyItem("color_ro", "Color (RO)", Color.green, true);
roColor.IsReadOnly = true;
entries.Add(roColor);
// ColorState (편집 가능)
entries.Add(new UTKColorStatePropertyItem("colorstate", "ColorState",
new UTKColorState("Normal", Color.blue)));
// ColorState (읽기 전용)
var roColorState = new UTKColorStatePropertyItem("colorstate_ro", "ColorState (RO)",
new UTKColorState("Locked", Color.gray));
roColorState.IsReadOnly = true;
entries.Add(roColorState);
// Date (편집 가능)
entries.Add(new UTKDatePropertyItem("date", "Date", DateTime.Today));
// Date (읽기 전용)
var roDate = new UTKDatePropertyItem("date_ro", "Date (RO)", DateTime.Today.AddDays(7));
roDate.IsReadOnly = true;
entries.Add(roDate);
// DateTime (편집 가능)
entries.Add(new UTKDateTimePropertyItem("datetime", "DateTime", DateTime.Now));
// DateTime (읽기 전용)
var roDateTime = new UTKDateTimePropertyItem("datetime_ro", "DateTime (RO)", DateTime.Now.AddHours(1));
roDateTime.IsReadOnly = true;
entries.Add(roDateTime);
// DateRange (편집 가능)
entries.Add(new UTKDateRangePropertyItem("daterange", "DateRange",
DateTime.Today, DateTime.Today.AddDays(7)));
// DateRange (읽기 전용)
var roDateRange = new UTKDateRangePropertyItem("daterange_ro", "DateRange (RO)",
DateTime.Today.AddDays(10), DateTime.Today.AddDays(20));
roDateRange.IsReadOnly = true;
entries.Add(roDateRange);
// DateTimeRange (편집 가능)
entries.Add(new UTKDateTimeRangePropertyItem("datetimerange", "DateTimeRange",
DateTime.Now, DateTime.Now.AddHours(2)));
// DateTimeRange (읽기 전용)
var roDateTimeRange = new UTKDateTimeRangePropertyItem("datetimerange_ro", "DateTimeRange (RO)",
DateTime.Now.AddHours(3), DateTime.Now.AddHours(5));
roDateTimeRange.IsReadOnly = true;
entries.Add(roDateTimeRange);
// Enum (편집 가능)
entries.Add(new UTKEnumPropertyItem("enum", "Enum", SampleLayer.Default));
// Enum (읽기 전용)
var roEnum = new UTKEnumPropertyItem("enum_ro", "Enum (RO)", SampleLayer.Water);
roEnum.IsReadOnly = true;
entries.Add(roEnum);
// Dropdown (편집 가능)
entries.Add(new UTKDropdownPropertyItem("dropdown", "Dropdown",
new List<string> { "Option A", "Option B", "Option C" }, "Option A"));
// Dropdown (읽기 전용)
var roDropdown = new UTKDropdownPropertyItem("dropdown_ro", "Dropdown (RO)",
new List<string> { "Option X", "Option Y", "Option Z" }, "Option Y");
roDropdown.IsReadOnly = true;
entries.Add(roDropdown);
// Radio (편집 가능)
entries.Add(new UTKRadioPropertyItem("radio", "Radio",
new List<string> { "Choice 1", "Choice 2", "Choice 3" }, 0));
// Radio (읽기 전용)
var roRadio = new UTKRadioPropertyItem("radio_ro", "Radio (RO)",
new List<string> { "Choice A", "Choice B", "Choice C" }, 1);
roRadio.IsReadOnly = true;
entries.Add(roRadio);
// IntRange (편집 가능)
entries.Add(new UTKIntRangePropertyItem("intrange", "IntRange", 10, 90));
// IntRange (읽기 전용)
var roIntRange = new UTKIntRangePropertyItem("intrange_ro", "IntRange (RO)", 20, 80);
roIntRange.IsReadOnly = true;
entries.Add(roIntRange);
// FloatRange (편집 가능)
entries.Add(new UTKFloatRangePropertyItem("floatrange", "FloatRange", 1.5f, 8.5f));
// FloatRange (읽기 전용)
var roFloatRange = new UTKFloatRangePropertyItem("floatrange_ro", "FloatRange (RO)", 2.5f, 7.5f);
roFloatRange.IsReadOnly = true;
entries.Add(roFloatRange);
// ===== Group에 속한 아이템들 =====
// 기본 속성 그룹 (편집 가능)
var basicGroup = new UTKPropertyGroup("basic", "Basic Properties (Editable)");
basicGroup.AddItem(new UTKStringPropertyItem("name", "Name", "Sample Object"));
basicGroup.AddItem(new UTKStringPropertyItem("description", "Description", "This is a sample object") { IsMultiline = true });
basicGroup.AddItem(new UTKBoolPropertyItem("active", "Is Active", true));
@@ -102,29 +251,99 @@ namespace UVC.Sample.UIToolkit
basicGroup.AddItem(new UTKFloatPropertyItem("speed", "Speed", 1.5f, 0f, 10f, true));
entries.Add(basicGroup);
// Transform 그룹
var transformGroup = new UTKPropertyGroup("transform", "Transform");
// 기본 속성 그룹 (읽기 전용)
var basicGroupRO = new UTKPropertyGroup("basic_ro", "Basic Properties (ReadOnly)");
var roName = new UTKStringPropertyItem("name_ro", "Name", "Locked Object");
roName.IsReadOnly = true;
basicGroupRO.AddItem(roName);
var roDesc = new UTKStringPropertyItem("desc_ro", "Description", "Cannot be modified") { IsMultiline = true };
roDesc.IsReadOnly = true;
basicGroupRO.AddItem(roDesc);
var roActive = new UTKBoolPropertyItem("active_ro", "Is Active", false);
roActive.IsReadOnly = true;
basicGroupRO.AddItem(roActive);
var roCount = new UTKIntPropertyItem("count_ro", "Count", 50, 0, 100, true);
roCount.IsReadOnly = true;
basicGroupRO.AddItem(roCount);
var roSpeed = new UTKFloatPropertyItem("speed_ro", "Speed", 5.5f, 0f, 10f, true);
roSpeed.IsReadOnly = true;
basicGroupRO.AddItem(roSpeed);
entries.Add(basicGroupRO);
// Transform 그룹 (편집 가능)
var transformGroup = new UTKPropertyGroup("transform", "Transform (Editable)");
transformGroup.AddItem(new UTKVector3PropertyItem("position", "Position", new Vector3(0, 1, 0)));
transformGroup.AddItem(new UTKVector3PropertyItem("rotation", "Rotation", Vector3.zero));
transformGroup.AddItem(new UTKVector3PropertyItem("scale", "Scale", Vector3.one));
transformGroup.AddItem(new UTKVector2PropertyItem("uv", "UV Offset", new Vector2(0.5f, 0.5f)));
entries.Add(transformGroup);
// Appearance 그룹
var appearanceGroup = new UTKPropertyGroup("appearance", "Appearance");
// Transform 그룹 (읽기 전용)
var transformGroupRO = new UTKPropertyGroup("transform_ro", "Transform (ReadOnly)");
var roPos = new UTKVector3PropertyItem("position_ro", "Position", new Vector3(10, 20, 30));
roPos.IsReadOnly = true;
transformGroupRO.AddItem(roPos);
var roRot = new UTKVector3PropertyItem("rotation_ro", "Rotation", new Vector3(90, 0, 0));
roRot.IsReadOnly = true;
transformGroupRO.AddItem(roRot);
var roScale = new UTKVector3PropertyItem("scale_ro", "Scale", new Vector3(2, 2, 2));
roScale.IsReadOnly = true;
transformGroupRO.AddItem(roScale);
var roUV = new UTKVector2PropertyItem("uv_ro", "UV Offset", new Vector2(0.25f, 0.75f));
roUV.IsReadOnly = true;
transformGroupRO.AddItem(roUV);
entries.Add(transformGroupRO);
// Appearance 그룹 (편집 가능)
var appearanceGroup = new UTKPropertyGroup("appearance", "Appearance (Editable)");
appearanceGroup.AddItem(new UTKColorPropertyItem("mainColor", "Main Color", Color.blue));
appearanceGroup.AddItem(new UTKColorPropertyItem("emissionColor", "Emission Color", Color.yellow, true));
appearanceGroup.AddItem(new UTKColorStatePropertyItem("status", "Status", new UTKColorState("Active", Color.green)));
appearanceGroup.AddItem(new UTKFloatPropertyItem("alpha", "Alpha", 1f, 0f, 1f, true));
entries.Add(appearanceGroup);
// Date/Time 그룹
var dateGroup = new UTKPropertyGroup("datetime", "Date & Time");
// Appearance 그룹 (읽기 전용)
var appearanceGroupRO = new UTKPropertyGroup("appearance_ro", "Appearance (ReadOnly)");
var roMainColor = new UTKColorPropertyItem("mainColor_ro", "Main Color", Color.red);
roMainColor.IsReadOnly = true;
appearanceGroupRO.AddItem(roMainColor);
var roEmission = new UTKColorPropertyItem("emissionColor_ro", "Emission Color", Color.cyan, true);
roEmission.IsReadOnly = true;
appearanceGroupRO.AddItem(roEmission);
var roStatus = new UTKColorStatePropertyItem("status_ro", "Status", new UTKColorState("Disabled", Color.gray));
roStatus.IsReadOnly = true;
appearanceGroupRO.AddItem(roStatus);
var roAlpha = new UTKFloatPropertyItem("alpha_ro", "Alpha", 0.5f, 0f, 1f, true);
roAlpha.IsReadOnly = true;
appearanceGroupRO.AddItem(roAlpha);
entries.Add(appearanceGroupRO);
// Date/Time 그룹 (편집 가능)
var dateGroup = new UTKPropertyGroup("datetime", "Date & Time (Editable)");
dateGroup.AddItem(new UTKDatePropertyItem("createdDate", "Created Date", DateTime.Today.AddDays(-30)));
dateGroup.AddItem(new UTKDateTimePropertyItem("lastModified", "Last Modified", DateTime.Now));
dateGroup.AddItem(new UTKDateRangePropertyItem("validPeriod", "Valid Period", DateTime.Today, DateTime.Today.AddMonths(1)));
dateGroup.AddItem(new UTKDateTimeRangePropertyItem("sessionPeriod", "Session Period", DateTime.Now, DateTime.Now.AddHours(2)));
entries.Add(dateGroup);
// Selection 그룹
var selectionGroup = new UTKPropertyGroup("selection", "Selection");
// Date/Time 그룹 (읽기 전용)
var dateGroupRO = new UTKPropertyGroup("datetime_ro", "Date & Time (ReadOnly)");
var roCreated = new UTKDatePropertyItem("createdDate_ro", "Created Date", DateTime.Today.AddDays(-60));
roCreated.IsReadOnly = true;
dateGroupRO.AddItem(roCreated);
var roModified = new UTKDateTimePropertyItem("lastModified_ro", "Last Modified", DateTime.Now.AddDays(-1));
roModified.IsReadOnly = true;
dateGroupRO.AddItem(roModified);
var roValid = new UTKDateRangePropertyItem("validPeriod_ro", "Valid Period", DateTime.Today.AddMonths(-1), DateTime.Today);
roValid.IsReadOnly = true;
dateGroupRO.AddItem(roValid);
var roSession = new UTKDateTimeRangePropertyItem("sessionPeriod_ro", "Session Period", DateTime.Now.AddHours(-2), DateTime.Now);
roSession.IsReadOnly = true;
dateGroupRO.AddItem(roSession);
entries.Add(dateGroupRO);
// Selection 그룹 (편집 가능)
var selectionGroup = new UTKPropertyGroup("selection", "Selection (Editable)");
selectionGroup.AddItem(new UTKEnumPropertyItem("layer", "Layer", SampleLayer.Default));
selectionGroup.AddItem(new UTKDropdownPropertyItem("tag", "Tag",
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Player"));
@@ -132,29 +351,36 @@ namespace UVC.Sample.UIToolkit
new List<string> { "Low", "Medium", "High", "Ultra" }, 2));
entries.Add(selectionGroup);
// Range 그룹
var rangeGroup = new UTKPropertyGroup("range", "Range Properties");
// Selection 그룹 (읽기 전용)
var selectionGroupRO = new UTKPropertyGroup("selection_ro", "Selection (ReadOnly)");
var roLayer = new UTKEnumPropertyItem("layer_ro", "Layer", SampleLayer.UI);
roLayer.IsReadOnly = true;
selectionGroupRO.AddItem(roLayer);
var roTag = new UTKDropdownPropertyItem("tag_ro", "Tag",
new List<string> { "Untagged", "Player", "Enemy", "Item", "Environment" }, "Enemy");
roTag.IsReadOnly = true;
selectionGroupRO.AddItem(roTag);
var roQuality = new UTKRadioPropertyItem("quality_ro", "Quality",
new List<string> { "Low", "Medium", "High", "Ultra" }, 3);
roQuality.IsReadOnly = true;
selectionGroupRO.AddItem(roQuality);
entries.Add(selectionGroupRO);
// Range 그룹 (편집 가능)
var rangeGroup = new UTKPropertyGroup("range", "Range Properties (Editable)");
rangeGroup.AddItem(new UTKIntRangePropertyItem("levelRange", "Level Range", 1, 50));
rangeGroup.AddItem(new UTKFloatRangePropertyItem("damageRange", "Damage Range", 10.5f, 25.0f));
entries.Add(rangeGroup);
// Special 그룹
var specialGroup = new UTKPropertyGroup("special", "Special Properties");
specialGroup.AddItem(new UTKColorStatePropertyItem("status", "Status",
new UTKColorState("Active", Color.green)));
specialGroup.AddItem(new UTKVector2PropertyItem("uv", "UV Offset", new Vector2(0.5f, 0.5f)));
entries.Add(specialGroup);
// 읽기 전용 그룹
var readOnlyGroup = new UTKPropertyGroup("readonly", "Read-Only Properties");
var idItem = new UTKStringPropertyItem("id", "ID", "OBJ-12345");
idItem.IsReadOnly = true;
readOnlyGroup.AddItem(idItem);
var versionItem = new UTKIntPropertyItem("version", "Version", 3);
versionItem.IsReadOnly = true;
readOnlyGroup.AddItem(versionItem);
entries.Add(readOnlyGroup);
// Range 그룹 (읽기 전용)
var rangeGroupRO = new UTKPropertyGroup("range_ro", "Range Properties (ReadOnly)");
var roLevelRange = new UTKIntRangePropertyItem("levelRange_ro", "Level Range", 25, 75);
roLevelRange.IsReadOnly = true;
rangeGroupRO.AddItem(roLevelRange);
var roDamageRange = new UTKFloatRangePropertyItem("damageRange_ro", "Damage Range", 50.0f, 100.0f);
roDamageRange.IsReadOnly = true;
rangeGroupRO.AddItem(roDamageRange);
entries.Add(rangeGroupRO);
return entries;
}

View File

@@ -114,7 +114,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnRadioValueChanged);
RegisterCallback<ChangeEvent<bool>>(OnRadioValueChanged);
}
private void SubscribeToThemeChanges()
@@ -168,6 +168,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<bool>>(OnRadioValueChanged);
}
#endregion
}

View File

@@ -17,6 +17,19 @@ namespace UVC.UIToolkit
/// toggle.label = "알림 받기";
/// toggle.IsOn = true;
/// toggle.OnValueChanged += (isOn) => Debug.Log($"토글: {isOn}");
///
/// // 상호작용 불가능 (읽기 전용, 프로그래밍 방식으로만 변경 가능)
/// var readOnlyToggle = new UTKToggle("읽기 전용 토글");
/// readOnlyToggle.IsOn = true;
/// readOnlyToggle.IsInteractive = false; // 사용자 클릭/키보드 입력 차단
/// readOnlyToggle.SetOn(false, true); // 프로그래밍 방식으로는 변경 가능
///
/// // IsEnabled vs IsInteractive
/// var disabledToggle = new UTKToggle("완전 비활성화");
/// disabledToggle.IsEnabled = false; // 시각적 비활성화 + 모든 변경 불가
///
/// var nonInteractiveToggle = new UTKToggle("상호작용 비활성화");
/// nonInteractiveToggle.IsInteractive = false; // 사용자 입력만 차단, 시각적 활성화 유지
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code>
@@ -29,6 +42,9 @@ namespace UVC.UIToolkit
///
/// <!-- 비활성화 -->
/// <utk:UTKToggle label="프리미엄 기능" is-enabled="false" />
///
/// <!-- 상호작용 불가능 (읽기 전용) -->
/// <utk:UTKToggle label="시스템 상태" value="true" is-interactive="false" />
/// </ui:UXML>
/// </code>
/// </example>
@@ -42,6 +58,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isInteractive = true;
#endregion
#region Events
@@ -70,6 +87,22 @@ namespace UVC.UIToolkit
EnableInClassList("utk-toggle--disabled", !value);
}
}
/// <summary>
/// 상호작용 가능 여부. false일 경우 마우스/키보드 입력을 무시하지만 프로그래밍 방식으로는 값 변경 가능.
/// IsEnabled와 달리 시각적으로는 활성화 상태를 유지합니다.
/// </summary>
[UxmlAttribute("is-interactive")]
public bool IsInteractive
{
get => _isInteractive;
set
{
_isInteractive = value;
UpdateInteractiveState();
EnableInClassList("utk-toggle--non-interactive", !value);
}
}
#endregion
#region Constructor
@@ -102,7 +135,9 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnToggleValueChanged);
RegisterCallback<ChangeEvent<bool>>(OnToggleValueChanged);
RegisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
RegisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
}
private void SubscribeToThemeChanges()
@@ -126,6 +161,28 @@ namespace UVC.UIToolkit
EnableInClassList("utk-toggle--on", evt.newValue);
OnValueChanged?.Invoke(evt.newValue);
}
private void OnMouseDown(MouseDownEvent evt)
{
if (!_isInteractive)
{
evt.StopImmediatePropagation();
evt.PreventDefault();
}
}
private void OnKeyDown(KeyDownEvent evt)
{
if (!_isInteractive)
{
// Space나 Enter 키로 토글 변경 방지
if (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
{
evt.StopImmediatePropagation();
evt.PreventDefault();
}
}
}
#endregion
#region Methods
@@ -144,6 +201,15 @@ namespace UVC.UIToolkit
OnValueChanged?.Invoke(newValue);
}
}
/// <summary>
/// 상호작용 상태 업데이트
/// </summary>
private void UpdateInteractiveState()
{
// IsInteractive가 false일 때 포커스 불가능하게 설정
focusable = _isInteractive;
}
#endregion
#region IDisposable
@@ -154,6 +220,9 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<bool>>(OnToggleValueChanged);
UnregisterCallback<MouseDownEvent>(OnMouseDown, TrickleDown.TrickleDown);
UnregisterCallback<KeyDownEvent>(OnKeyDown, TrickleDown.TrickleDown);
}
#endregion
}

View File

@@ -98,7 +98,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnValueChanged);
RegisterCallback<ChangeEvent<ToggleButtonGroupState>>(OnValueChanged);
}
private void SubscribeToThemeChanges()
@@ -157,6 +157,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnSelectionChanged = null;
UnregisterCallback<ChangeEvent<ToggleButtonGroupState>>(OnValueChanged);
}
#endregion
}

View File

@@ -126,7 +126,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFoldoutValueChanged);
RegisterCallback<ChangeEvent<bool>>(OnFoldoutValueChanged);
}
private void SubscribeToThemeChanges()
@@ -159,6 +159,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<bool>>(OnFoldoutValueChanged);
}
#endregion
}

View File

@@ -163,7 +163,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnDropdownValueChanged);
RegisterCallback<ChangeEvent<string>>(OnDropdownValueChanged);
}
private void SubscribeToThemeChanges()
@@ -240,6 +240,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnSelectionChanged = null;
UnregisterCallback<ChangeEvent<string>>(OnDropdownValueChanged);
}
#endregion
}

View File

@@ -172,7 +172,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Enum>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -204,6 +204,7 @@ namespace UVC.UIToolkit
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
UnregisterCallback<ChangeEvent<Enum>>(OnFieldValueChanged);
OnValueChanged = null;
}
#endregion

View File

@@ -46,6 +46,11 @@ namespace UVC.UIToolkit
///
/// // 비활성화
/// boundsField.IsEnabled = false;
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyField = new UTKBoundsField("고정 경계");
/// readOnlyField.Value = new Bounds(Vector3.zero, Vector3.one);
/// readOnlyField.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code>
@@ -61,7 +66,10 @@ namespace UVC.UIToolkit
/// extents-label="크기" />
///
/// <!-- 비활성화 -->
/// <utk:UTKBoundsField label="읽기전용" is-enabled="false" />
/// <utk:UTKBoundsField label="비활성화" is-enabled="false" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKBoundsField label="고정 경계" is-readonly="true" />
/// </UXML>
/// </code>
/// <para><b>실제 활용 예시:</b></para>
@@ -88,6 +96,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isReadOnly;
private string _centerLabel = "Center";
private string _extentsLabel = "Extents";
private string _xLabel = "X";
@@ -180,6 +189,19 @@ namespace UVC.UIToolkit
UpdateAxisLabels();
}
}
/// <summary>읽기 전용 상태</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-boundsfield--readonly", value);
}
}
#endregion
#region Constructor
@@ -215,7 +237,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Bounds>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -254,6 +276,16 @@ namespace UVC.UIToolkit
}
}
}
private void UpdateReadOnlyState()
{
// 내부 Vector3Field들의 TextInput을 찾아서 읽기 전용 설정
var textInputs = this.Query<TextInputBaseField<float>>().ToList();
foreach (var textInput in textInputs)
{
textInput.isReadOnly = _isReadOnly;
}
}
#endregion
#region Event Handlers
@@ -271,6 +303,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Bounds>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -132,7 +132,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<double>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -165,6 +165,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<double>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -131,7 +131,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<float>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -164,6 +164,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<float>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -198,7 +198,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnTextValueChanged);
RegisterCallback<ChangeEvent<string>>(OnTextValueChanged);
RegisterCallback<FocusInEvent>(_ =>
{
@@ -304,6 +304,7 @@ namespace UVC.UIToolkit
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
UnregisterCallback<ChangeEvent<string>>(OnTextValueChanged);
OnValueChanged = null;
OnFocused = null;
OnBlurred = null;

View File

@@ -130,7 +130,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<int>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -163,6 +163,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<int>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -124,7 +124,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<long>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -157,6 +157,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<long>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -51,6 +51,10 @@ namespace UVC.UIToolkit
/// stepper.Increment(); // Step만큼 증가
/// stepper.Decrement(); // Step만큼 감소
/// stepper.SetValue(75); // 직접 설정
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyStepper = new UTKNumberStepper(0, 100, 50, 1);
/// readOnlyStepper.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code><![CDATA[
@@ -62,6 +66,9 @@ namespace UVC.UIToolkit
///
/// <!-- 순환 모드 -->
/// <utk:UTKNumberStepper value="1" min-value="1" max-value="12" wrap-around="true" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKNumberStepper value="50" is-readonly="true" />
/// ]]></code>
/// <para><b>실제 활용 예시 (월 선택기):</b></para>
/// <code>
@@ -74,7 +81,7 @@ namespace UVC.UIToolkit
/// </code>
/// </example>
[UxmlElement]
public partial class UTKNumberStepper : VisualElement
public partial class UTKNumberStepper : VisualElement, IDisposable
{
#region Constants
private const string USS_PATH = "UIToolkit/Input/UTKNumberStepper";
@@ -123,9 +130,24 @@ namespace UVC.UIToolkit
get => _wrapAround;
set => _wrapAround = value;
}
/// <summary>읽기 전용 상태. true일 때 사용자가 값을 수정할 수 없음</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-number-stepper--readonly", value);
}
}
#endregion
#region Fields
private bool _disposed;
private bool _isReadOnly;
private int _value;
private int _minValue = int.MinValue;
private int _maxValue = int.MaxValue;
@@ -151,6 +173,7 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKNumberStepper()
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
@@ -160,19 +183,18 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
public UTKNumberStepper(int minValue, int maxValue, int initialValue = 0, int step = 1)
public UTKNumberStepper(bool isReadOnly = false): this()
{
_isReadOnly = isReadOnly;
}
public UTKNumberStepper(int minValue, int maxValue, int initialValue = 0, int step = 1, bool isReadOnly = false): this()
{
_isReadOnly = isReadOnly;
_minValue = minValue;
_maxValue = maxValue;
_step = Math.Max(1, step);
_value = Mathf.Clamp(initialValue, minValue, maxValue);
UTKThemeManager.Instance.ApplyThemeToElement(this);
LoadStyleSheet();
CreateUI();
SetupEvents();
SubscribeToThemeChanges();
UpdateDisplay();
}
private void LoadStyleSheet()
@@ -269,6 +291,7 @@ namespace UVC.UIToolkit
// Text Field
_textField = new TextField { name = "stepper-input" };
_textField.AddToClassList("utk-number-stepper__input");
_textField.isReadOnly = _isReadOnly;
// TextField 내부 input 스타일링
_textField.RegisterCallback<AttachToPanelEvent>(_ =>
@@ -290,6 +313,7 @@ namespace UVC.UIToolkit
_upButton = new Button { name = "stepper-up", text = UTKMaterialIcons.KeyboardArrowUp };
_upButton.AddToClassList("utk-number-stepper__btn");
_upButton.AddToClassList("utk-number-stepper__btn--up");
_upButton.SetEnabled(!_isReadOnly);
UTKMaterialIcons.ApplyIconStyle(_upButton, 14);
buttonContainer.Add(_upButton);
@@ -297,6 +321,7 @@ namespace UVC.UIToolkit
_downButton = new Button { name = "stepper-down", text = UTKMaterialIcons.KeyboardArrowDown };
_downButton.AddToClassList("utk-number-stepper__btn");
_downButton.AddToClassList("utk-number-stepper__btn--down");
_downButton.SetEnabled(!_isReadOnly);
UTKMaterialIcons.ApplyIconStyle(_downButton, 14);
buttonContainer.Add(_downButton);
@@ -307,78 +332,85 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
_upButton?.RegisterCallback<ClickEvent>(_ => Increment());
_downButton?.RegisterCallback<ClickEvent>(_ => Decrement());
_upButton?.RegisterCallback<ClickEvent>(OnUpButtonClick);
_downButton?.RegisterCallback<ClickEvent>(OnDownButtonClick);
_textField?.RegisterValueChangedCallback(evt =>
_textField?.RegisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
_textField?.RegisterCallback<KeyDownEvent>(OnTextFieldKeyDown);
_textField?.RegisterCallback<KeyDownEvent>(OnTextFieldTabKeyDown, TrickleDown.TrickleDown);
RegisterCallback<MouseEnterEvent>(OnMouseEnter);
RegisterCallback<MouseLeaveEvent>(OnMouseLeave);
RegisterCallback<WheelEvent>(OnWheelEvent);
}
#endregion
#region Event Handlers
private void OnUpButtonClick(ClickEvent evt) => Increment();
private void OnDownButtonClick(ClickEvent evt) => Decrement();
private void OnTextFieldChanged(ChangeEvent<string> evt)
{
if (_isUpdating) return;
if (int.TryParse(evt.newValue, out int parsed))
{
if (_isUpdating) return;
if (int.TryParse(evt.newValue, out int parsed))
{
SetValue(parsed);
}
else
{
// 유효하지 않은 입력이면 이전 값으로 복원
UpdateDisplay();
}
});
// 키보드 이벤트 (위/아래 화살표)
_textField?.RegisterCallback<KeyDownEvent>(evt =>
SetValue(parsed);
}
else
{
if (evt.keyCode == KeyCode.UpArrow)
{
Increment();
evt.StopPropagation();
}
else if (evt.keyCode == KeyCode.DownArrow)
{
Decrement();
evt.StopPropagation();
}
});
// 유효하지 않은 입력이면 이전 값으로 복원
UpdateDisplay();
}
}
// Tab 키 이벤트 - TrickleDown으로 먼저 캡처
_textField?.RegisterCallback<KeyDownEvent>(evt =>
private void OnTextFieldKeyDown(KeyDownEvent evt)
{
if (evt.keyCode == KeyCode.UpArrow)
{
if (evt.keyCode == KeyCode.Tab)
{
if (evt.shiftKey && OnShiftTabPressed != null)
{
// Shift+Tab: 이전 요소로 이동
OnShiftTabPressed.Invoke();
evt.StopImmediatePropagation();
}
else if (!evt.shiftKey && OnTabPressed != null)
{
// Tab: 다음 요소로 이동
OnTabPressed.Invoke();
evt.StopImmediatePropagation();
}
}
}, TrickleDown.TrickleDown);
// 마우스 호버 상태 추적
RegisterCallback<MouseEnterEvent>(_ => _isHovered = true);
RegisterCallback<MouseLeaveEvent>(_ => _isHovered = false);
// 마우스 휠 이벤트 (호버 상태에서만)
RegisterCallback<WheelEvent>(evt =>
{
if (!_isHovered) return;
if (evt.delta.y < 0)
{
Increment();
}
else if (evt.delta.y > 0)
{
Decrement();
}
Increment();
evt.StopPropagation();
});
}
else if (evt.keyCode == KeyCode.DownArrow)
{
Decrement();
evt.StopPropagation();
}
}
private void OnTextFieldTabKeyDown(KeyDownEvent evt)
{
if (evt.keyCode == KeyCode.Tab)
{
if (evt.shiftKey && OnShiftTabPressed != null)
{
OnShiftTabPressed.Invoke();
evt.StopImmediatePropagation();
}
else if (!evt.shiftKey && OnTabPressed != null)
{
OnTabPressed.Invoke();
evt.StopImmediatePropagation();
}
}
}
private void OnMouseEnter(MouseEnterEvent evt) => _isHovered = true;
private void OnMouseLeave(MouseLeaveEvent evt) => _isHovered = false;
private void OnWheelEvent(WheelEvent evt)
{
if (!_isHovered) return;
if (evt.delta.y < 0)
{
Increment();
}
else if (evt.delta.y > 0)
{
Decrement();
}
evt.StopPropagation();
}
#endregion
@@ -401,6 +433,56 @@ namespace UVC.UIToolkit
{
return Mathf.Clamp(value, _minValue, _maxValue);
}
private void UpdateReadOnlyState()
{
if (_textField != null)
{
_textField.isReadOnly = _isReadOnly;
}
if (_upButton != null)
{
_upButton.SetEnabled(!_isReadOnly);
}
if (_downButton != null)
{
_downButton.SetEnabled(!_isReadOnly);
}
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
// 이벤트 콜백 해제
_upButton?.UnregisterCallback<ClickEvent>(OnUpButtonClick);
_downButton?.UnregisterCallback<ClickEvent>(OnDownButtonClick);
_textField?.UnregisterCallback<ChangeEvent<string>>(OnTextFieldChanged);
_textField?.UnregisterCallback<KeyDownEvent>(OnTextFieldKeyDown);
_textField?.UnregisterCallback<KeyDownEvent>(OnTextFieldTabKeyDown);
UnregisterCallback<MouseEnterEvent>(OnMouseEnter);
UnregisterCallback<MouseLeaveEvent>(OnMouseLeave);
UnregisterCallback<WheelEvent>(OnWheelEvent);
// 이벤트 null 처리
OnValueChanged = null;
OnTabPressed = null;
OnShiftTabPressed = null;
// UI 참조 정리
_textField = null;
_upButton = null;
_downButton = null;
}
#endregion
}
}

View File

@@ -41,6 +41,11 @@ namespace UVC.UIToolkit
/// rectField.YLabel = "Top";
/// rectField.WLabel = "Width";
/// rectField.HLabel = "Height";
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyField = new UTKRectField("고정 영역");
/// readOnlyField.Value = new Rect(10, 10, 200, 100);
/// readOnlyField.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code><![CDATA[
@@ -53,7 +58,10 @@ namespace UVC.UIToolkit
/// w-label="W" h-label="H" />
///
/// <!-- 비활성화 상태 -->
/// <utk:UTKRectField label="읽기 전용" is-enabled="false" />
/// <utk:UTKRectField label="비활성화" is-enabled="false" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKRectField label="고정 영역" is-readonly="true" />
/// ]]></code>
/// <para><b>실제 활용 예시 (스프라이트 영역 편집):</b></para>
/// <code>
@@ -76,6 +84,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isReadOnly;
private string _xLabel = "X";
private string _yLabel = "Y";
private string _wLabel = "W";
@@ -155,6 +164,19 @@ namespace UVC.UIToolkit
UpdateAxisLabels();
}
}
/// <summary>읽기 전용 상태</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-rectfield--readonly", value);
}
}
#endregion
#region Constructor
@@ -173,9 +195,17 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
public UTKRectField(string label) : this()
public UTKRectField(bool isReadOnly) : this()
{
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
public UTKRectField(string label, bool isReadOnly = false) : this()
{
this.label = label;
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
#endregion
@@ -190,7 +220,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Rect>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -219,6 +249,16 @@ namespace UVC.UIToolkit
floatFields[3].label = _hLabel;
}
}
private void UpdateReadOnlyState()
{
// 내부 FloatField들의 TextInput을 찾아서 읽기 전용 설정
var textInputs = this.Query<TextInputBaseField<float>>().ToList();
foreach (var textInput in textInputs)
{
textInput.isReadOnly = _isReadOnly;
}
}
#endregion
#region Event Handlers
@@ -236,6 +276,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Rect>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -38,6 +38,11 @@ namespace UVC.UIToolkit
///
/// // 비활성화
/// vec2Field.IsEnabled = false;
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyField = new UTKVector2Field("고정 크기");
/// readOnlyField.Value = new Vector2(100, 50);
/// readOnlyField.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code>
@@ -50,7 +55,10 @@ namespace UVC.UIToolkit
/// <utk:UTKVector2Field label="크기" x-label="Width" y-label="Height" />
///
/// <!-- 비활성화 -->
/// <utk:UTKVector2Field label="고정 크기" is-enabled="false" />
/// <utk:UTKVector2Field label="비활성화" is-enabled="false" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKVector2Field label="고정 크기" is-readonly="true" />
/// </UXML>
/// </code>
/// <para><b>실제 활용 예시:</b></para>
@@ -79,6 +87,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isReadOnly = false;
private string _xLabel = "X";
private string _yLabel = "Y";
#endregion
@@ -132,6 +141,19 @@ namespace UVC.UIToolkit
UpdateAxisLabels();
}
}
/// <summary>읽기 전용 상태</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-vector2-field--readonly", value);
}
}
#endregion
#region Constructor
@@ -150,9 +172,17 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
public UTKVector2Field(string label) : this()
public UTKVector2Field(bool isReadOnly) : this()
{
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
public UTKVector2Field(string label, bool isReadOnly = false) : this()
{
this.label = label;
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
#endregion
@@ -167,7 +197,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Vector2>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -194,6 +224,16 @@ namespace UVC.UIToolkit
floatFields[1].label = _yLabel;
}
}
private void UpdateReadOnlyState()
{
// 내부 FloatField들의 TextInput을 찾아서 읽기 전용 설정
var textInputs = this.Query<TextInputBaseField<float>>().ToList();
foreach (var textInput in textInputs)
{
textInput.isReadOnly = _isReadOnly;
}
}
#endregion
#region Event Handlers
@@ -211,6 +251,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Vector2>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -46,6 +46,11 @@ namespace UVC.UIToolkit
/// var posField = new UTKVector3Field("위치");
/// posField.Value = transform.position;
/// posField.OnValueChanged += (pos) => transform.position = pos;
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyField = new UTKVector3Field("고정 위치");
/// readOnlyField.Value = new Vector3(1, 2, 3);
/// readOnlyField.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code><![CDATA[
@@ -57,7 +62,10 @@ namespace UVC.UIToolkit
/// x-label="Width" y-label="Height" z-label="Depth" />
///
/// <!-- 비활성화 상태 -->
/// <utk:UTKVector3Field label="읽기 전용" is-enabled="false" />
/// <utk:UTKVector3Field label="비활성화" is-enabled="false" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKVector3Field label="고정 위치" is-readonly="true" />
/// ]]></code>
/// <para><b>실제 활용 예시 (Transform 편집기):</b></para>
/// <code>
@@ -85,6 +93,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isReadOnly = false;
private string _xLabel = "X";
private string _yLabel = "Y";
private string _zLabel = "Z";
@@ -151,6 +160,19 @@ namespace UVC.UIToolkit
UpdateAxisLabels();
}
}
/// <summary>읽기 전용 상태</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-vector3-field--readonly", value);
}
}
#endregion
#region Constructor
@@ -169,9 +191,17 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
public UTKVector3Field(string label) : this()
public UTKVector3Field(bool isReadOnly) : this()
{
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
public UTKVector3Field(string label, bool isReadOnly = false) : this()
{
this.label = label;
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
#endregion
@@ -186,7 +216,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Vector3>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -214,6 +244,16 @@ namespace UVC.UIToolkit
floatFields[2].label = _zLabel;
}
}
private void UpdateReadOnlyState()
{
// 내부 FloatField들의 TextInput을 찾아서 읽기 전용 설정
var textInputs = this.Query<TextInputBaseField<float>>().ToList();
foreach (var textInput in textInputs)
{
textInput.isReadOnly = _isReadOnly;
}
}
#endregion
#region Event Handlers
@@ -231,6 +271,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Vector3>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -42,6 +42,11 @@ namespace UVC.UIToolkit
///
/// // 비활성화
/// vec4Field.IsEnabled = false;
///
/// // 읽기 전용 (사용자가 수정할 수 없음)
/// var readOnlyField = new UTKVector4Field("고정 파라미터");
/// readOnlyField.Value = new Vector4(1, 0.5f, 0.5f, 1);
/// readOnlyField.IsReadOnly = true;
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code>
@@ -55,7 +60,10 @@ namespace UVC.UIToolkit
/// x-label="R" y-label="G" z-label="B" w-label="A" />
///
/// <!-- 비활성화 -->
/// <utk:UTKVector4Field label="고정값" is-enabled="false" />
/// <utk:UTKVector4Field label="비활성화" is-enabled="false" />
///
/// <!-- 읽기 전용 -->
/// <utk:UTKVector4Field label="고정값" is-readonly="true" />
/// </UXML>
/// </code>
/// <para><b>실제 활용 예시:</b></para>
@@ -82,6 +90,7 @@ namespace UVC.UIToolkit
#region Fields
private bool _disposed;
private bool _isEnabled = true;
private bool _isReadOnly = false;
private string _xLabel = "X";
private string _yLabel = "Y";
private string _zLabel = "Z";
@@ -161,6 +170,19 @@ namespace UVC.UIToolkit
UpdateAxisLabels();
}
}
/// <summary>읽기 전용 상태</summary>
[UxmlAttribute("is-readonly")]
public bool IsReadOnly
{
get => _isReadOnly;
set
{
_isReadOnly = value;
UpdateReadOnlyState();
EnableInClassList("utk-vector4field--readonly", value);
}
}
#endregion
#region Constructor
@@ -179,9 +201,17 @@ namespace UVC.UIToolkit
SubscribeToThemeChanges();
}
public UTKVector4Field(string label) : this()
public UTKVector4Field(bool isReadOnly) : this()
{
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
public UTKVector4Field(string label, bool isReadOnly = false) : this()
{
this.label = label;
_isReadOnly = isReadOnly;
UpdateReadOnlyState();
}
#endregion
@@ -196,7 +226,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnFieldValueChanged);
RegisterCallback<ChangeEvent<Vector4>>(OnFieldValueChanged);
}
private void SubscribeToThemeChanges()
@@ -225,6 +255,16 @@ namespace UVC.UIToolkit
floatFields[3].label = _wLabel;
}
}
private void UpdateReadOnlyState()
{
// 내부 FloatField들의 TextInput을 찾아서 읽기 전용 설정
var textInputs = this.Query<TextInputBaseField<float>>().ToList();
foreach (var textInput in textInputs)
{
textInput.isReadOnly = _isReadOnly;
}
}
#endregion
#region Event Handlers
@@ -242,6 +282,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Vector4>>(OnFieldValueChanged);
}
#endregion
}

View File

@@ -308,6 +308,12 @@ namespace UVC.UIToolkit
#endregion
#region Public Methods
/// <summary>
/// 현재 색상을 설정합니다.
/// </summary>
/// <param name="color"></param>
/// <param name="setAsOriginal"></param>
public void SetColor(Color color, bool setAsOriginal = true)
{
_currentColor = color;
@@ -321,8 +327,15 @@ namespace UVC.UIToolkit
UpdateAllUI();
}
/// <summary>
/// 현재 선택된 색상을 반환합니다.
/// </summary>
/// <returns></returns>
public Color GetColor() => _currentColor;
/// <summary>
/// 피커를 닫고, 정리합니다.
/// </summary>
public void Close()
{
OnClosed?.Invoke();
@@ -331,6 +344,9 @@ namespace UVC.UIToolkit
Dispose();
}
/// <summary>
/// 선택을 취소하고 원래 색상으로 복원합니다.
/// </summary>
public void Cancel()
{
_currentColor = _originalColor;
@@ -338,6 +354,9 @@ namespace UVC.UIToolkit
Close();
}
/// <summary>
/// 현재 색상을 확정하고 선택 이벤트를 발생시킵니다.
/// </summary>
public void Confirm()
{
OnColorSelected?.Invoke(_currentColor);
@@ -535,19 +554,19 @@ namespace UVC.UIToolkit
_hueBar?.RegisterCallback<PointerUpEvent>(OnHueBarPointerUp);
// RGB Sliders
_sliderR?.RegisterValueChangedCallback(evt => OnRGBSliderChanged('R', evt.newValue));
_sliderG?.RegisterValueChangedCallback(evt => OnRGBSliderChanged('G', evt.newValue));
_sliderB?.RegisterValueChangedCallback(evt => OnRGBSliderChanged('B', evt.newValue));
_sliderA?.RegisterValueChangedCallback(evt => OnRGBSliderChanged('A', evt.newValue));
_sliderR?.RegisterCallback<ChangeEvent<float>>(OnSliderRChanged);
_sliderG?.RegisterCallback<ChangeEvent<float>>(OnSliderGChanged);
_sliderB?.RegisterCallback<ChangeEvent<float>>(OnSliderBChanged);
_sliderA?.RegisterCallback<ChangeEvent<float>>(OnSliderAChanged);
// RGB Fields
_fieldR?.RegisterValueChangedCallback(evt => OnRGBFieldChanged('R', evt.newValue));
_fieldG?.RegisterValueChangedCallback(evt => OnRGBFieldChanged('G', evt.newValue));
_fieldB?.RegisterValueChangedCallback(evt => OnRGBFieldChanged('B', evt.newValue));
_fieldA?.RegisterValueChangedCallback(evt => OnRGBFieldChanged('A', evt.newValue));
_fieldR?.RegisterCallback<ChangeEvent<int>>(OnFieldRChanged);
_fieldG?.RegisterCallback<ChangeEvent<int>>(OnFieldGChanged);
_fieldB?.RegisterCallback<ChangeEvent<int>>(OnFieldBChanged);
_fieldA?.RegisterCallback<ChangeEvent<int>>(OnFieldAChanged);
// Hex Field
_hexField?.RegisterValueChangedCallback(OnHexFieldChanged);
_hexField?.RegisterCallback<ChangeEvent<string>>(OnHexFieldChanged);
// Original preview click - 원래 색상으로 복원
_previewOriginal?.RegisterCallback<ClickEvent>(_ => SetColor(_originalColor));
@@ -685,6 +704,16 @@ namespace UVC.UIToolkit
NotifyColorChanged();
}
private void OnSliderRChanged(ChangeEvent<float> evt) => OnRGBSliderChanged('R', evt.newValue);
private void OnSliderGChanged(ChangeEvent<float> evt) => OnRGBSliderChanged('G', evt.newValue);
private void OnSliderBChanged(ChangeEvent<float> evt) => OnRGBSliderChanged('B', evt.newValue);
private void OnSliderAChanged(ChangeEvent<float> evt) => OnRGBSliderChanged('A', evt.newValue);
private void OnFieldRChanged(ChangeEvent<int> evt) => OnRGBFieldChanged('R', evt.newValue);
private void OnFieldGChanged(ChangeEvent<int> evt) => OnRGBFieldChanged('G', evt.newValue);
private void OnFieldBChanged(ChangeEvent<int> evt) => OnRGBFieldChanged('B', evt.newValue);
private void OnFieldAChanged(ChangeEvent<int> evt) => OnRGBFieldChanged('A', evt.newValue);
private void OnRGBSliderChanged(char channel, float value)
{
if (_isUpdating) return;
@@ -1148,6 +1177,19 @@ namespace UVC.UIToolkit
_sliderATexture = null;
}
// 이벤트 콜백 해제
_sliderR?.UnregisterCallback<ChangeEvent<float>>(OnSliderRChanged);
_sliderG?.UnregisterCallback<ChangeEvent<float>>(OnSliderGChanged);
_sliderB?.UnregisterCallback<ChangeEvent<float>>(OnSliderBChanged);
_sliderA?.UnregisterCallback<ChangeEvent<float>>(OnSliderAChanged);
_fieldR?.UnregisterCallback<ChangeEvent<int>>(OnFieldRChanged);
_fieldG?.UnregisterCallback<ChangeEvent<int>>(OnFieldGChanged);
_fieldB?.UnregisterCallback<ChangeEvent<int>>(OnFieldBChanged);
_fieldA?.UnregisterCallback<ChangeEvent<int>>(OnFieldAChanged);
_hexField?.UnregisterCallback<ChangeEvent<string>>(OnHexFieldChanged);
OnColorChanged = null;
OnColorSelected = null;
OnClosed = null;

View File

@@ -533,6 +533,9 @@ namespace UVC.UIToolkit
s_dayNameKeys = DEFAULT_DAY_NAME_KEYS;
}
/// <summary>
/// 피커를 닫고, 정리합니다.
/// </summary>
public void Close()
{
OnClosed?.Invoke();

View File

@@ -83,6 +83,7 @@ namespace UVC.UIToolkit
{
FadeOut(() =>
{
style.display = DisplayStyle.None;
RemoveFromHierarchy();
Dispose();
});

View File

@@ -28,8 +28,8 @@ namespace UVC.UIToolkit
#region Fields
private T _value;
private bool _isReadOnly;
private bool _isVisible = true;
protected bool _isReadOnly;
protected bool _isVisible = true;
private string? _description;
private string? _tooltip;
private string? _groupId;

View File

@@ -18,9 +18,10 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKBoolPropertyItem(string id, string name, bool initialValue = false)
public UTKBoolPropertyItem(string id, string name, bool initialValue = false, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -37,6 +38,7 @@ namespace UVC.UIToolkit
if (_toggle != null)
{
_toggle.IsOn = Value;
_toggle.IsInteractive = !IsReadOnly;
}
return container;
@@ -55,6 +57,7 @@ namespace UVC.UIToolkit
_toggle = new UTKToggle();
_toggle.name = "value-field";
_toggle.IsOn = Value;
_toggle.IsInteractive = !IsReadOnly;
valueContainer.Add(_toggle);
container.Add(valueContainer);
@@ -70,7 +73,7 @@ namespace UVC.UIToolkit
if (_toggle != null)
{
_toggle.IsOn = Value;
_toggle.IsEnabled = !IsReadOnly;
_toggle.IsInteractive = !IsReadOnly;
_toggle.OnValueChanged += OnToggleChanged;
}
}
@@ -100,7 +103,7 @@ namespace UVC.UIToolkit
if (_toggle != null)
{
_toggle.IsEnabled = !IsReadOnly;
_toggle.IsInteractive = !IsReadOnly;
}
}
#endregion

View File

@@ -30,10 +30,11 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKColorPropertyItem(string id, string name, Color initialValue = default, bool useAlpha = false)
public UTKColorPropertyItem(string id, string name, Color initialValue = default, bool useAlpha = false, bool isReadOnly = false)
: base(id, name, initialValue)
{
_useAlpha = useAlpha;
base._isReadOnly = isReadOnly;
}
#endregion
@@ -53,11 +54,18 @@ namespace UVC.UIToolkit
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value;
_colorPreview.SetEnabled(!IsReadOnly);
}
if (_hexField != null)
{
_hexField.Value = ColorToHex(Value);
_hexField.isReadOnly = IsReadOnly;
}
if(_pickerButton != null)
{
_pickerButton.IsEnabled = !IsReadOnly;
}
return container;
@@ -78,6 +86,7 @@ namespace UVC.UIToolkit
_colorPreview.name = "color-preview";
_colorPreview.AddToClassList("utk-property-item__color-preview");
_colorPreview.style.backgroundColor = Value;
_colorPreview.SetEnabled(!IsReadOnly);
valueContainer.Add(_colorPreview);
// Hex 입력
@@ -86,11 +95,13 @@ namespace UVC.UIToolkit
_hexField.Value = ColorToHex(Value);
_hexField.style.width = 80;
_hexField.style.marginLeft = 5;
_hexField.isReadOnly = IsReadOnly;
valueContainer.Add(_hexField);
// 피커 버튼
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.IsEnabled = !IsReadOnly;
_pickerButton.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_pickerButton);
@@ -110,13 +121,14 @@ namespace UVC.UIToolkit
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value;
_colorPreview.SetEnabled(!IsReadOnly);
_colorPreview.RegisterCallback<ClickEvent>(OnPreviewClicked);
}
if (_hexField != null)
{
_hexField.Value = ColorToHex(Value);
_hexField.SetEnabled(!IsReadOnly);
_hexField.isReadOnly = IsReadOnly;
_hexField.OnValueChanged += OnHexChanged;
}
@@ -173,11 +185,10 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
_hexField?.SetEnabled(!IsReadOnly);
if (_pickerButton != null)
{
_pickerButton.IsEnabled = !IsReadOnly;
}
_colorPreview?.SetEnabled(!IsReadOnly);
if(_hexField != null) _hexField.isReadOnly = IsReadOnly;
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
}
#endregion
@@ -217,7 +228,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnColorChanged -= OnPickerColorChanged;
_currentPicker.OnColorSelected -= OnPickerColorSelected;
_currentPicker.Dispose();
_currentPicker.Close(); // Close()를 호출하여 블로커도 함께 정리
_currentPicker = null;
}
}

View File

@@ -22,14 +22,16 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKColorStatePropertyItem(string id, string name, UTKColorState initialValue = default)
public UTKColorStatePropertyItem(string id, string name, UTKColorState initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKColorStatePropertyItem(string id, string name, string state, Color color)
public UTKColorStatePropertyItem(string id, string name, string state, Color color, bool isReadOnly = false)
: base(id, name, new UTKColorState(state, color))
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -54,8 +56,8 @@ namespace UVC.UIToolkit
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value.Color;
_colorPreview.SetEnabled(!IsReadOnly);
}
return container;
}
@@ -81,12 +83,14 @@ namespace UVC.UIToolkit
_colorPreview.name = "color-preview";
_colorPreview.AddToClassList("utk-property-item__color-preview");
_colorPreview.style.backgroundColor = Value.Color;
_colorPreview.SetEnabled(!IsReadOnly);
valueContainer.Add(_colorPreview);
// 피커 버튼
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.AddToClassList("utk-property-item__picker-btn");
_pickerButton.IsEnabled = !IsReadOnly;
valueContainer.Add(_pickerButton);
container.Add(valueContainer);
@@ -110,6 +114,7 @@ namespace UVC.UIToolkit
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value.Color;
_colorPreview.SetEnabled(!IsReadOnly);
_colorPreview.RegisterCallback<ClickEvent>(OnPreviewClicked);
}
@@ -156,6 +161,7 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
if (_colorPreview != null) _colorPreview.SetEnabled(!IsReadOnly);
}
#endregion
@@ -199,7 +205,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnColorChanged -= OnPickerColorChanged;
_currentPicker.OnColorSelected -= OnPickerColorSelected;
_currentPicker.Dispose();
_currentPicker.Close();
_currentPicker = null;
}
}

View File

@@ -33,9 +33,10 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKDatePropertyItem(string id, string name, DateTime initialValue = default)
public UTKDatePropertyItem(string id, string name, DateTime initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue == default ? DateTime.Today : initialValue)
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -54,6 +55,12 @@ namespace UVC.UIToolkit
if (_dateField != null)
{
_dateField.Value = Value.ToString(_dateFormat);
_dateField.isReadOnly = IsReadOnly;
}
if (_pickerButton != null)
{
_pickerButton.IsEnabled = !IsReadOnly;
}
return container;
@@ -73,11 +80,13 @@ namespace UVC.UIToolkit
_dateField.name = "date-field";
_dateField.Value = Value.ToString(_dateFormat);
_dateField.style.flexGrow = 1;
_dateField.isReadOnly = IsReadOnly;
valueContainer.Add(_dateField);
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.AddToClassList("utk-property-item__picker-btn");
_pickerButton.IsEnabled = !IsReadOnly;
valueContainer.Add(_pickerButton);
container.Add(valueContainer);
@@ -95,7 +104,7 @@ namespace UVC.UIToolkit
if (_dateField != null)
{
_dateField.Value = Value.ToString(_dateFormat);
_dateField.SetEnabled(!IsReadOnly);
_dateField.isReadOnly = IsReadOnly;
_dateField.OnValueChanged += OnDateTextChanged;
}
@@ -141,7 +150,7 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
_dateField?.SetEnabled(!IsReadOnly);
if (_dateField != null) _dateField.isReadOnly = IsReadOnly;
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
}
#endregion
@@ -173,7 +182,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnDateSelected -= OnPickerDateSelected;
_currentPicker.OnClosed -= OnPickerClosed;
_currentPicker.Dispose();
_currentPicker.Close();
_currentPicker = null;
}
}

View File

@@ -35,14 +35,16 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKDateRangePropertyItem(string id, string name, UTKDateRange initialValue = default)
public UTKDateRangePropertyItem(string id, string name, UTKDateRange initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue.Start == default ? new UTKDateRange(DateTime.Today, DateTime.Today) : initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKDateRangePropertyItem(string id, string name, DateTime start, DateTime end)
public UTKDateRangePropertyItem(string id, string name, DateTime start, DateTime end, bool isReadOnly = false)
: base(id, name, new UTKDateRange(start, end))
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -63,11 +65,22 @@ namespace UVC.UIToolkit
if (_startField != null)
{
_startField.Value = Value.Start.ToString(_dateFormat);
_startField.isReadOnly = IsReadOnly;
}
if (_endField != null)
{
_endField.Value = Value.End.ToString(_dateFormat);
_endField.isReadOnly = IsReadOnly;
}
if(_startPickerBtn != null)
{
_startPickerBtn.IsEnabled = !IsReadOnly;
}
if(_endPickerBtn != null)
{
_endPickerBtn.IsEnabled = !IsReadOnly;
}
return container;
@@ -89,10 +102,12 @@ namespace UVC.UIToolkit
_startField.name = "start-field";
_startField.Value = Value.Start.ToString(_dateFormat);
_startField.style.flexGrow = 1;
_startField.isReadOnly = IsReadOnly;
valueContainer.Add(_startField);
_startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_startPickerBtn.name = "start-picker-btn";
_startPickerBtn.IsEnabled = !IsReadOnly;
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_startPickerBtn);
@@ -105,10 +120,12 @@ namespace UVC.UIToolkit
_endField.name = "end-field";
_endField.Value = Value.End.ToString(_dateFormat);
_endField.style.flexGrow = 1;
_endField.isReadOnly = IsReadOnly;
valueContainer.Add(_endField);
_endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_endPickerBtn.name = "end-picker-btn";
_endPickerBtn.IsEnabled = !IsReadOnly;
_endPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_endPickerBtn);
@@ -129,14 +146,14 @@ namespace UVC.UIToolkit
if (_startField != null)
{
_startField.Value = Value.Start.ToString(_dateFormat);
_startField.SetEnabled(!IsReadOnly);
_startField.isReadOnly = IsReadOnly;
_startField.OnValueChanged += OnStartTextChanged;
}
if (_endField != null)
{
_endField.Value = Value.End.ToString(_dateFormat);
_endField.SetEnabled(!IsReadOnly);
_endField.isReadOnly = IsReadOnly;
_endField.OnValueChanged += OnEndTextChanged;
}
@@ -208,8 +225,8 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
_startField?.SetEnabled(!IsReadOnly);
_endField?.SetEnabled(!IsReadOnly);
if (_startField != null) _startField.isReadOnly = IsReadOnly;
if (_endField != null) _endField.isReadOnly = IsReadOnly;
if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly;
if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly;
}
@@ -247,7 +264,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnDateSelected -= OnPickerDateSelected;
_currentPicker.OnClosed -= OnPickerClosed;
_currentPicker.Dispose();
_currentPicker.Close();
_currentPicker = null;
}
}

View File

@@ -33,9 +33,10 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKDateTimePropertyItem(string id, string name, DateTime initialValue = default)
public UTKDateTimePropertyItem(string id, string name, DateTime initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue == default ? DateTime.Now : initialValue)
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -54,6 +55,12 @@ namespace UVC.UIToolkit
if (_dateTimeField != null)
{
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
_dateTimeField.isReadOnly = IsReadOnly;
}
if (_pickerButton != null)
{
_pickerButton.IsEnabled = !IsReadOnly;
}
return container;
@@ -73,10 +80,12 @@ namespace UVC.UIToolkit
_dateTimeField.name = "datetime-field";
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
_dateTimeField.style.flexGrow = 1;
_dateTimeField.isReadOnly = IsReadOnly;
valueContainer.Add(_dateTimeField);
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.IsEnabled = !IsReadOnly;
_pickerButton.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_pickerButton);
@@ -95,7 +104,7 @@ namespace UVC.UIToolkit
if (_dateTimeField != null)
{
_dateTimeField.Value = Value.ToString(_dateTimeFormat);
_dateTimeField.SetEnabled(!IsReadOnly);
_dateTimeField.isReadOnly = IsReadOnly;
_dateTimeField.OnValueChanged += OnDateTimeTextChanged;
}
@@ -141,7 +150,7 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
_dateTimeField?.SetEnabled(!IsReadOnly);
if (_dateTimeField != null) _dateTimeField.isReadOnly = IsReadOnly;
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
}
#endregion
@@ -173,7 +182,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnDateSelected -= OnPickerDateSelected;
_currentPicker.OnClosed -= OnPickerClosed;
_currentPicker.Dispose();
_currentPicker.Close();
_currentPicker = null;
}
}

View File

@@ -35,14 +35,16 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKDateTimeRangePropertyItem(string id, string name, UTKDateTimeRange initialValue = default)
public UTKDateTimeRangePropertyItem(string id, string name, UTKDateTimeRange initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue.Start == default ? new UTKDateTimeRange(DateTime.Now, DateTime.Now) : initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKDateTimeRangePropertyItem(string id, string name, DateTime start, DateTime end)
public UTKDateTimeRangePropertyItem(string id, string name, DateTime start, DateTime end, bool isReadOnly = false)
: base(id, name, new UTKDateTimeRange(start, end))
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -63,11 +65,23 @@ namespace UVC.UIToolkit
if (_startField != null)
{
_startField.Value = Value.Start.ToString(_dateTimeFormat);
_startField.isReadOnly = IsReadOnly;
}
if (_endField != null)
{
_endField.Value = Value.End.ToString(_dateTimeFormat);
_endField.isReadOnly = IsReadOnly;
}
if (_startPickerBtn != null)
{
_startPickerBtn.IsEnabled = !IsReadOnly;
}
if (_endPickerBtn != null)
{
_endPickerBtn.IsEnabled = !IsReadOnly;
}
return container;
@@ -89,10 +103,12 @@ namespace UVC.UIToolkit
_startField.name = "start-field";
_startField.Value = Value.Start.ToString(_dateTimeFormat);
_startField.style.flexGrow = 1;
_startField.isReadOnly = IsReadOnly;
valueContainer.Add(_startField);
_startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_startPickerBtn.name = "start-picker-btn";
_startPickerBtn.IsEnabled = !IsReadOnly;
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_startPickerBtn);
@@ -105,10 +121,12 @@ namespace UVC.UIToolkit
_endField.name = "end-field";
_endField.Value = Value.End.ToString(_dateTimeFormat);
_endField.style.flexGrow = 1;
_endField.isReadOnly = IsReadOnly;
valueContainer.Add(_endField);
_endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_endPickerBtn.name = "end-picker-btn";
_endPickerBtn.IsEnabled = !IsReadOnly;
_endPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_endPickerBtn);
@@ -129,14 +147,14 @@ namespace UVC.UIToolkit
if (_startField != null)
{
_startField.Value = Value.Start.ToString(_dateTimeFormat);
_startField.SetEnabled(!IsReadOnly);
_startField.isReadOnly = IsReadOnly;
_startField.OnValueChanged += OnStartTextChanged;
}
if (_endField != null)
{
_endField.Value = Value.End.ToString(_dateTimeFormat);
_endField.SetEnabled(!IsReadOnly);
_endField.isReadOnly = IsReadOnly;
_endField.OnValueChanged += OnEndTextChanged;
}
@@ -208,8 +226,8 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
_startField?.SetEnabled(!IsReadOnly);
_endField?.SetEnabled(!IsReadOnly);
if (_startField != null) _startField.isReadOnly = IsReadOnly;
if (_endField != null) _endField.isReadOnly = IsReadOnly;
if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly;
if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly;
}
@@ -247,7 +265,7 @@ namespace UVC.UIToolkit
{
_currentPicker.OnDateSelected -= OnPickerDateSelected;
_currentPicker.OnClosed -= OnPickerClosed;
_currentPicker.Dispose();
_currentPicker.Close();
_currentPicker = null;
}
}

View File

@@ -36,7 +36,7 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKDropdownPropertyItem(string id, string name, List<string> choices, string initialValue = "")
public UTKDropdownPropertyItem(string id, string name, List<string> choices, string initialValue = "", bool isReadOnly = false)
: base(id, name, initialValue)
{
_choices = choices ?? new List<string>();
@@ -46,9 +46,10 @@ namespace UVC.UIToolkit
{
Value = _choices[0];
}
base._isReadOnly = isReadOnly;
}
public UTKDropdownPropertyItem(string id, string name, IEnumerable<string> choices, int selectedIndex = 0)
public UTKDropdownPropertyItem(string id, string name, IEnumerable<string> choices, int selectedIndex = 0, bool isReadOnly = false)
: base(id, name, string.Empty)
{
_choices = choices?.ToList() ?? new List<string>();
@@ -61,6 +62,7 @@ namespace UVC.UIToolkit
{
Value = _choices[0];
}
base._isReadOnly = isReadOnly;
}
#endregion
@@ -79,6 +81,7 @@ namespace UVC.UIToolkit
_dropdown.SetOptions(_choices);
int selectedIndex = _choices.IndexOf(Value);
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
_dropdown.IsEnabled = !IsReadOnly;
}
return container;
@@ -99,6 +102,7 @@ namespace UVC.UIToolkit
_dropdown.SetOptions(_choices);
int selectedIndex = _choices.IndexOf(Value);
_dropdown.SelectedIndex = Math.Max(0, selectedIndex);
_dropdown.IsEnabled = !IsReadOnly;
valueContainer.Add(_dropdown);
container.Add(valueContainer);

View File

@@ -23,10 +23,11 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKEnumPropertyItem(string id, string name, Enum initialValue)
public UTKEnumPropertyItem(string id, string name, Enum initialValue, bool isReadOnly = false)
: base(id, name, initialValue ?? throw new ArgumentNullException(nameof(initialValue)))
{
_enumType = initialValue.GetType();
base._isReadOnly = isReadOnly;
}
#endregion
@@ -43,6 +44,7 @@ namespace UVC.UIToolkit
if (_enumDropdown != null)
{
_enumDropdown.Init(Value);
_enumDropdown.IsEnabled = !IsReadOnly;
}
return container;
@@ -61,6 +63,7 @@ namespace UVC.UIToolkit
_enumDropdown = new UTKEnumDropDown();
_enumDropdown.name = "enum-dropdown";
_enumDropdown.Init(Value);
_enumDropdown.IsEnabled = !IsReadOnly;
valueContainer.Add(_enumDropdown);
container.Add(valueContainer);

View File

@@ -58,17 +58,19 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKFloatPropertyItem(string id, string name, float initialValue = 0f)
public UTKFloatPropertyItem(string id, string name, float initialValue = 0f, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKFloatPropertyItem(string id, string name, float initialValue, float minValue, float maxValue, bool useSlider = true)
public UTKFloatPropertyItem(string id, string name, float initialValue, float minValue, float maxValue, bool useSlider = true, bool isReadOnly = false)
: base(id, name, initialValue)
{
_minValue = minValue;
_maxValue = maxValue;
_useSlider = useSlider;
base._isReadOnly = isReadOnly;
}
#endregion
@@ -88,6 +90,7 @@ namespace UVC.UIToolkit
if (_floatField != null)
{
_floatField.Value = Value;
_floatField.isReadOnly = IsReadOnly;
}
if (_slider != null)
@@ -95,6 +98,7 @@ namespace UVC.UIToolkit
_slider.lowValue = _minValue;
_slider.highValue = _maxValue;
_slider.Value = Value;
_slider.IsEnabled = !IsReadOnly;
}
return container;
@@ -119,12 +123,14 @@ namespace UVC.UIToolkit
{
_slider = new UTKSlider("", _minValue, _maxValue, Value);
_slider.name = "slider-field";
_slider.IsEnabled = !IsReadOnly;
_slider.AddToClassList("utk-property-item__slider");
valueContainer.Add(_slider);
_floatField = new UTKFloatField();
_floatField.name = "value-field";
_floatField.Value = Value;
_floatField.isReadOnly = IsReadOnly;
_floatField.AddToClassList("utk-property-item__number-field");
valueContainer.Add(_floatField);
}
@@ -133,6 +139,7 @@ namespace UVC.UIToolkit
_floatField = new UTKFloatField();
_floatField.name = "value-field";
_floatField.Value = Value;
_floatField.isReadOnly = IsReadOnly;
valueContainer.Add(_floatField);
}
@@ -151,7 +158,7 @@ namespace UVC.UIToolkit
if (_floatField != null)
{
_floatField.Value = Value;
_floatField.IsEnabled = !IsReadOnly;
_floatField.isReadOnly = IsReadOnly;
_floatField.OnValueChanged += OnFloatFieldChanged;
}
@@ -201,15 +208,17 @@ namespace UVC.UIToolkit
if (_floatField != null)
{
_floatField.IsEnabled = !IsReadOnly;
_floatField.isReadOnly = IsReadOnly;
}
if (_slider != null) _slider.IsEnabled = !IsReadOnly;
}
#endregion
#region Private Methods
private void OnFloatFieldChanged(float newValue)
{
Debug.Log($"OnFloatFieldChanged: {newValue}");
float clampedValue = _useSlider ? Mathf.Clamp(newValue, _minValue, _maxValue) : newValue;
if (_slider != null && !Mathf.Approximately(_slider.Value, clampedValue))
@@ -217,6 +226,11 @@ namespace UVC.UIToolkit
_slider.SetValueWithoutNotify(clampedValue);
}
if (_floatField != null && !Mathf.Approximately(_floatField.Value, clampedValue))
{
_floatField.SetValueWithoutNotify(clampedValue);
}
DebounceValueChange(clampedValue, 100).Forget();
}

View File

@@ -20,14 +20,16 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKFloatRangePropertyItem(string id, string name, UTKFloatRange initialValue = default)
public UTKFloatRangePropertyItem(string id, string name, UTKFloatRange initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKFloatRangePropertyItem(string id, string name, float min, float max)
public UTKFloatRangePropertyItem(string id, string name, float min, float max, bool isReadOnly = false)
: base(id, name, new UTKFloatRange(min, max))
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -46,11 +48,13 @@ namespace UVC.UIToolkit
if (_minField != null)
{
_minField.Value = Value.Min;
_minField.isReadOnly = IsReadOnly;
}
if (_maxField != null)
{
_maxField.Value = Value.Max;
_maxField.isReadOnly = IsReadOnly;
}
return container;
@@ -71,6 +75,7 @@ namespace UVC.UIToolkit
_minField.name = "min-field";
_minField.Value = Value.Min;
_minField.style.flexGrow = 1;
_minField.isReadOnly = IsReadOnly;
valueContainer.Add(_minField);
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
@@ -81,6 +86,7 @@ namespace UVC.UIToolkit
_maxField.name = "max-field";
_maxField.Value = Value.Max;
_maxField.style.flexGrow = 1;
_maxField.isReadOnly = IsReadOnly;
valueContainer.Add(_maxField);
container.Add(valueContainer);
@@ -98,14 +104,14 @@ namespace UVC.UIToolkit
if (_minField != null)
{
_minField.Value = Value.Min;
_minField.IsEnabled = !IsReadOnly;
_minField.isReadOnly = IsReadOnly;
_minField.OnValueChanged += OnMinChanged;
}
if (_maxField != null)
{
_maxField.Value = Value.Max;
_maxField.IsEnabled = !IsReadOnly;
_maxField.isReadOnly = IsReadOnly;
_maxField.OnValueChanged += OnMaxChanged;
}
}
@@ -144,8 +150,8 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
if (_minField != null) _minField.IsEnabled = !IsReadOnly;
if (_maxField != null) _maxField.IsEnabled = !IsReadOnly;
if (_minField != null) _minField.isReadOnly = IsReadOnly;
if (_maxField != null) _maxField.isReadOnly = IsReadOnly;
}
#endregion

View File

@@ -63,12 +63,13 @@ namespace UVC.UIToolkit
{
}
public UTKIntPropertyItem(string id, string name, int initialValue, int minValue, int maxValue, bool useSlider = true)
public UTKIntPropertyItem(string id, string name, int initialValue, int minValue, int maxValue, bool useSlider = true, bool isReadOnly = false)
: base(id, name, initialValue)
{
_minValue = minValue;
_maxValue = maxValue;
_useSlider = useSlider;
_isReadOnly = isReadOnly;
}
#endregion
@@ -88,6 +89,7 @@ namespace UVC.UIToolkit
if (_intField != null)
{
_intField.Value = Value;
_intField.isReadOnly = IsReadOnly;
}
if (_slider != null)
@@ -95,6 +97,7 @@ namespace UVC.UIToolkit
_slider.lowValue = _minValue;
_slider.highValue = _maxValue;
_slider.Value = Value;
_slider.IsEnabled = !IsReadOnly;
}
return container;
@@ -120,11 +123,13 @@ namespace UVC.UIToolkit
_slider = new UTKSliderInt("", _minValue, _maxValue, Value);
_slider.name = "slider-field";
_slider.AddToClassList("utk-property-item__slider");
_slider.IsEnabled = !IsReadOnly;
valueContainer.Add(_slider);
_intField = new UTKIntegerField();
_intField.name = "value-field";
_intField.Value = Value;
_intField.isReadOnly = IsReadOnly;
_intField.AddToClassList("utk-property-item__number-field");
valueContainer.Add(_intField);
}
@@ -133,6 +138,7 @@ namespace UVC.UIToolkit
_intField = new UTKIntegerField();
_intField.name = "value-field";
_intField.Value = Value;
_intField.isReadOnly = IsReadOnly;
valueContainer.Add(_intField);
}
@@ -151,7 +157,7 @@ namespace UVC.UIToolkit
if (_intField != null)
{
_intField.Value = Value;
_intField.IsEnabled = !IsReadOnly;
_intField.isReadOnly = IsReadOnly;
_intField.OnValueChanged += OnIntFieldChanged;
}
@@ -201,7 +207,7 @@ namespace UVC.UIToolkit
if (_intField != null)
{
_intField.IsEnabled = !IsReadOnly;
_intField.isReadOnly = IsReadOnly;
}
if (_slider != null) _slider.IsEnabled = !IsReadOnly;
}

View File

@@ -19,14 +19,16 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKIntRangePropertyItem(string id, string name, UTKIntRange initialValue = default)
public UTKIntRangePropertyItem(string id, string name, UTKIntRange initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
public UTKIntRangePropertyItem(string id, string name, int min, int max)
public UTKIntRangePropertyItem(string id, string name, int min, int max, bool isReadOnly = false)
: base(id, name, new UTKIntRange(min, max))
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -45,11 +47,13 @@ namespace UVC.UIToolkit
if (_minField != null)
{
_minField.Value = Value.Min;
_minField.isReadOnly = IsReadOnly;
}
if (_maxField != null)
{
_maxField.Value = Value.Max;
_maxField.isReadOnly = IsReadOnly;
}
return container;
@@ -70,6 +74,7 @@ namespace UVC.UIToolkit
_minField.name = "min-field";
_minField.Value = Value.Min;
_minField.style.flexGrow = 1;
_minField.isReadOnly = IsReadOnly;
valueContainer.Add(_minField);
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
@@ -80,6 +85,7 @@ namespace UVC.UIToolkit
_maxField.name = "max-field";
_maxField.Value = Value.Max;
_maxField.style.flexGrow = 1;
_maxField.isReadOnly = IsReadOnly;
valueContainer.Add(_maxField);
container.Add(valueContainer);
@@ -97,14 +103,14 @@ namespace UVC.UIToolkit
if (_minField != null)
{
_minField.Value = Value.Min;
_minField.IsEnabled = !IsReadOnly;
_minField.isReadOnly = IsReadOnly;
_minField.OnValueChanged += OnMinChanged;
}
if (_maxField != null)
{
_maxField.Value = Value.Max;
_maxField.IsEnabled = !IsReadOnly;
_maxField.isReadOnly = IsReadOnly;
_maxField.OnValueChanged += OnMaxChanged;
}
}
@@ -143,8 +149,8 @@ namespace UVC.UIToolkit
{
base.UpdateReadOnlyState();
if (_minField != null) _minField.IsEnabled = !IsReadOnly;
if (_maxField != null) _maxField.IsEnabled = !IsReadOnly;
if (_minField != null) _minField.isReadOnly = IsReadOnly;
if (_maxField != null) _maxField.isReadOnly = IsReadOnly;
}
#endregion

View File

@@ -47,14 +47,15 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKRadioPropertyItem(string id, string name, List<string> choices, int selectedIndex = 0)
public UTKRadioPropertyItem(string id, string name, List<string> choices, int selectedIndex = 0, bool isReadOnly = false)
: base(id, name, Math.Max(0, Math.Min(selectedIndex, (choices?.Count ?? 1) - 1)))
{
base._isReadOnly = isReadOnly;
_choices = choices ?? new List<string>();
}
public UTKRadioPropertyItem(string id, string name, IEnumerable<string> choices, int selectedIndex = 0)
: this(id, name, choices?.ToList() ?? new List<string>(), selectedIndex)
public UTKRadioPropertyItem(string id, string name, IEnumerable<string> choices, int selectedIndex = 0, bool isReadOnly = false)
: this(id, name, choices?.ToList() ?? new List<string>(), selectedIndex, isReadOnly)
{
}
#endregion
@@ -185,8 +186,9 @@ namespace UVC.UIToolkit
radio.AddToClassList("utk-property-item__radio");
int index = i;
radio.OnValueChanged += (isChecked) => OnRadioChanged(index, isChecked);
radio.OnValueChanged += (isChecked) => OnRadioChanged(index, isChecked);
radio.IsEnabled = !IsReadOnly;
_radioButtons.Add(radio);
_radioContainer.Add(radio);
}

View File

@@ -48,9 +48,12 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKStringPropertyItem(string id, string name, string initialValue = "")
public UTKStringPropertyItem(string id, string name, string initialValue = "", bool isReadOnly = false, bool isMultiline = false, int maxLength = 0)
: base(id, name, initialValue ?? string.Empty)
{
base._isReadOnly = isReadOnly;
_isMultiline = isMultiline;
_maxLength = maxLength;
}
#endregion
@@ -68,6 +71,7 @@ namespace UVC.UIToolkit
{
_inputField.Value = Value;
_inputField.multiline = _isMultiline;
_inputField.isReadOnly = base._isReadOnly;
if (_maxLength > 0)
{
_inputField.maxLength = _maxLength;
@@ -91,6 +95,7 @@ namespace UVC.UIToolkit
_inputField.name = "value-field";
_inputField.Value = Value;
_inputField.multiline = _isMultiline;
_inputField.isReadOnly = IsReadOnly;
if (_maxLength > 0)
{
_inputField.maxLength = _maxLength;
@@ -110,7 +115,8 @@ namespace UVC.UIToolkit
if (_inputField != null)
{
_inputField.Value = Value;
_inputField.SetEnabled(!IsReadOnly);
_inputField.multiline = _isMultiline;
_inputField.isReadOnly = IsReadOnly;
_inputField.OnValueChanged += OnTextChanged;
}
}
@@ -140,7 +146,7 @@ namespace UVC.UIToolkit
if (_inputField != null)
{
_inputField.SetEnabled(!IsReadOnly);
_inputField.isReadOnly = IsReadOnly;
}
}
#endregion

View File

@@ -19,9 +19,10 @@ namespace UVC.UIToolkit
#endregion
#region Constructor
public UTKVector2PropertyItem(string id, string name, Vector2 initialValue = default)
public UTKVector2PropertyItem(string id, string name, Vector2 initialValue = default, bool isReadOnly = false)
: base(id, name, initialValue)
{
base._isReadOnly = isReadOnly;
}
#endregion
@@ -39,6 +40,7 @@ namespace UVC.UIToolkit
{
_vectorField.Value = Value;
_vectorField.label = "";
_vectorField.IsEnabled = !IsReadOnly;
}
return container;

View File

@@ -26,7 +26,7 @@ namespace UVC.UIToolkit
private UTKLabel? _titleLabel;
private UTKButton? _closeButton;
private string _title = "Properties";
private bool _showCloseButton = true;
private bool _showCloseButton = false;
private bool _isDragging;
private Vector2 _dragStartPosition;
private Vector2 _dragStartMousePosition;
@@ -165,8 +165,7 @@ namespace UVC.UIToolkit
if (_titleLabel != null)
{
_titleLabel.Text = _title;
_titleLabel.IsBold = true;
_titleLabel.Size = UTKLabel.LabelSize.Label1;
_titleLabel.Size = UTKLabel.LabelSize.Label3;
}
// 닫기 버튼 설정
@@ -195,9 +194,8 @@ namespace UVC.UIToolkit
_header.name = "header";
_header.AddToClassList("utk-property-window__header");
_titleLabel = new UTKLabel(_title, UTKLabel.LabelSize.Label1);
_titleLabel = new UTKLabel(_title, UTKLabel.LabelSize.Label3);
_titleLabel.name = "title";
_titleLabel.IsBold = true;
_titleLabel.AddToClassList("utk-property-window__title");
_header.Add(_titleLabel);

View File

@@ -162,7 +162,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnSliderValueChanged);
RegisterCallback<ChangeEvent<Vector2>>(OnSliderValueChanged);
}
private void SubscribeToThemeChanges()
@@ -195,6 +195,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<Vector2>>(OnSliderValueChanged);
}
#endregion
}

View File

@@ -144,7 +144,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnSliderValueChanged);
RegisterCallback<ChangeEvent<float>>(OnSliderValueChanged);
}
private void SubscribeToThemeChanges()
@@ -177,6 +177,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<float>>(OnSliderValueChanged);
}
#endregion
}

View File

@@ -145,7 +145,7 @@ namespace UVC.UIToolkit
private void SetupEvents()
{
this.RegisterValueChangedCallback(OnSliderValueChanged);
RegisterCallback<ChangeEvent<int>>(OnSliderValueChanged);
}
private void SubscribeToThemeChanges()
@@ -186,6 +186,7 @@ namespace UVC.UIToolkit
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
OnValueChanged = null;
UnregisterCallback<ChangeEvent<int>>(OnSliderValueChanged);
}
#endregion
}

179
CLAUDE.md
View File

@@ -11,6 +11,31 @@
- **UI Toolkit(UIElements) 필수 사용**. uGUI(Canvas 기반)는 레거시로 취급합니다.
- UXML(구조)과 USS(스타일)를 분리하고, C# 코드에서 인라인 스타일 지정을 지양합니다.
### 이벤트 콜백 등록 규칙
**⚠️ 중요: `RegisterValueChangedCallback` 대신 `RegisterCallback<ChangeEvent<T>>`를 사용합니다.**
`RegisterValueChangedCallback`은 확장 메서드로 `UnregisterCallback`과 대칭이 맞지 않아 이벤트 해제가 어렵습니다.
```csharp
// ❌ 잘못된 예: 해제가 어려운 방식
field.RegisterValueChangedCallback(OnValueChanged);
// field.UnregisterValueChangedCallback(OnValueChanged); // 이런 메서드 없음!
// ✅ 올바른 예: 대칭적인 등록/해제
field.RegisterCallback<ChangeEvent<float>>(OnValueChanged);
field.UnregisterCallback<ChangeEvent<float>>(OnValueChanged);
private void OnValueChanged(ChangeEvent<float> evt)
{
// evt.newValue, evt.previousValue 사용
}
```
| 메서드 | 권장 | 이유 |
|--------|------|------|
| `RegisterValueChangedCallback` | ❌ | 해제용 메서드 없음 |
| `RegisterCallback<ChangeEvent<T>>` | ✅ | `UnregisterCallback` 대칭 |
### 커스텀 VisualElement (Unity 6)
Unity 6에서는 **레거시 `UxmlFactory`/`UxmlTraits` 방식을 사용하지 않고**, 소스 생성기 기반의 `[UxmlElement]``[UxmlAttribute]`를 사용합니다.
@@ -369,45 +394,149 @@ public async UniTask<UserData?> LoadUserAsync(string userId, CancellationToken c
---
## 11) View 기본 패턴
## 11) UTK 컴포넌트 기본 패턴
UTK 컴포넌트는 다음 패턴을 따릅니다. `UTKHelpBox`를 예시로 설명합니다.
```csharp
#nullable enable
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
/// <summary>
/// UI Toolkit View 기본 구조.
/// </summary>
public abstract class UIViewBase : IDisposable
namespace UVC.UIToolkit
{
protected VisualElement Root { get; private set; } = null!;
protected CancellationTokenSource? Cts { get; private set; }
public virtual void Initialize(VisualElement root)
/// <summary>
/// 컴포넌트 설명.
/// </summary>
[UxmlElement]
public partial class UTKExample : VisualElement, IDisposable
{
Root = root ?? throw new ArgumentNullException(nameof(root));
Cts = new CancellationTokenSource();
QueryElements();
RegisterEvents();
}
#region Constants
private const string UXML_PATH = "UIToolkit/Common/UTKExample";
private const string USS_PATH = "UIToolkit/Common/UTKExampleUss";
#endregion
protected abstract void QueryElements();
protected abstract void RegisterEvents();
protected abstract void UnregisterEvents();
#region Fields
private bool _disposed;
private Label? _label;
private string _text = "";
#endregion
public virtual void Dispose()
{
UnregisterEvents();
Cts?.Cancel();
Cts?.Dispose();
Cts = null;
#region Properties
/// <summary>텍스트</summary>
[UxmlAttribute("text")]
public string Text
{
get => _text;
set
{
_text = value;
if (_label != null)
_label.text = value;
}
}
#endregion
#region Constructor
public UTKExample() : base()
{
// 1. 테마 적용
UTKThemeManager.Instance.ApplyThemeToElement(this);
// 2. USS 로드
var uss = Resources.Load<StyleSheet>(USS_PATH);
if (uss != null)
{
styleSheets.Add(uss);
}
// 3. UI 생성 (UXML 또는 Fallback)
CreateUI();
// 4. 테마 변경 구독
SubscribeToThemeChanges();
}
public UTKExample(string text) : this()
{
Text = text;
}
#endregion
#region Setup
private void CreateUI()
{
AddToClassList("utk-example");
var asset = Resources.Load<VisualTreeAsset>(UXML_PATH);
if (asset != null)
{
CreateUIFromUxml(asset);
}
else
{
CreateUIFallback();
}
}
private void CreateUIFromUxml(VisualTreeAsset asset)
{
var root = asset.Instantiate();
// UXML 요소 참조 가져오기
_label = root.Q<Label>("label");
Add(root);
}
private void CreateUIFallback()
{
_label = new Label(_text);
_label.AddToClassList("utk-example__label");
Add(_label);
}
private void SubscribeToThemeChanges()
{
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
RegisterCallback<DetachFromPanelEvent>(_ =>
{
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
});
}
private void OnThemeChanged(UTKTheme theme)
{
UTKThemeManager.Instance.ApplyThemeToElement(this);
}
#endregion
#region IDisposable
public void Dispose()
{
if (_disposed) return;
_disposed = true;
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
}
#endregion
}
}
```
### 주요 패턴 요약
| 항목 | 설명 |
|------|------|
| **`[UxmlElement]`** | UXML에서 사용 가능하도록 등록 |
| **`partial class`** | 소스 생성기 요구사항 |
| **`[UxmlAttribute]`** | UXML 속성 매핑 (케밥 케이스) |
| **`IDisposable`** | 리소스 정리 인터페이스 |
| **`UTKThemeManager`** | 테마 스타일시트 적용 |
| **`DetachFromPanelEvent`** | 패널에서 분리 시 이벤트 해제 |
| **UXML 경로** | `Resources.Load<VisualTreeAsset>()` 사용 |
| **USS 경로** | `Resources.Load<StyleSheet>()` 사용, 파일명에 `Uss` 접미사 |
| **Fallback 패턴** | UXML 로드 실패 시 코드로 UI 생성 |
---
## 12) Unity Nullable 주의

View File

@@ -8,12 +8,15 @@ EditorBuildSettings:
- enabled: 0
path: Assets/Sample/WebGLSample.unity
guid: 7e9d74fe60e878d42aa29a89d4b0bea6
- enabled: 1
- enabled: 0
path: Assets/Sample/Injector/InjectorSample.unity
guid: 0562dcfb29668494badcb7b04e5e55f5
- enabled: 1
- enabled: 0
path: Assets/Sample/Injector/InjectorSample2.unity
guid: be808ba01dc1fb54ca27fa1f5e2f95bf
- enabled: 1
path: Assets/Sample/UIToolkit/UTKStyleGuideSample.unity
guid: e9eed1940cd247f4cbb17191de66e853
m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3}
m_UseUCBPForAssetBundles: 0

View File

@@ -12,8 +12,8 @@ PlayerSettings:
targetDevice: 2
useOnDemandResources: 0
accelerometerFrequency: 60
companyName: CowinTech
productName: SetupTool
companyName: UVC
productName: StyleGuide
defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0}
m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1}
@@ -83,7 +83,7 @@ PlayerSettings:
androidApplicationEntry: 2
defaultIsNativeResolution: 1
macRetinaSupport: 1
runInBackground: 0
runInBackground: 1
muteOtherAudioSources: 0
Prepare IOS For Recording: 0
Force IOS Speakers When Recording: 0
@@ -94,9 +94,9 @@ PlayerSettings:
usePlayerLog: 1
dedicatedServerOptimizations: 1
bakeCollisionMeshes: 0
forceSingleInstance: 0
forceSingleInstance: 1
useFlipModelSwapchain: 1
resizableWindow: 0
resizableWindow: 1
useMacAppStoreValidation: 0
macAppStoreCategory: public.app-category.games
gpuSkinning: 1
@@ -108,7 +108,7 @@ PlayerSettings:
xboxEnableFitness: 0
visibleInBackground: 1
allowFullscreenSwitch: 1
fullscreenMode: 1
fullscreenMode: 2
xboxSpeechDB: 0
xboxEnableHeadOrientation: 0
xboxEnableGuest: 0