UTKProperyWIndow 수정 중

This commit is contained in:
logonkhi
2026-02-02 19:33:27 +09:00
parent f2d0f3d423
commit 297ca29082
118 changed files with 3043 additions and 1924 deletions

View File

@@ -77,14 +77,14 @@
.utk-input--multiline > .unity-base-text-field__input {
height: auto;
min-height: 60px;
min-height: 24px;
padding-top: var(--space-s);
padding-bottom: var(--space-s);
white-space: normal;
align-items: flex-start;
}
/* Note: min-width: 60px (label), min-width: 120px (input), min-height: 60px (multiline)
/* Note: min-width: 60px (label), min-width: 120px (input), min-height: 24px (multiline)
are intentionally kept as fixed values for layout consistency */
/* ===================================

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<VisualElement name="item-root" class="utk-property-item utk-property-item--bool">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<utk:UTKToggle name="value-field" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: af7209711f828a043a1e83c107ea1a79
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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 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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a590f1887a0d8bb4fa61bfd30deaad66
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 07854bdb3074d114bb54153675ef87d2
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4bc0ccd23105ba24f9f23130d2ee4df5
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a091b6d5a85f3c6438c7620031f9ce29
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b44d3939f475cff4f93626c5411f3987
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 56efa3588ece0074cad9fe2325a45eba
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e710406c0264f904799e0756299d40ae
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 027e0d57a68a4af4db7510ae8d56df94
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3ee0c314d2d2093418ea0a699b9d3835
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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">
<utk:UTKSlider name="slider-field" class="utk-property-item__slider" />
<utk:UTKFloatField name="value-field" class="utk-property-item__number-field" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 94e27ad36d95a7844b847381aeeb5a0e
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKFloatField name="max-field" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ba55984b797be144c884c69e7e83a7ca
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 6a33c9ae335c3734583a12969a246930
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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">
<utk:UTKSliderInt name="slider-field" class="utk-property-item__slider" />
<utk:UTKIntegerField name="value-field" class="utk-property-item__number-field" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7dcc0056178d1a849a6d47dacca02b8c
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
<utk:UTKLabel text="~" size="Body2" class="utk-property-item__range-separator" />
<utk:UTKIntegerField name="max-field" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a17058a524ef0fa4c8d704fb67de64a7
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<VisualElement name="item-root" class="utk-property-item utk-property-item--radio">
<utk:UTKLabel name="name-label" class="utk-property-item__label" />
<VisualElement class="utk-property-item__value">
<VisualElement name="radio-container" class="utk-property-item__radio-container" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c680dc30ab6d1f84f89ad40b21494760
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c8a37446f52e9a24c9f6178da44e0a26
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: afcba438b06894046aeefe41948564fb
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3f7e527b348e7e640aaca70d1a45cda6
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -12,26 +12,36 @@
.utk-property-list__search-container {
flex-direction: row;
padding: var(--space-m);
position: relative;
}
/* UTKInputField 검색 필드 스타일 */
.utk-property-list__search-field {
flex-grow: 1;
}
.utk-property-list__search-placeholder {
position: absolute;
left: 18px;
top: 50%;
translate: 0 -50%;
color: var(--color-text-placeholder);
.utk-property-list__search-field .unity-text-field__input {
background-color: var(--color-bg-input);
border-width: var(--border-width);
border-color: var(--color-border);
border-radius: var(--radius-s);
padding: var(--space-s) var(--space-m);
min-height: 24px;
font-size: var(--font-size-body2);
}
.utk-property-list__search-field:focus .unity-text-field__input {
border-color: var(--color-border-focus);
}
.utk-property-list__tree-view {
flex-grow: 1;
}
/* TreeView 기본 토글 버튼 숨기기 (커스텀 그룹 헤더 아이콘 사용) */
.utk-property-list__tree-view .unity-tree-view__item-toggle {
display: none;
}
/* ===================================
Property Item Container
=================================== */
@@ -304,31 +314,43 @@
background-color: var(--color-collection-item-hover);
}
/* UTKLabel 펼침 아이콘 스타일 */
.utk-property-group__expand-icon {
width: 16px;
height: 16px;
font-size: var(--font-size-body2);
margin-right: var(--space-s);
justify-content: center;
align-items: center;
}
.utk-property-group__expand-icon .utk-label__icon {
font-size: 16px;
color: var(--color-text-secondary);
-unity-text-align: middle-center;
margin-right: var(--space-m);
transition: rotate var(--anim-fast) ease-out;
}
.utk-property-group--collapsed .utk-property-group__expand-icon {
rotate: -90deg;
.utk-property-group__header:hover .utk-property-group__expand-icon .utk-label__icon {
color: var(--color-text-primary);
}
/* UTKLabel 그룹 타이틀 스타일 */
.utk-property-group__title {
flex-grow: 1;
}
.utk-property-group__title .utk-label__text {
font-size: var(--font-size-body2);
-unity-font-style: bold;
color: var(--color-text-primary);
}
/* UTKLabel 아이템 개수 스타일 */
.utk-property-group__count {
margin-left: var(--space-m);
}
.utk-property-group__count .utk-label__text {
font-size: var(--font-size-label4);
color: var(--color-text-secondary);
margin-left: 10px;
}
.utk-property-group__content {

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<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" />
</VisualElement>
<utk:UTKPropertyList name="content" class="utk-property-window__content" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c65692f7d30cee54483b01c3283a705b
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -6,7 +6,7 @@
border-radius: 8px;
border-width: 1px;
border-color: var(--color-border);
min-width: 300px;
min-width: 400px;
min-height: 200px;
/* box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); */ /* Unity USS does not support box-shadow */
}
@@ -26,24 +26,36 @@
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
}
/* UTKLabel 타이틀 스타일 */
.utk-property-window__title {
flex-grow: 1;
}
.utk-property-window__title .utk-label__text {
color: var(--color-text-primary);
font-size: 13px;
font-size: var(--font-size-body2);
-unity-font-style: bold;
}
/* UTKButton 닫기 버튼 스타일 */
.utk-property-window__close-btn {
width: 24px;
height: 24px;
background-color: transparent;
border-width: 0;
min-width: 24px;
padding: 0;
margin: 0;
border-radius: var(--radius-s);
}
.utk-property-window__close-btn .utk-button__icon {
color: var(--color-text-secondary);
font-size: 12px;
border-radius: 4px;
}
.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);
}

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
<Style src="../UTKSampleCommon.uss" />
<Style src="UTKSliderSample.uss" />
<VisualElement class="utk-sample-container">
<Label class="utk-sample-desc" text="정수 슬라이더 컴포넌트" />
<!-- Normal -->
<VisualElement class="utk-sample-section">
<Label class="utk-sample-section__title" text="Normal" />
<VisualElement class="utk-sample-row">
<utk:UTKSliderInt name="slider-level" label="Level" low-value="1" high-value="10" value="5" class="utk-sample-slider" />
</VisualElement>
</VisualElement>
<!-- Range -->
<VisualElement class="utk-sample-section">
<Label class="utk-sample-section__title" text="Large Range" />
<VisualElement class="utk-sample-row">
<utk:UTKSliderInt name="slider-quantity" label="Quantity" low-value="0" high-value="100" value="25" class="utk-sample-slider" />
</VisualElement>
</VisualElement>
<!-- Disabled -->
<VisualElement class="utk-sample-section">
<Label class="utk-sample-section__title" text="Disabled" />
<VisualElement class="utk-sample-row">
<utk:UTKSliderInt name="slider-disabled" label="Disabled" low-value="0" high-value="100" value="30" is-enabled="false" class="utk-sample-slider" />
</VisualElement>
</VisualElement>
<!-- Code Sample -->
<VisualElement class="utk-code-sample-container">
<utk:UTKCodeBlock name="code-csharp" title="C#" />
<utk:UTKCodeBlock name="code-uxml" title="UXML" />
</VisualElement>
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d647ca71fcaf1504fa87105a50b10a57
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -104,3 +104,80 @@
.utk-slider--disabled > .unity-label {
color: var(--color-text-disabled);
}
/* ===================================
SliderInt 스타일 (UTKSliderInt)
=================================== */
.utk-slider-int {
flex-direction: row;
align-items: center;
}
.utk-slider-int > .unity-label {
min-width: 60px;
margin-right: var(--space-m);
font-size: var(--font-size-body2);
color: var(--color-text-primary);
-unity-text-align: middle-left;
}
.utk-slider-int > .unity-slider-int__input {
flex-grow: 1;
flex-direction: row;
align-items: center;
}
.utk-slider-int .unity-base-slider__tracker {
height: 6px;
border-radius: 3px;
background-color: var(--color-bg-input);
border-width: var(--border-width);
border-color: var(--color-border);
}
.utk-slider-int .unity-base-slider__drag-container {
height: 6px;
}
.utk-slider-int .unity-base-slider__dragger {
width: 16px;
height: 16px;
margin-top: -8px;
border-radius: var(--radius-full);
background-color: var(--color-btn-primary);
border-width: 0;
transition-duration: var(--anim-fast);
transition-property: scale;
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
}
.utk-slider-int .unity-base-slider__dragger:hover {
scale: 1.1;
}
.utk-slider-int:active .unity-base-slider__dragger {
scale: 1.2;
}
/* SliderInt Disabled State */
.utk-slider-int.utk-slider--disabled {
cursor: arrow;
}
.utk-slider-int.utk-slider--disabled .unity-base-slider__tracker {
background-color: var(--color-btn-disabled);
}
.utk-slider-int.utk-slider--disabled .unity-base-slider__dragger {
background-color: var(--color-text-disabled);
cursor: arrow;
}
.utk-slider-int.utk-slider--disabled .unity-base-slider__dragger:hover {
scale: 1;
}
.utk-slider-int.utk-slider--disabled > .unity-label {
color: var(--color-text-disabled);
}

View File

@@ -1,7 +1,13 @@
@import url("unity-theme://default");
@import url("UTKVariables.uss");
@import url("UTKComponents.uss");
@import url("UTKDefaultStyle.uss");

View File

@@ -24,28 +24,32 @@
- UTKComponents.uss에서 전역 스타일 정의됨
=================================== */
.unity-base-dropdown * {
--color-base-01: #FFFFFF;
--color-base-02: #F8F8F8;
--color-border: #D0D0D0;
--color-collection-item-hover: #004578;
--color-item-selected-bg: #004578;
--color-item-selected-bg-hover: ##003F6E;
--color-text-primary: #222222;
--color-text-primary-hover: #1C1C1C;
}
/* 드롭다운 최외곽 컨테이너 - border-radius 및 border 적용
UTKComponents.uss에도 정의되어 있지만, Unity 기본 스타일시트가 나중에 로드되어
덮어쓰기 때문에를 사용하여 강제로 오버라이드합니다.
border와 border-radius를 같은 요소에 적용하여 코너 렌더링 문제 방지. */
.unity-base-dropdown__container-outer {
border-radius: var(--radius-m) ;
border-width: var(--border-width) ;
border-color: var(--color-base-06) ;
border-radius: var(--radius-s) ;
border-width: 1px ;
border-color: var(--color-border) ;
overflow: hidden ;
}
/* 드롭다운 팝업 컨테이너 - border 제거 (최외곽 컨테이너에만 border 적용) */
.unity-base-dropdown {
background-color: var(--color-base-01);
padding: var(--space-xs);
margin-top: 2px;
border-width: 0 ;
}
/* 드롭다운 내부 컨테이너 - 패딩 제거 */
.unity-base-dropdown__container-inner {
padding: 0px;
background-color: var(--color-base-01) ;
}
/* 드롭다운 항목 hover 스타일 - Unity 기본 스타일 오버라이드
@@ -54,6 +58,7 @@
:enabled 의사 클래스는 Unity 기본 스타일과 동일한 specificity 확보용. */
.unity-base-dropdown__item:hover:enabled {
background-color: var(--color-collection-item-hover) ;
color: var(--color-text-primary);
}
/* 드롭다운 항목 콘텐츠 */
@@ -67,41 +72,39 @@
}
.unity-base-dropdown__item:checked {
background-color: var(--color-blue-05);
background-color: var(--color-item-selected-bg);
color: #ffffff;
}
.unity-base-dropdown__item:checked:hover {
background-color: var(--color-blue-06);
}
.unity-base-dropdown__item:hover .unity-base-dropdown__item-content {
background-color: var(--color-collection-item-hover);
background-color: var(--color-item-selected-bg-hover);
}
.unity-base-dropdown__item:checked .unity-base-dropdown__item-content {
background-color: var(--color-blue-05);
background-color: var(--color-item-selected-bg);
}
.unity-base-dropdown__item:checked:hover .unity-base-dropdown__item-content {
background-color: var(--color-blue-06);
background-color: var(--color-item-selected-bg-hover);
}
/* 드롭다운 항목 라벨 */
.unity-base-dropdown__label {
font-size: 12px;
color: var(--color-base-20);
-unity-text-align: middle-left;
padding-left: 0;
padding-right: 0;
}
.unity-base-dropdown__item:checked .unity-base-dropdown__label {
color: var(--color-base-01);
}
/* 드롭다운 체크마크 */
.unity-base-dropdown__item .unity-base-dropdown__checkmark {
/* 드롭다운 호버 시 텍스트 색상 */
.unity-base-dropdown__item:hover .unity-base-dropdown__label {
color: var(--color-text-primary-hover);
}
.unity-base-dropdown__item:checked:hover .unity-base-dropdown__label {
color: var(--color-text-primary);
}
/* 드롭다운 체크마크 크기 오버라이드
Unity 런타임 테마의 체크마크 스타일(.unity-base-dropdown__item > .unity-base-dropdown__item-content > .unity-base-dropdown__checkmark)을
덮어쓰기 위해 동일한 specificity + 순서 우선 또는 더 높은 specificity 사용 */
.unity-base-dropdown__item > .unity-base-dropdown__item-content > .unity-base-dropdown__checkmark {
width: 16px;
height: 16px;
min-width: 16px;
@@ -109,6 +112,33 @@
-unity-background-image-tint-color: var(--color-base-01);
}
.unity-base-dropdown__item {
height: 28px;
padding-left: var(--space-m);
padding-right: var(--space-m);
padding-top: var(--space-xs);
padding-bottom: var(--space-xs);
background-color: transparent;
border-radius: 0;
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
}
/* 드롭다운 라벨 스타일 오버라이드 */
.unity-base-dropdown__item .unity-base-dropdown__label {
font-size: 12px;
color: var(--color-text-primary);
-unity-text-align: middle-left;
padding-left: 0;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0;
}
/* ===================================
ListView/TreeView 항목 텍스트 스타일
- Unity 기본 스타일 오버라이드
@@ -214,43 +244,6 @@ TreeView 항목 스타일
-unity-background-image-tint-color: var(--color-tree-checkmark);
}
/* 드롭다운 체크마크 크기 오버라이드 */
.unity-base-dropdown__checkmark {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
}
.unity-base-dropdown__item {
height: 28px;
padding-left: var(--space-m);
padding-right: var(--space-m);
padding-top: var(--space-xs);
padding-bottom: var(--space-xs);
background-color: transparent;
border-radius: var(--radius-xs);
transition-duration: var(--anim-fast);
transition-property: background-color;
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
}
/* 드롭다운 라벨 스타일 오버라이드 */
.unity-base-dropdown__item .unity-base-dropdown__label {
font-size: 12px;
color: var(--color-base-20);
-unity-text-align: middle-left;
padding-left: 0;
padding-right: 0;
padding-top: 0;
padding-bottom: 0;
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0;
}
#unity-tree-view__item-toggle > VisualElement > VisualElement {
margin-left: 0;
margin-right: 0;

View File

@@ -8,7 +8,7 @@
* 테마 전환 시 이 파일을 UTKThemeLight.uss로 교체합니다.
*/
:root {
* {
/* ===================================
Base Colors (color-base-*)
그레이스케일 팔레트 (밝음 -> 어두움)
@@ -50,6 +50,13 @@
--color-blue-10: #062F4A;
--color-blue-11: #001F33;
/* ===================================
Accent Colors - Blue Light (color-blue-light-*)
라이트 테마 선택 항목용 밝은 파란색
=================================== */
--color-blue-light-01: #B3D7FF;
--color-blue-light-02: #8EC5FF;
/* ===================================
Accent Colors - Red (color-red-*)
=================================== */
@@ -121,7 +128,8 @@
--color-text-disabled: var(--color-gray-02);
--color-text-placeholder: var(--color-base-12);
--color-text-inverse: var(--color-base-20);
--color-text-on-primary: var(--color-base-01);
--color-text-on-inverse: var(--color-base-21);
--color-text-on-primary: var(--color-base-04);
/* ===================================
Semantic Colors - Background (color-bg-*)
@@ -201,7 +209,7 @@
--color-collection-item: rgba(37, 37, 38, 0);
--color-collection-item-hover: var(--color-base-16);
--color-collection-item-selected: var(--color-blue-07);
--color-collection-item-selected-hover: #1176b9;
--color-collection-item-selected-hover: var(--color-blue-08);
/* ===================================
Semantic Colors - Tree Checkmark (color-tree-checkmark-*)

View File

@@ -12,7 +12,7 @@
* - 텍스트: 어두운 색상 (base-16 ~ base-21)
*/
:root {
* {
/* ===================================
Base Colors (color-base-*)
그레이스케일 팔레트 (밝음 -> 어두움)
@@ -55,6 +55,13 @@
--color-blue-10: #001530;
--color-blue-11: #000B1A;
/* ===================================
Accent Colors - Blue Light (color-blue-light-*)
라이트 테마 선택 항목용 밝은 파란색
=================================== */
--color-blue-light-01: #B3D7FF;
--color-blue-light-02: #8EC5FF;
/* ===================================
Accent Colors - Red (color-red-*)
=================================== */
@@ -127,7 +134,8 @@
--color-text-disabled: var(--color-base-08);
--color-text-placeholder: var(--color-base-09);
--color-text-inverse: var(--color-base-01);
--color-text-on-primary: var(--color-base-01);
--color-text-on-inverse: var(--color-base-02);
--color-text-on-primary: var(--color-base-18);
/* ===================================
Semantic Colors - Background (color-bg-*)
@@ -207,8 +215,8 @@
=================================== */
--color-collection-item: rgba(255, 255, 255, 0);
--color-collection-item-hover: var(--color-base-04);
--color-collection-item-selected: var(--color-blue-01);
--color-collection-item-selected-hover: var(--color-blue-02);
--color-collection-item-selected: var(--color-blue-light-01);
--color-collection-item-selected-hover: var(--color-blue-light-02);
/* ===================================
Semantic Colors - Tree Checkmark (color-tree-checkmark-*)

View File

@@ -8,7 +8,7 @@
* 색상 변수는 UTKThemeDark.uss / UTKThemeLight.uss에서 정의됩니다.
*/
:root {
* {
/* ===================================
Font
=================================== */

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 9d3024917e8d4b546943ec3f7b03d9e4, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 3cdfb535a2976ab48975316e1f7196d9, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1

View File

@@ -25,12 +25,19 @@ public class UTKAccordionListWindowSample : MonoBehaviour
[Tooltip("UI를 표시할 UIDocument 컴포넌트")]
public UIDocument? uiDocument;
[SerializeField]
[Tooltip("드래그 시 고스트 이미지 표시 여부")]
private bool showDragGhost = true;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
/// <summary>UTKAccordionListWindow 인스턴스</summary>
private UTKAccordionListWindow? _accordionWindow;
private UTKAccordionListWindow _accordionWindow;
/// <summary>드롭 위치 (스크린 좌표)</summary>
private Vector2 _lastDropScreenPosition;
@@ -44,6 +51,8 @@ public class UTKAccordionListWindowSample : MonoBehaviour
/// <summary>드래그 중 리스트 영역 밖에 있는지 여부</summary>
private bool _isOutsideListArea;
#endregion
#region Unity
@@ -51,30 +60,49 @@ public class UTKAccordionListWindowSample : MonoBehaviour
private void Start()
{
// UIDocument 참조 확인
uiDocument ??= GetComponent<UIDocument>();
if (uiDocument == null)
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("[UTKAccordionListWindowSample] UIDocument가 할당되지 않았습니다.");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
uiDocument = doc;
var toggle = uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
var window = uiDocument.rootVisualElement.Q<UTKAccordionListWindow>("window");
if (window == null)
{
Debug.LogError("UXML에서 UTKAccordionListWindow를 찾을 수 없습니다.");
return;
}
_accordionWindow = window;
UTKThemeManager.Instance.RegisterRoot(uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
// UTKAccordionListWindow 인스턴스 생성 및 추가
_accordionWindow = new UTKAccordionListWindow();
_accordionWindow.ShowDragGhost = showDragGhost;
_accordionWindow.Title = "ACCORDION";
_accordionWindow.ShowCloseButton = true;
uiDocument.rootVisualElement.Add(_accordionWindow);
// 테스트 데이터 생성
CreateTestData();
// 이벤트 핸들러 등록
RegisterEventHandlers();
// 윈도우 표시
_accordionWindow.Show();
Debug.Log("[UTKAccordionListWindowSample] 초기화 완료");
}

View File

@@ -0,0 +1,6 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKAccordionListWindow name="window" />
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3cdfb535a2976ab48975316e1f7196d9
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 5ad0294e546cb18409d368046997e670, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 16e3bccfa967f4249856b24387b10b0a, type: 3}
m_SortingOrder: 1
m_Position: 0
m_WorldSpaceSizeMode: 1
@@ -188,6 +188,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UTKColorPickerSample
_uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!1 &1331954412
GameObject:
m_ObjectHideFlags: 0

View File

@@ -15,6 +15,12 @@ namespace UVC.Sample.UIToolkit
{
[SerializeField] private UIDocument? _uiDocument;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
private VisualElement? _root;
private VisualElement? _colorPreview;
private Label? _colorLabel;
@@ -25,11 +31,30 @@ namespace UVC.Sample.UIToolkit
private void Start()
{
if (_uiDocument == null)
// UIDocument 참조 확인
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("UIDocument is not assigned!");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
_uiDocument = doc;
var toggle = _uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
UTKThemeManager.Instance.RegisterRoot(_uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
_root = _uiDocument.rootVisualElement;
CreateSampleUI();

View File

@@ -0,0 +1,5 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 16e3bccfa967f4249856b24387b10b0a
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -131,7 +131,7 @@ GameObject:
- component: {fileID: 1097328754}
- component: {fileID: 1097328755}
m_Layer: 0
m_Name: UTKComponentListWindowSample
m_Name: Sample
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 9d3024917e8d4b546943ec3f7b03d9e4, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: ac1b05d63bb20cd468b39a2802e42701, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1
@@ -188,6 +188,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UTKComponentListWindowSample
uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!1 &1331954412
GameObject:
m_ObjectHideFlags: 0

View File

@@ -12,36 +12,71 @@ public class UTKComponentListWindowSample : MonoBehaviour
[SerializeField]
public UIDocument uiDocument;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
private UTKComponentListWindow listWindow;
private UTKComponentTabListWindow tabListWindow;
void Start()
{
uiDocument ??= GetComponent<UIDocument>();
// UIDocument 참조 확인
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
uiDocument = doc;
var toggle = uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
// UTKComponentTabListWindow 사용 (탭 기능 포함)
tabListWindow = new UTKComponentTabListWindow();
tabListWindow.style.marginLeft = 500;
uiDocument.rootVisualElement.Add(tabListWindow);
var window = uiDocument.rootVisualElement.Q<UTKComponentListWindow>("window");
if (window == null)
{
Debug.LogError("UXML에서 UTKComponentListWindow를 찾을 수 없습니다.");
return;
}
listWindow = window;
// 테스트용 계층적 트리 데이터 생성
CreateTabListTestData();
var tabWindow = uiDocument.rootVisualElement.Q<UTKComponentTabListWindow>("tabWindow");
if (tabWindow == null)
{
Debug.LogError("UXML에서 UTKComponentTabListWindow를 찾을 수 없습니다.");
return;
}
tabListWindow = tabWindow;
UTKThemeManager.Instance.RegisterRoot(uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
// 이벤트 핸들러 등록
RegisterTabListEventHandlers();
// 테스트용 계층적 트리 데이터 생성
CreateTabListTestData();
// UTKComponentListWindow 사용 (기본)
listWindow = new UTKComponentListWindow();
uiDocument.rootVisualElement.Add(listWindow);
// 이벤트 핸들러 등록
RegisterTabListEventHandlers();
// 테스트용 계층적 트리 데이터 생성
CreateTestData();
// 테스트용 계층적 트리 데이터 생성
CreateTestData();
// 이벤트 핸들러 등록
RegisterEventHandlers();
// 이벤트 핸들러 등록
RegisterEventHandlers();
}

View File

@@ -0,0 +1,7 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKComponentListWindow name="window" />
<utk:UTKComponentTabListWindow name="tabWindow" style="position: absolute; top: 0; left: 400px;" />
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ac1b05d63bb20cd468b39a2802e42701
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 5ad0294e546cb18409d368046997e670, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 532efe3b9768cdf4e86046f72462a043, type: 3}
m_SortingOrder: 10
m_Position: 0
m_WorldSpaceSizeMode: 1
@@ -188,6 +188,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UTKDatePickerSample
_uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!1 &1331954412
GameObject:
m_ObjectHideFlags: 0

View File

@@ -16,6 +16,12 @@ namespace UVC.Sample.UIToolkit
{
[SerializeField] private UIDocument? _uiDocument;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
private VisualElement? _root;
private Label? _dateLabel;
private Label? _dateTimeLabel;
@@ -29,11 +35,30 @@ namespace UVC.Sample.UIToolkit
private void Start()
{
if (_uiDocument == null)
// UIDocument 참조 확인
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("UIDocument is not assigned!");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
_uiDocument = doc;
var toggle = _uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
UTKThemeManager.Instance.RegisterRoot(_uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
bool success = LocalizationManager.Instance.LoadDefaultLocalizationData("ko", "locale.json");
Debug.Log($"LocalizationManager: LoadDefaultLocalizationData success: {success}");

View File

@@ -0,0 +1,5 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 532efe3b9768cdf4e86046f72462a043
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 9d3024917e8d4b546943ec3f7b03d9e4, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 5b85ca454c8b3e8408b994aa6e24b2d0, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1

View File

@@ -28,6 +28,12 @@ public class UTKImageListWindowSample : MonoBehaviour
[Tooltip("드래그 시 이미지가 커서를 따라다니도록 설정")]
private bool dragImageFollowCursor = true;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
/// <summary>UTKImageListWindow 인스턴스</summary>
private UTKImageListWindow? _imageListWindow;
@@ -50,20 +56,42 @@ public class UTKImageListWindowSample : MonoBehaviour
private void Start()
{
// UIDocument 참조 확인
uiDocument ??= GetComponent<UIDocument>();
if (uiDocument == null)
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("[UTKImageListWindowSample] UIDocument가 할당되지 않았습니다.");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
uiDocument = doc;
var toggle = uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
var window = uiDocument.rootVisualElement.Q<UTKImageListWindow>("window");
if (window == null)
{
Debug.LogError("UXML에서 UTKImageListWindow를 찾을 수 없습니다.");
return;
}
_imageListWindow = window;
// UTKImageListWindow 인스턴스 생성 및 추가
_imageListWindow = new UTKImageListWindow();
_imageListWindow.DragImageFollowCursor = dragImageFollowCursor;
_imageListWindow.Title = "LIBRARY";
_imageListWindow.ShowCloseButton = true;
uiDocument.rootVisualElement.Add(_imageListWindow);
UTKThemeManager.Instance.RegisterRoot(uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
// 테스트 데이터 생성
CreateTestData();

View File

@@ -0,0 +1,6 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKImageListWindow name="window" />
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5b85ca454c8b3e8408b994aa6e24b2d0
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 9d3024917e8d4b546943ec3f7b03d9e4, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: ad778edbbf3d39349bcbcefeb341584c, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1
@@ -188,6 +188,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UVC.Sample.UIToolkit.UTKPropertyWindowSample
_uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!1 &1331954412
GameObject:
m_ObjectHideFlags: 0

View File

@@ -13,17 +13,50 @@ namespace UVC.Sample.UIToolkit
/// </summary>
public class UTKPropertyWindowSample : MonoBehaviour
{
[SerializeField] private UIDocument? _uiDocument;
[SerializeField] private UIDocument _uiDocument;
private UTKPropertyWindow? _propertyWindow;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
private UTKPropertyWindow _propertyWindow;
private void Start()
{
if (_uiDocument == null)
// UIDocument 참조 확인
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("UIDocument is not assigned!");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
_uiDocument = doc;
var toggle = _uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
var window = _uiDocument.rootVisualElement.Q<UTKPropertyWindow>("window");
if (window == null)
{
Debug.LogError("UXML에서 UTKPropertyWindow를 찾을 수 없습니다.");
return;
}
_propertyWindow = window;
UTKThemeManager.Instance.RegisterRoot(_uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
var root = _uiDocument.rootVisualElement;
CreateSamplePropertyWindow(root);
@@ -31,7 +64,6 @@ namespace UVC.Sample.UIToolkit
private void CreateSamplePropertyWindow(VisualElement root)
{
_propertyWindow = new UTKPropertyWindow("Sample Properties");
// 세로 높이를 부모에 맞게 꽉 채우기
_propertyWindow.style.position = Position.Absolute;
@@ -47,7 +79,7 @@ namespace UVC.Sample.UIToolkit
_propertyWindow.OnPropertyValueChanged += args =>
{
Debug.Log($"Property Changed: {args.PropertyName} ({args.PropertyType}) = {args.NewValue}");
Debug.Log($"Property Changed: {args.PropertyId} {args.PropertyName} ({args.PropertyType}) = {args.NewValue}");
};
// 샘플 데이터 생성

View File

@@ -0,0 +1,6 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKPropertyWindow name="window" />
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; left: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ad778edbbf3d39349bcbcefeb341584c
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -555,6 +555,45 @@ slider.IsEnabled = false;",
<!-- 비활성화 -->
<utk:UTKSlider label=""잠금"" is-enabled=""false"" value=""30"" />
</ui:UXML>");
}
private void InitializeSliderIntSample(VisualElement root)
{
SetCodeSamples(root,
csharpCode: @"// 기본 정수 슬라이더
var slider = new UTKSliderInt();
slider.label = ""레벨""; // Unity SliderInt의 label 프로퍼티 (소문자)
slider.lowValue = 1;
slider.highValue = 10;
slider.value = 5;
slider.OnValueChanged += (value) => Debug.Log($""레벨: {value}"");
// 생성자로 생성
var levelSlider = new UTKSliderInt(""레벨"", 1, 10, 5);
// 수량 슬라이더
var quantitySlider = new UTKSliderInt(""수량"", 0, 100, 25);
quantitySlider.OnValueChanged += (value) => Debug.Log($""수량: {value}"");
// 페이지 선택
var pageSlider = new UTKSliderInt(""페이지"", 1, 100, 1);
pageSlider.OnValueChanged += (page) => LoadPage(page);
// 비활성화
slider.IsEnabled = false;",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
<!-- 기본 정수 슬라이더 -->
<utk:UTKSliderInt label=""레벨"" low-value=""1"" high-value=""10"" value=""5"" />
<!-- 큰 범위 -->
<utk:UTKSliderInt label=""수량"" low-value=""0"" high-value=""100"" value=""25"" />
<!-- 비활성화 -->
<utk:UTKSliderInt label=""잠금"" is-enabled=""false"" value=""30"" />
</ui:UXML>");
}

View File

@@ -70,6 +70,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
["UTKNumberStepper"] = "UIToolkit/Sample/Input/UTKNumberStepperSample",
// Slider
["UTKSlider"] = "UIToolkit/Sample/Slider/UTKSliderSample",
["UTKSliderInt"] = "UIToolkit/Sample/Slider/UTKSliderIntSample",
["UTKMinMaxSlider"] = "UIToolkit/Sample/Slider/UTKMinMaxSliderSample",
["UTKProgressBar"] = "UIToolkit/Sample/Slider/UTKProgressBarSample",
// Dropdown
@@ -110,7 +111,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
["Icon"] = new[] { "UTKMaterialIcons", "UTKImageIcons", "UTKImage" },
["Button"] = new[] { "UTKButton", "UTKCheckBox", "UTKToggle", "UTKRadioButton", "UTKToggleButtonGroup" },
["Input"] = new[] { "UTKInputField", "UTKIntegerField", "UTKLongField", "UTKFloatField", "UTKDoubleField", "UTKVector2Field", "UTKVector3Field", "UTKVector4Field", "UTKRectField", "UTKBoundsField", "UTKNumberStepper" },
["Slider"] = new[] { "UTKSlider", "UTKMinMaxSlider", "UTKProgressBar" },
["Slider"] = new[] { "UTKSlider", "UTKSliderInt", "UTKMinMaxSlider", "UTKProgressBar" },
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown" },
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
["List"] = new[] { "UTKListView", "UTKTreeView", "UTKMultiColumnListView", "UTKMultiColumnTreeView", "UTKFoldout", "UTKScrollView" },
@@ -468,6 +469,9 @@ public partial class UTKStyleGuideSample : MonoBehaviour
case "UTKSlider":
InitializeSliderSample(root);
break;
case "UTKSliderInt":
InitializeSliderIntSample(root);
break;
case "UTKMinMaxSlider":
InitializeMinMaxSliderSample(root);
break;

View File

@@ -131,7 +131,7 @@ GameObject:
- component: {fileID: 1097328754}
- component: {fileID: 1097328753}
m_Layer: 0
m_Name: UTKTreeListWindowSample
m_Name: Sample
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@@ -165,6 +165,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UTKTreeListWindowSample
uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!114 &1097328754
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -179,7 +180,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 9d3024917e8d4b546943ec3f7b03d9e4, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 775bcf7c96049ed4798a80a09093b776, type: 3}
m_SortingOrder: 0
m_Position: 0
m_WorldSpaceSizeMode: 1

View File

@@ -12,23 +12,49 @@ public class UTKTreeListWindowSample : MonoBehaviour
[SerializeField]
public UIDocument uiDocument;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle _themeToggle;
private UTKTreeListWindow treeListWindow;
void Start()
{
uiDocument ??= GetComponent<UIDocument>();
// UIDocument에서 UTKTreeListWindow 인스턴스 생성해서 할당
treeListWindow = new UTKTreeListWindow();
uiDocument.rootVisualElement.Add(treeListWindow);
// UTKTreeListWindow가 할당되지 않은 경우 경고
if (treeListWindow == null)
// UIDocument 참조 확인
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogWarning("[UTKTreeListWindowSample] treeListWindow가 할당되지 않았습니다.");
Debug.LogError("UIDocument가 할당되지 않았습니다.");
return;
}
uiDocument = doc;
var toggle = uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
if (toggle == null)
{
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
return;
}
_themeToggle = toggle;
var window = uiDocument.rootVisualElement.Q<UTKTreeListWindow>("window");
if (window == null)
{
Debug.LogError("UXML에서 UTKTreeListWindow를 찾을 수 없습니다.");
return;
}
treeListWindow = window;
UTKThemeManager.Instance.RegisterRoot(uiDocument.rootVisualElement);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
};
// 테스트용 계층적 트리 데이터 생성
CreateTestData();

View File

@@ -0,0 +1,6 @@
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
<VisualElement style="width: 100%; height: 100%;">
<utk:UTKTreeListWindow name="window" />
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
</VisualElement>
</UXML>

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 775bcf7c96049ed4798a80a09093b776
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@@ -0,0 +1,547 @@
# UVC.UIToolkit 컴포넌트 라이브러리
본 문서는 `Assets/Scripts/UVC/UIToolkit` 폴더의 UI Toolkit 컴포넌트 라이브러리에 대한 참조 가이드입니다.
---
## 1) 폴더 구조
```
Assets/Scripts/UVC/UIToolkit/
├── Button/ # 버튼 컴포넌트
│ ├── UTKButton.cs # 메인 버튼 (9가지 variant)
│ ├── UTKCheckBox.cs # 체크박스
│ ├── UTKToggle.cs # 토글 스위치
│ ├── UTKRadioButton.cs # 라디오 버튼
│ └── UTKToggleButtonGroup.cs # 토글 버튼 그룹
├── Card/
│ └── UTKCard.cs # 카드 컨테이너
├── Common/ # 공통 유틸리티
│ ├── UTKCodeBlock.cs # 코드 블록 표시
│ ├── UTKFoldout.cs # 접이식 컨테이너
│ ├── UTKHelpBox.cs # 도움말/정보 박스
│ ├── UTKImage.cs # 이미지 로더 (내부/URL)
│ ├── UTKImageIcons.cs # 이미지 아이콘 레지스트리
│ ├── UTKMaterialIcons.cs # Material Symbols (4102개)
│ ├── UTKScrollView.cs # 커스텀 스크롤 뷰
│ └── UTKTooltipManager.cs # 툴팁 시스템
├── Dropdown/
│ ├── UTKDropdown.cs # 일반 드롭다운
│ └── UTKEnumDropDown.cs # Enum 전용 드롭다운
├── Input/ # 입력 필드
│ ├── UTKBoundsField.cs # Bounds 입력
│ ├── UTKDoubleField.cs # Double 입력
│ ├── UTKFloatField.cs # Float 입력
│ ├── UTKInputField.cs # 텍스트 입력
│ ├── UTKIntegerField.cs # Integer 입력
│ ├── UTKLongField.cs # Long 입력
│ ├── UTKNumberStepper.cs # 숫자 스테퍼 (+/-)
│ ├── UTKRectField.cs # Rect 입력
│ ├── UTKVector2Field.cs # Vector2 입력
│ ├── UTKVector3Field.cs # Vector3 입력
│ └── UTKVector4Field.cs # Vector4 입력
├── Label/
│ └── UTKLabel.cs # 커스텀 레이블
├── List/ # 리스트/트리 컴포넌트
│ ├── UTKAccordionList.cs # 아코디언 리스트 (섹션+그리드)
│ ├── UTKAccordionListItemData.cs
│ ├── UTKComponentList.cs # 컴포넌트 리스트
│ ├── UTKComponentListItemData.cs
│ ├── UTKImageList.cs # 이미지 그리드 리스트
│ ├── UTKImageListItemData.cs
│ ├── UTKListView.cs # 일반 리스트 뷰
│ ├── UTKMultiColumnListView.cs # 다중 컬럼 리스트
│ ├── UTKMultiColumnTreeView.cs # 다중 컬럼 트리
│ ├── UTKTreeListItemData.cs
│ └── UTKTreeView.cs # 트리 뷰
├── Modal/ # 모달/다이얼로그
│ ├── UTKAlert.cs # 알림 다이얼로그
│ ├── UTKColorPicker.cs # 색상 선택기
│ ├── UTKDatePicker.cs # 날짜 선택기
│ ├── UTKModal.cs # 기본 모달
│ ├── UTKModalBlocker.cs # 모달 배경 블로커
│ ├── UTKNotification.cs # 알림 토스트
│ ├── UTKPanel.cs # 패널 컨테이너
│ └── UTKToast.cs # 토스트 알림
├── Property/ # 프로퍼티 에디터 시스템
│ ├── Core/ # 핵심 인터페이스
│ │ ├── IUTKPropertyEntry.cs
│ │ ├── IUTKPropertyGroup.cs
│ │ ├── IUTKPropertyItem.cs
│ │ ├── UTKPropertyGroup.cs
│ │ ├── UTKPropertyType.cs
│ │ └── UTKPropertyValueChangedEventArgs.cs
│ ├── Data/ # 데이터 타입
│ │ ├── UTKColorState.cs
│ │ └── UTKRangeValue.cs
│ ├── Items/ # 프로퍼티 아이템 (16종)
│ │ ├── Base/UTKPropertyItemBase.cs
│ │ ├── UTKBoolPropertyItem.cs
│ │ ├── UTKColorPropertyItem.cs
│ │ ├── UTKColorStatePropertyItem.cs
│ │ ├── UTKDatePropertyItem.cs
│ │ ├── UTKDateRangePropertyItem.cs
│ │ ├── UTKDateTimePropertyItem.cs
│ │ ├── UTKDateTimeRangePropertyItem.cs
│ │ ├── UTKDropdownPropertyItem.cs
│ │ ├── UTKEnumPropertyItem.cs
│ │ ├── UTKFloatPropertyItem.cs
│ │ ├── UTKFloatRangePropertyItem.cs
│ │ ├── UTKIntPropertyItem.cs
│ │ ├── UTKIntRangePropertyItem.cs
│ │ ├── UTKRadioPropertyItem.cs
│ │ ├── UTKStringPropertyItem.cs
│ │ ├── UTKVector2PropertyItem.cs
│ │ └── UTKVector3PropertyItem.cs
│ ├── UTKPropertyList.cs # 프로퍼티 리스트 컨테이너
│ └── UTKPropertyWindow.cs # 프로퍼티 윈도우
├── Slider/
│ ├── UTKMinMaxSlider.cs # 범위 슬라이더
│ ├── UTKProgressBar.cs # 프로그레스 바
│ └── UTKSlider.cs # 단일 슬라이더
├── Tab/
│ ├── UTKTab.cs # 개별 탭
│ └── UTKTabView.cs # 탭 컨테이너
├── Window/ # 윈도우 컴포넌트
│ ├── UTKAccordionListWindow.cs
│ ├── UTKComponentListWindow.cs
│ ├── UTKComponentTabListWindow.cs
│ ├── UTKImageListWindow.cs
│ └── UTKTreeListWindow.cs
├── UTKStyleGuide.cs # 스타일 상수 및 색상
└── UTKThemeManager.cs # 테마 관리 (Light/Dark)
```
---
## 2) 핵심 시스템
### 테마 관리 (UTKThemeManager)
라이트/다크 테마 전환을 관리하는 싱글톤입니다.
```csharp
// 테마 등록 및 전환
UTKThemeManager.Instance.RegisterRoot(rootVisualElement);
UTKThemeManager.Instance.SetTheme(UTKTheme.Dark);
UTKThemeManager.Instance.ToggleTheme();
// 테마 변경 이벤트 구독
UTKThemeManager.OnThemeChanged += OnThemeChanged;
// 관리 USS 파일
// - UTKVariables.uss (CSS 변수)
// - UTKThemeDark.uss / UTKThemeLight.uss (테마별 스타일)
// - UTKComponents.uss (컴포넌트 공통 스타일)
```
### 스타일 가이드 (UTKStyleGuide)
색상, 간격, 폰트, 컴포넌트 크기 등의 상수를 정의합니다.
| 카테고리 | 설명 |
|----------|------|
| Base Colors | Base01~Base21 기본 색상 팔레트 |
| Accent Colors | Blue, Red, Green, Purple, Yellow, Orange, Gray, Seti |
| Semantic Colors | Text, Background, Border, Button, Calendar, State, Scrollbar |
### 아이콘 시스템
**1순위: Material Icons (UTKMaterialIcons)**
```csharp
// 폰트 로드
await UTKMaterialIcons.LoadFontAsync();
// 아이콘 적용
var icon = UTKMaterialIcons.GetIcon("settings");
UTKMaterialIcons.ApplyIconStyle(label, fontSize: 24);
// 버튼에 아이콘 설정
button.SetMaterialIcon(UTKMaterialIcons.Home);
```
**2순위: Image Icons (UTKImageIcons)**
```csharp
// 필요한 아이콘이 Material에 없는 경우에만 사용
var texture = UTKImageIcons.LoadTexture("path/to/icon");
button.SetImageIcon(UTKImageIcons.CustomIcon);
```
> **참고**: Material Icons는 4,102개의 아이콘을 제공하며, 폰트 기반으로 메모리 효율이 높습니다.
---
## 3) 주요 컴포넌트
### UTKButton
다양한 스타일과 크기를 지원하는 버튼 컴포넌트입니다.
**Variants:**
- Normal, Primary, Secondary, Ghost, Danger
- OutlineNormal, OutlinePrimary, OutlineDanger, Text
**Sizes:** Small, Medium, Large
```csharp
var button = new UTKButton("확인", UTKButton.Variant.Primary, UTKButton.Size.Medium);
button.SetMaterialIcon(UTKMaterialIcons.Check);
button.OnClicked += () => Debug.Log("Clicked!");
// UXML 사용
<utk:UTKButton text="확인"
icon="check"
variant="Primary"
size="Medium"
icon-only="false" />
```
### UTKAccordionList
섹션별 접기/펼치기가 가능한 아코디언 리스트입니다.
```csharp
var accordionList = new UTKAccordionList();
accordionList.SetData(sections); // List<UTKAccordionListItemData>
accordionList.Search("검색어");
accordionList.ExpandAll();
accordionList.CollapseAll();
// 이벤트
accordionList.OnItemClick += (item) => { };
accordionList.OnDragExitList += (item) => { };
accordionList.OnSectionToggled += (section, expanded) => { };
```
**특징:**
- TreeView 기반 가상화
- 검색 필터링
- 드래그 앤 드롭 지원
- 비동기 이미지 로딩 및 캐싱
- 그리드 레이아웃 (2열)
### UTKTreeView / UTKTreeListWindow
계층 구조 데이터를 트리 형태로 표시합니다.
```csharp
var treeView = new UTKTreeView();
treeView.SetData(rootItems); // List<UTKTreeListItemData>
// TreeListWindow 사용
var window = new UTKTreeListWindow();
window.SetData(items);
window.OnItemSelected += (item) => { };
```
### UTKModal / UTKColorPicker / UTKDatePicker
모달 다이얼로그 및 특수 선택기입니다.
```csharp
// 기본 모달
var modal = new UTKModal("제목");
modal.AddContent(contentElement);
modal.AddToFooter(confirmButton);
modal.Show();
modal.OnClosed += () => { };
// 색상 선택기
var colorPicker = new UTKColorPicker();
colorPicker.Color = Color.red;
colorPicker.OnColorChanged += (color) => { };
// 날짜 선택기
var datePicker = new UTKDatePicker();
datePicker.SelectedDate = DateTime.Now;
datePicker.OnDateSelected += (date) => { };
```
### UTKImage
내부 리소스 및 외부 URL에서 이미지를 로드합니다.
```csharp
var image = new UTKImage();
// Resources 로드
await image.LoadAsync("UIToolkit/Icons/sample");
// HTTP/HTTPS 로드
await image.LoadAsync("https://example.com/image.png");
// 이벤트
image.OnImageLoaded += (texture) => { };
image.OnImageFailed += (error) => { };
// 스케일 모드
image.ScaleMode = ScaleMode.ScaleToFit; // ScaleToFit, ScaleAndCrop, StretchToFill
```
---
## 4) 프로퍼티 에디터 시스템
Inspector 스타일의 프로퍼티 편집 시스템입니다.
### 아키텍처
```
IUTKPropertyEntry (기본 인터페이스)
├── IUTKPropertyItem<T> # 개별 프로퍼티
│ ├── OnValueChanged event
│ ├── GetValue() / SetValue()
│ └── CreateUI() / BindUI() / UnbindUI()
└── IUTKPropertyGroup # 프로퍼티 그룹
├── Items 컬렉션
├── AddItem() / RemoveItem()
└── OnExpandedChanged event
```
### 프로퍼티 타입 (16종)
| 타입 | 클래스 | 설명 |
|------|--------|------|
| String | UTKStringPropertyItem | 텍스트 입력 |
| Int | UTKIntPropertyItem | 정수 (슬라이더 옵션) |
| Float | UTKFloatPropertyItem | 실수 (슬라이더 옵션) |
| Bool | UTKBoolPropertyItem | 토글 스위치 |
| Vector2 | UTKVector2PropertyItem | 2D 벡터 |
| Vector3 | UTKVector3PropertyItem | 3D 벡터 |
| Color | UTKColorPropertyItem | 색상 선택기 |
| Date | UTKDatePropertyItem | 날짜 선택 |
| DateTime | UTKDateTimePropertyItem | 날짜+시간 선택 |
| Enum | UTKEnumPropertyItem | Enum 드롭다운 |
| Dropdown | UTKDropdownPropertyItem | 문자열 드롭다운 |
| Radio | UTKRadioPropertyItem | 라디오 버튼 그룹 |
| IntRange | UTKIntRangePropertyItem | 정수 범위 (Min/Max) |
| FloatRange | UTKFloatRangePropertyItem | 실수 범위 |
| DateRange | UTKDateRangePropertyItem | 날짜 범위 |
| DateTimeRange | UTKDateTimeRangePropertyItem | 날짜+시간 범위 |
| ColorState | UTKColorStatePropertyItem | 색상+상태 (커스텀) |
### 사용 예시
```csharp
// 프로퍼티 윈도우 생성
var propertyWindow = new UTKPropertyWindow("설정");
// 그룹 생성
var generalGroup = new UTKPropertyGroup("일반 설정");
// 프로퍼티 아이템 추가
generalGroup.AddItem(new UTKStringPropertyItem("이름", "기본값"));
generalGroup.AddItem(new UTKIntPropertyItem("레벨", 1, minValue: 1, maxValue: 100));
generalGroup.AddItem(new UTKBoolPropertyItem("활성화", true));
generalGroup.AddItem(new UTKColorPropertyItem("색상", Color.white));
// 그룹을 윈도우에 추가
propertyWindow.PropertyList.AddGroup(generalGroup);
// 값 변경 이벤트
generalGroup.OnValueChanged += (sender, args) => {
Debug.Log($"{args.PropertyName}: {args.OldValue} → {args.NewValue}");
};
```
**특징:**
- 디바운스 지원 (기본 300ms)
- 읽기 전용 모드
- 툴팁 지원
- IDisposable 패턴으로 메모리 관리
---
## 5) Window 컴포넌트
### UTKAccordionListWindow
아코디언 리스트를 포함한 윈도우입니다.
```csharp
var window = new UTKAccordionListWindow();
window.SetTitle("아이템 목록");
window.SetData(sections);
window.OnItemSelected += (item) => { };
```
### UTKComponentListWindow
컴포넌트 리스트 윈도우입니다.
```csharp
var window = new UTKComponentListWindow();
window.SetData(components);
window.OnComponentSelected += (component) => { };
```
### UTKTreeListWindow
트리 구조 데이터를 표시하는 윈도우입니다.
```csharp
var window = new UTKTreeListWindow();
window.SetData(treeItems);
window.OnItemSelected += (item) => { };
window.ExpandAll();
```
### UTKImageListWindow
이미지 그리드를 표시하는 윈도우입니다.
```csharp
var window = new UTKImageListWindow();
window.SetData(images);
window.OnImageSelected += (image) => { };
```
---
## 6) 입력 필드
### 기본 입력 필드
```csharp
// 정수 입력
var intField = new UTKIntegerField("수량");
intField.Value = 10;
intField.OnValueChanged += (value) => { };
// 실수 입력
var floatField = new UTKFloatField("크기");
floatField.Value = 1.5f;
// 벡터 입력
var vector3Field = new UTKVector3Field("위치");
vector3Field.Value = Vector3.zero;
```
### UTKNumberStepper
증가/감소 버튼이 있는 숫자 입력 필드입니다.
```csharp
var stepper = new UTKNumberStepper();
stepper.Value = 5;
stepper.Min = 0;
stepper.Max = 100;
stepper.Step = 1;
```
---
## 7) UXML 속성 규칙
Unity 6 소스 생성기 기반의 `[UxmlElement]``[UxmlAttribute]`를 사용합니다.
```csharp
[UxmlElement]
public partial class UTKCustomComponent : VisualElement
{
[UxmlAttribute("title")]
public string Title { get; set; }
[UxmlAttribute("is-enabled")]
public bool IsEnabled { get; set; }
[UxmlAttribute("border-width")]
public int BorderWidth { get; set; }
}
```
**UXML 사용:**
```xml
<utk:UTKCustomComponent title="제목" is-enabled="true" border-width="2" />
```
**주의사항:**
- 클래스는 반드시 `partial`로 선언
- 속성명은 케밥 케이스(kebab-case) 소문자 사용
- 레거시 `UxmlFactory`/`UxmlTraits` 방식 사용 금지
---
## 8) 네이밍 규칙
| 항목 | 규칙 | 예시 |
|------|------|------|
| 클래스 | UTK 접두사 + PascalCase | `UTKButton`, `UTKAccordionList` |
| UXML 파일 | `{ComponentName}.uxml` | `UTKAccordionListWindow.uxml` |
| USS 파일 | `{ComponentName}Uss.uss` | `UTKAccordionListWindowUss.uss` |
| Public 멤버 | PascalCase | `OnClicked`, `Value` |
| Private 필드 | _camelCase | `_button`, `_labelCache` |
| 이벤트 | On 접두사 | `OnValueChanged`, `OnClicked` |
> **중요**: UXML과 USS 파일명을 다르게 지정해야 `Resources.Load<T>()` 충돌을 방지할 수 있습니다.
---
## 9) 메모리 관리 패턴
### 이벤트 구독/해제
```csharp
private EventCallback<ClickEvent>? _onClick;
void OnEnable()
{
_onClick = OnButtonClick;
_button?.RegisterCallback(_onClick);
UTKThemeManager.OnThemeChanged += OnThemeChanged;
}
void OnDisable()
{
_button?.UnregisterCallback(_onClick);
UTKThemeManager.OnThemeChanged -= OnThemeChanged;
}
```
### IDisposable 패턴
```csharp
public void Dispose()
{
UnregisterEvents();
_cts?.Cancel();
_cts?.Dispose();
_cts = null;
}
```
### 체크리스트
- [ ] `RegisterCallback<T>``UnregisterCallback<T>` 대칭 확인
- [ ] `UTKThemeManager.OnThemeChanged` 구독 해제
- [ ] `CancellationTokenSource` 정리 (`Cancel``Dispose`)
- [ ] 람다/클로저 캡처로 인한 누수 점검
- [ ] 이미지 캐시 정리 (해당 시)
---
## 10) 주요 API 요약
| 카테고리 | 클래스 | 주요 메서드 |
|----------|--------|-------------|
| **테마** | UTKThemeManager | `RegisterRoot`, `SetTheme`, `ToggleTheme` |
| **버튼** | UTKButton | `SetMaterialIcon`, `SetImageIcon`, `OnClicked` |
| **리스트** | UTKAccordionList | `SetData`, `Search`, `ExpandAll`, `OnItemClick` |
| **트리** | UTKTreeView | `SetData`, `ExpandAll`, `CollapseAll` |
| **프로퍼티** | UTKPropertyWindow | `LoadProperties`, `AddGroup`, `UpdatePropertyValue` |
| **모달** | UTKModal | `Show`, `Close`, `AddContent`, `AddToFooter` |
| **이미지** | UTKImage | `LoadAsync`, `SetTexture`, `ClearImage` |
| **입력** | UTK*Field | `Value`, `OnValueChanged` |
---
## 11) 참조 리소스
| 경로 | 설명 |
|------|------|
| `Assets/Resources/UIToolkit/Style/` | USS 스타일 파일 |
| `Assets/Resources/UIToolkit/Common/` | 공통 UXML 컴포넌트 |
| `Assets/Resources/UIToolkit/Window/` | 윈도우 UXML 템플릿 |
| `Assets/Resources/UIToolkit/Modal/` | 모달 UXML 템플릿 |
| `Assets/Resources/UIToolkit/List/` | 리스트 UXML 템플릿 |
| `Assets/Resources/UIToolkit/Property/` | 프로퍼티 UXML 템플릿 |

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: c578c7c6f7f524444942296a64ad16be
guid: 212bcd23b16ea4443a97af8619e3d333
TextScriptImporter:
externalObjects: {}
userData:

View File

@@ -83,24 +83,24 @@ namespace UVC.UIToolkit
/// </code>
/// <para><b>UXML에서 사용:</b></para>
/// <code>
/// &lt;ui:UXML xmlns:utk="UVC.UIToolkit"&gt;
/// &lt;!-- 내부 리소스 이미지 --&gt;
/// &lt;utk:UTKImage src="UIToolkit/Images/icon_setting_22"
/// style="width: 48px; height: 48px;" /&gt;
/// <ui:UXML xmlns:utk="UVC.UIToolkit">
/// <!-- 내부 리소스 이미지 -->
/// <utk:UTKImage src="UIToolkit/Images/icon_setting_22"
/// style="width: 48px; height: 48px;" />
///
/// &lt;!-- 외부 URL 이미지 --&gt;
/// &lt;utk:UTKImage src="https://example.com/image.png"
/// <!-- 외부 URL 이미지 -->
/// <utk:UTKImage src="https://example.com/image.png"
/// style="width: 200px; height: 150px;"
/// scale-mode="ScaleToFit" /&gt;
/// scale-mode="ScaleToFit" />
///
/// &lt;!-- 둥근 프로필 이미지 --&gt;
/// &lt;utk:UTKImage src="https://api.example.com/avatar"
/// class="profile-avatar" /&gt;
/// <!-- 둥근 프로필 이미지 -->
/// <utk:UTKImage src="https://api.example.com/avatar"
/// class="profile-avatar" />
///
/// &lt;!-- 틴트 색상 적용 --&gt;
/// &lt;utk:UTKImage src="UIToolkit/Images/icon_home"
/// tint-color="#FF5500" /&gt;
/// &lt;/ui:UXML&gt;
/// <!-- 틴트 색상 적용 -->
/// <utk:UTKImage src="UIToolkit/Images/icon_home"
/// tint-color="#FF5500" />
/// </ui:UXML>
/// </code>
/// </example>
[UxmlElement]

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: e911fc8aa11c95841af462e5a845974b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,5 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
@@ -18,6 +19,11 @@ namespace UVC.UIToolkit
protected const int DEFAULT_DEBOUNCE_MS = 300;
protected const string USS_CLASS_READONLY = "utk-property-item--readonly";
protected const string USS_CLASS_HIDDEN = "utk-property-item--hidden";
protected const string UXML_BASE_PATH = "UIToolkit/Property/Items/";
#endregion
#region Static Cache
private static readonly Dictionary<string, VisualTreeAsset> _uxmlCache = new();
#endregion
#region Fields
@@ -243,9 +249,9 @@ namespace UVC.UIToolkit
_rootElement.SetTooltip(_tooltip);
}
protected Label CreateNameLabel()
protected UTKLabel CreateNameLabel()
{
var label = new Label(Name);
var label = new UTKLabel(Name, UTKLabel.LabelSize.Body2);
label.AddToClassList("utk-property-item__label");
return label;
}
@@ -263,6 +269,62 @@ namespace UVC.UIToolkit
return container;
}
/// <summary>
/// UXML 템플릿에서 UI 생성
/// </summary>
/// <param name="uxmlName">UXML 파일명 (확장자 제외)</param>
/// <returns>생성된 루트 VisualElement</returns>
protected VisualElement? CreateUIFromUxml(string uxmlName)
{
var asset = LoadUxmlAsset(uxmlName);
if (asset == null)
{
Debug.LogWarning($"[UTKPropertyItem] UXML not found: {uxmlName}");
return null;
}
var root = asset.Instantiate();
var itemRoot = root.Q<VisualElement>("item-root") ?? root;
// name-label에 Name 설정
var nameLabel = itemRoot.Q<UTKLabel>("name-label");
if (nameLabel != null)
{
nameLabel.Text = Name;
}
return itemRoot;
}
/// <summary>
/// UXML 에셋 로드 (캐시 사용)
/// </summary>
protected static VisualTreeAsset? LoadUxmlAsset(string uxmlName)
{
string path = UXML_BASE_PATH + uxmlName;
if (_uxmlCache.TryGetValue(path, out var cached))
{
return cached;
}
var asset = Resources.Load<VisualTreeAsset>(path);
if (asset != null)
{
_uxmlCache[path] = asset;
}
return asset;
}
/// <summary>
/// UXML 캐시 클리어 (에디터 용도)
/// </summary>
public static void ClearUxmlCache()
{
_uxmlCache.Clear();
}
#endregion
#region IDisposable

View File

@@ -10,7 +10,7 @@ namespace UVC.UIToolkit
public class UTKBoolPropertyItem : UTKPropertyItemBase<bool>
{
#region Fields
private Toggle? _toggle;
private UTKToggle? _toggle;
#endregion
#region Properties
@@ -26,6 +26,23 @@ namespace UVC.UIToolkit
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateUIFromUxml("UTKBoolPropertyItem");
if (container == null)
{
return CreateUIFallback();
}
_toggle = container.Q<UTKToggle>("value-field");
if (_toggle != null)
{
_toggle.IsOn = Value;
}
return container;
}
private VisualElement CreateUIFallback()
{
var container = CreateContainer();
@@ -35,9 +52,9 @@ namespace UVC.UIToolkit
var valueContainer = new VisualElement();
valueContainer.AddToClassList("utk-property-item__value");
_toggle = new Toggle();
_toggle = new UTKToggle();
_toggle.name = "value-field";
_toggle.value = Value;
_toggle.IsOn = Value;
valueContainer.Add(_toggle);
container.Add(valueContainer);
@@ -49,12 +66,12 @@ namespace UVC.UIToolkit
{
base.BindUI(element);
_toggle = element.Q<Toggle>("value-field");
_toggle = element.Q<UTKToggle>("value-field");
if (_toggle != null)
{
_toggle.value = Value;
_toggle.SetEnabled(!IsReadOnly);
_toggle.RegisterValueChangedCallback(OnToggleChanged);
_toggle.IsOn = Value;
_toggle.IsEnabled = !IsReadOnly;
_toggle.OnValueChanged += OnToggleChanged;
}
}
@@ -62,7 +79,7 @@ namespace UVC.UIToolkit
{
if (_toggle != null)
{
_toggle.UnregisterValueChangedCallback(OnToggleChanged);
_toggle.OnValueChanged -= OnToggleChanged;
_toggle = null;
}
@@ -71,9 +88,9 @@ namespace UVC.UIToolkit
public override void RefreshUI()
{
if (_toggle != null && _toggle.value != Value)
if (_toggle != null && _toggle.IsOn != Value)
{
_toggle.SetValueWithoutNotify(Value);
_toggle.SetOn(Value, false);
}
}
@@ -83,16 +100,16 @@ namespace UVC.UIToolkit
if (_toggle != null)
{
_toggle.SetEnabled(!IsReadOnly);
_toggle.IsEnabled = !IsReadOnly;
}
}
#endregion
#region Private Methods
private void OnToggleChanged(ChangeEvent<bool> evt)
private void OnToggleChanged(bool newValue)
{
// Toggle은 디바운스 없이 즉시 적용
Value = evt.newValue;
Value = newValue;
}
#endregion
}

View File

@@ -12,8 +12,8 @@ namespace UVC.UIToolkit
{
#region Fields
private VisualElement? _colorPreview;
private Button? _pickerButton;
private TextField? _hexField;
private UTKButton? _pickerButton;
private UTKInputField? _hexField;
private UTKColorPicker? _currentPicker;
private bool _useAlpha;
#endregion
@@ -39,6 +39,31 @@ namespace UVC.UIToolkit
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateUIFromUxml("UTKColorPropertyItem");
if (container == null)
{
return CreateUIFallback();
}
_colorPreview = container.Q<VisualElement>("color-preview");
_hexField = container.Q<UTKInputField>("hex-field");
_pickerButton = container.Q<UTKButton>("picker-btn");
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value;
}
if (_hexField != null)
{
_hexField.Value = ColorToHex(Value);
}
return container;
}
private VisualElement CreateUIFallback()
{
var container = CreateContainer();
@@ -56,17 +81,16 @@ namespace UVC.UIToolkit
valueContainer.Add(_colorPreview);
// Hex 입력
_hexField = new TextField();
_hexField = new UTKInputField();
_hexField.name = "hex-field";
_hexField.value = ColorToHex(Value);
_hexField.Value = ColorToHex(Value);
_hexField.style.width = 80;
_hexField.style.marginLeft = 5;
valueContainer.Add(_hexField);
// 피커 버튼
_pickerButton = new Button();
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.text = "...";
_pickerButton.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_pickerButton);
@@ -80,8 +104,8 @@ namespace UVC.UIToolkit
base.BindUI(element);
_colorPreview = element.Q<VisualElement>("color-preview");
_hexField = element.Q<TextField>("hex-field");
_pickerButton = element.Q<Button>("picker-btn");
_hexField = element.Q<UTKInputField>("hex-field");
_pickerButton = element.Q<UTKButton>("picker-btn");
if (_colorPreview != null)
{
@@ -91,15 +115,15 @@ namespace UVC.UIToolkit
if (_hexField != null)
{
_hexField.value = ColorToHex(Value);
_hexField.Value = ColorToHex(Value);
_hexField.SetEnabled(!IsReadOnly);
_hexField.RegisterValueChangedCallback(OnHexChanged);
_hexField.OnValueChanged += OnHexChanged;
}
if (_pickerButton != null)
{
_pickerButton.SetEnabled(!IsReadOnly);
_pickerButton.clicked += OnPickerButtonClicked;
_pickerButton.IsEnabled = !IsReadOnly;
_pickerButton.OnClicked += OnPickerButtonClicked;
}
}
@@ -113,13 +137,13 @@ namespace UVC.UIToolkit
if (_hexField != null)
{
_hexField.UnregisterValueChangedCallback(OnHexChanged);
_hexField.OnValueChanged -= OnHexChanged;
_hexField = null;
}
if (_pickerButton != null)
{
_pickerButton.clicked -= OnPickerButtonClicked;
_pickerButton.OnClicked -= OnPickerButtonClicked;
_pickerButton = null;
}
@@ -138,9 +162,9 @@ namespace UVC.UIToolkit
if (_hexField != null)
{
var hex = ColorToHex(Value);
if (_hexField.value != hex)
if (_hexField.Value != hex)
{
_hexField.SetValueWithoutNotify(hex);
_hexField.SetValue(hex, false);
}
}
}
@@ -150,7 +174,10 @@ namespace UVC.UIToolkit
base.UpdateReadOnlyState();
_hexField?.SetEnabled(!IsReadOnly);
_pickerButton?.SetEnabled(!IsReadOnly);
if (_pickerButton != null)
{
_pickerButton.IsEnabled = !IsReadOnly;
}
}
#endregion
@@ -205,7 +232,7 @@ namespace UVC.UIToolkit
if (_hexField != null)
{
_hexField.SetValueWithoutNotify(ColorToHex(color));
_hexField.SetValue(ColorToHex(color), false);
}
}
@@ -215,9 +242,9 @@ namespace UVC.UIToolkit
ClosePicker();
}
private void OnHexChanged(ChangeEvent<string> evt)
private void OnHexChanged(string newValue)
{
if (TryParseHex(evt.newValue, out Color color))
if (TryParseHex(newValue, out Color color))
{
DebounceValueChange(color, 300).Forget();
}

View File

@@ -11,9 +11,9 @@ namespace UVC.UIToolkit
public class UTKColorStatePropertyItem : UTKPropertyItemBase<UTKColorState>
{
#region Fields
private Label? _stateLabel;
private UTKLabel? _stateLabel;
private VisualElement? _colorPreview;
private Button? _pickerButton;
private UTKButton? _pickerButton;
private UTKColorPicker? _currentPicker;
#endregion
@@ -35,6 +35,31 @@ namespace UVC.UIToolkit
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateUIFromUxml("UTKColorStatePropertyItem");
if (container == null)
{
return CreateUIFallback();
}
_stateLabel = container.Q<UTKLabel>("state-label");
_colorPreview = container.Q<VisualElement>("color-preview");
_pickerButton = container.Q<UTKButton>("picker-btn");
if (_stateLabel != null)
{
_stateLabel.Text = Value.State;
}
if (_colorPreview != null)
{
_colorPreview.style.backgroundColor = Value.Color;
}
return container;
}
private VisualElement CreateUIFallback()
{
var container = CreateContainer();
@@ -46,7 +71,7 @@ namespace UVC.UIToolkit
valueContainer.style.flexDirection = FlexDirection.Row;
// 상태 라벨
_stateLabel = new Label(Value.State);
_stateLabel = new UTKLabel(Value.State, UTKLabel.LabelSize.Body2);
_stateLabel.name = "state-label";
_stateLabel.AddToClassList("utk-property-item__state-label");
valueContainer.Add(_stateLabel);
@@ -59,7 +84,7 @@ namespace UVC.UIToolkit
valueContainer.Add(_colorPreview);
// 피커 버튼
_pickerButton = new Button { text = "..." };
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_pickerButton);
@@ -73,13 +98,13 @@ namespace UVC.UIToolkit
{
base.BindUI(element);
_stateLabel = element.Q<Label>("state-label");
_stateLabel = element.Q<UTKLabel>("state-label");
_colorPreview = element.Q<VisualElement>("color-preview");
_pickerButton = element.Q<Button>("picker-btn");
_pickerButton = element.Q<UTKButton>("picker-btn");
if (_stateLabel != null)
{
_stateLabel.text = Value.State;
_stateLabel.Text = Value.State;
}
if (_colorPreview != null)
@@ -90,8 +115,8 @@ namespace UVC.UIToolkit
if (_pickerButton != null)
{
_pickerButton.SetEnabled(!IsReadOnly);
_pickerButton.clicked += OnPickerButtonClicked;
_pickerButton.IsEnabled = !IsReadOnly;
_pickerButton.OnClicked += OnPickerButtonClicked;
}
}
@@ -105,7 +130,7 @@ namespace UVC.UIToolkit
if (_pickerButton != null)
{
_pickerButton.clicked -= OnPickerButtonClicked;
_pickerButton.OnClicked -= OnPickerButtonClicked;
_pickerButton = null;
}
@@ -116,9 +141,9 @@ namespace UVC.UIToolkit
public override void RefreshUI()
{
if (_stateLabel != null && _stateLabel.text != Value.State)
if (_stateLabel != null && _stateLabel.Text != Value.State)
{
_stateLabel.text = Value.State;
_stateLabel.Text = Value.State;
}
if (_colorPreview != null)
@@ -130,7 +155,7 @@ namespace UVC.UIToolkit
protected override void UpdateReadOnlyState()
{
base.UpdateReadOnlyState();
_pickerButton?.SetEnabled(!IsReadOnly);
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
}
#endregion

View File

@@ -11,8 +11,8 @@ namespace UVC.UIToolkit
public class UTKDatePropertyItem : UTKPropertyItemBase<DateTime>
{
#region Fields
private TextField? _dateField;
private Button? _pickerButton;
private UTKInputField? _dateField;
private UTKButton? _pickerButton;
private UTKDatePicker? _currentPicker;
private string _dateFormat = "yyyy-MM-dd";
#endregion
@@ -41,6 +41,25 @@ namespace UVC.UIToolkit
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateUIFromUxml("UTKDatePropertyItem");
if (container == null)
{
return CreateUIFallback();
}
_dateField = container.Q<UTKInputField>("date-field");
_pickerButton = container.Q<UTKButton>("picker-btn");
if (_dateField != null)
{
_dateField.Value = Value.ToString(_dateFormat);
}
return container;
}
private VisualElement CreateUIFallback()
{
var container = CreateContainer();
@@ -50,15 +69,14 @@ namespace UVC.UIToolkit
var valueContainer = new VisualElement();
valueContainer.AddToClassList("utk-property-item__value");
_dateField = new TextField();
_dateField = new UTKInputField();
_dateField.name = "date-field";
_dateField.value = Value.ToString(_dateFormat);
_dateField.Value = Value.ToString(_dateFormat);
_dateField.style.flexGrow = 1;
valueContainer.Add(_dateField);
_pickerButton = new Button();
_pickerButton = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_pickerButton.name = "picker-btn";
_pickerButton.text = "...";
_pickerButton.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_pickerButton);
@@ -71,20 +89,20 @@ namespace UVC.UIToolkit
{
base.BindUI(element);
_dateField = element.Q<TextField>("date-field");
_pickerButton = element.Q<Button>("picker-btn");
_dateField = element.Q<UTKInputField>("date-field");
_pickerButton = element.Q<UTKButton>("picker-btn");
if (_dateField != null)
{
_dateField.value = Value.ToString(_dateFormat);
_dateField.Value = Value.ToString(_dateFormat);
_dateField.SetEnabled(!IsReadOnly);
_dateField.RegisterValueChangedCallback(OnDateTextChanged);
_dateField.OnValueChanged += OnDateTextChanged;
}
if (_pickerButton != null)
{
_pickerButton.SetEnabled(!IsReadOnly);
_pickerButton.clicked += OnPickerButtonClicked;
_pickerButton.IsEnabled = !IsReadOnly;
_pickerButton.OnClicked += OnPickerButtonClicked;
}
}
@@ -92,13 +110,13 @@ namespace UVC.UIToolkit
{
if (_dateField != null)
{
_dateField.UnregisterValueChangedCallback(OnDateTextChanged);
_dateField.OnValueChanged -= OnDateTextChanged;
_dateField = null;
}
if (_pickerButton != null)
{
_pickerButton.clicked -= OnPickerButtonClicked;
_pickerButton.OnClicked -= OnPickerButtonClicked;
_pickerButton = null;
}
@@ -112,9 +130,9 @@ namespace UVC.UIToolkit
if (_dateField != null)
{
var formatted = Value.ToString(_dateFormat);
if (_dateField.value != formatted)
if (_dateField.Value != formatted)
{
_dateField.SetValueWithoutNotify(formatted);
_dateField.SetValue(formatted, false);
}
}
}
@@ -124,7 +142,7 @@ namespace UVC.UIToolkit
base.UpdateReadOnlyState();
_dateField?.SetEnabled(!IsReadOnly);
_pickerButton?.SetEnabled(!IsReadOnly);
if (_pickerButton != null) _pickerButton.IsEnabled = !IsReadOnly;
}
#endregion
@@ -171,9 +189,9 @@ namespace UVC.UIToolkit
_currentPicker = null;
}
private void OnDateTextChanged(ChangeEvent<string> evt)
private void OnDateTextChanged(string newValue)
{
if (DateTime.TryParse(evt.newValue, out DateTime date))
if (DateTime.TryParse(newValue, out DateTime date))
{
DebounceValueChange(date.Date, 500).Forget();
}

View File

@@ -11,10 +11,10 @@ namespace UVC.UIToolkit
public class UTKDateRangePropertyItem : UTKPropertyItemBase<UTKDateRange>
{
#region Fields
private TextField? _startField;
private TextField? _endField;
private Button? _startPickerBtn;
private Button? _endPickerBtn;
private UTKInputField? _startField;
private UTKInputField? _endField;
private UTKButton? _startPickerBtn;
private UTKButton? _endPickerBtn;
private UTKDatePicker? _currentPicker;
private bool _isEditingStart;
private string _dateFormat = "yyyy-MM-dd";
@@ -48,6 +48,32 @@ namespace UVC.UIToolkit
#region Override Methods
public override VisualElement CreateUI()
{
var container = CreateUIFromUxml("UTKDateRangePropertyItem");
if (container == null)
{
return CreateUIFallback();
}
_startField = container.Q<UTKInputField>("start-field");
_endField = container.Q<UTKInputField>("end-field");
_startPickerBtn = container.Q<UTKButton>("start-picker-btn");
_endPickerBtn = container.Q<UTKButton>("end-picker-btn");
if (_startField != null)
{
_startField.Value = Value.Start.ToString(_dateFormat);
}
if (_endField != null)
{
_endField.Value = Value.End.ToString(_dateFormat);
}
return container;
}
private VisualElement CreateUIFallback()
{
var container = CreateContainer();
@@ -59,29 +85,29 @@ namespace UVC.UIToolkit
valueContainer.style.flexDirection = FlexDirection.Row;
// Start field
_startField = new TextField();
_startField = new UTKInputField();
_startField.name = "start-field";
_startField.value = Value.Start.ToString(_dateFormat);
_startField.Value = Value.Start.ToString(_dateFormat);
_startField.style.flexGrow = 1;
valueContainer.Add(_startField);
_startPickerBtn = new Button { text = "..." };
_startPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_startPickerBtn.name = "start-picker-btn";
_startPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_startPickerBtn);
var separator = new Label("~");
var separator = new UTKLabel("~", UTKLabel.LabelSize.Body2);
separator.AddToClassList("utk-property-item__range-separator");
valueContainer.Add(separator);
// End field
_endField = new TextField();
_endField = new UTKInputField();
_endField.name = "end-field";
_endField.value = Value.End.ToString(_dateFormat);
_endField.Value = Value.End.ToString(_dateFormat);
_endField.style.flexGrow = 1;
valueContainer.Add(_endField);
_endPickerBtn = new Button { text = "..." };
_endPickerBtn = new UTKButton("...", "", UTKButton.ButtonVariant.Secondary);
_endPickerBtn.name = "end-picker-btn";
_endPickerBtn.AddToClassList("utk-property-item__picker-btn");
valueContainer.Add(_endPickerBtn);
@@ -95,35 +121,35 @@ namespace UVC.UIToolkit
{
base.BindUI(element);
_startField = element.Q<TextField>("start-field");
_endField = element.Q<TextField>("end-field");
_startPickerBtn = element.Q<Button>("start-picker-btn");
_endPickerBtn = element.Q<Button>("end-picker-btn");
_startField = element.Q<UTKInputField>("start-field");
_endField = element.Q<UTKInputField>("end-field");
_startPickerBtn = element.Q<UTKButton>("start-picker-btn");
_endPickerBtn = element.Q<UTKButton>("end-picker-btn");
if (_startField != null)
{
_startField.value = Value.Start.ToString(_dateFormat);
_startField.Value = Value.Start.ToString(_dateFormat);
_startField.SetEnabled(!IsReadOnly);
_startField.RegisterValueChangedCallback(OnStartTextChanged);
_startField.OnValueChanged += OnStartTextChanged;
}
if (_endField != null)
{
_endField.value = Value.End.ToString(_dateFormat);
_endField.Value = Value.End.ToString(_dateFormat);
_endField.SetEnabled(!IsReadOnly);
_endField.RegisterValueChangedCallback(OnEndTextChanged);
_endField.OnValueChanged += OnEndTextChanged;
}
if (_startPickerBtn != null)
{
_startPickerBtn.SetEnabled(!IsReadOnly);
_startPickerBtn.clicked += OnStartPickerClicked;
_startPickerBtn.IsEnabled = !IsReadOnly;
_startPickerBtn.OnClicked += OnStartPickerClicked;
}
if (_endPickerBtn != null)
{
_endPickerBtn.SetEnabled(!IsReadOnly);
_endPickerBtn.clicked += OnEndPickerClicked;
_endPickerBtn.IsEnabled = !IsReadOnly;
_endPickerBtn.OnClicked += OnEndPickerClicked;
}
}
@@ -131,25 +157,25 @@ namespace UVC.UIToolkit
{
if (_startField != null)
{
_startField.UnregisterValueChangedCallback(OnStartTextChanged);
_startField.OnValueChanged -= OnStartTextChanged;
_startField = null;
}
if (_endField != null)
{
_endField.UnregisterValueChangedCallback(OnEndTextChanged);
_endField.OnValueChanged -= OnEndTextChanged;
_endField = null;
}
if (_startPickerBtn != null)
{
_startPickerBtn.clicked -= OnStartPickerClicked;
_startPickerBtn.OnClicked -= OnStartPickerClicked;
_startPickerBtn = null;
}
if (_endPickerBtn != null)
{
_endPickerBtn.clicked -= OnEndPickerClicked;
_endPickerBtn.OnClicked -= OnEndPickerClicked;
_endPickerBtn = null;
}
@@ -162,18 +188,18 @@ namespace UVC.UIToolkit
if (_startField != null)
{
var formatted = Value.Start.ToString(_dateFormat);
if (_startField.value != formatted)
if (_startField.Value != formatted)
{
_startField.SetValueWithoutNotify(formatted);
_startField.SetValue(formatted, false);
}
}
if (_endField != null)
{
var formatted = Value.End.ToString(_dateFormat);
if (_endField.value != formatted)
if (_endField.Value != formatted)
{
_endField.SetValueWithoutNotify(formatted);
_endField.SetValue(formatted, false);
}
}
}
@@ -184,8 +210,8 @@ namespace UVC.UIToolkit
_startField?.SetEnabled(!IsReadOnly);
_endField?.SetEnabled(!IsReadOnly);
_startPickerBtn?.SetEnabled(!IsReadOnly);
_endPickerBtn?.SetEnabled(!IsReadOnly);
if (_startPickerBtn != null) _startPickerBtn.IsEnabled = !IsReadOnly;
if (_endPickerBtn != null) _endPickerBtn.IsEnabled = !IsReadOnly;
}
#endregion
@@ -241,21 +267,21 @@ namespace UVC.UIToolkit
private void OnPickerClosed() => _currentPicker = null;
private void OnStartTextChanged(ChangeEvent<string> evt)
private void OnStartTextChanged(string newValue)
{
if (DateTime.TryParse(evt.newValue, out DateTime date))
if (DateTime.TryParse(newValue, out DateTime date))
{
var newValue = new UTKDateRange(date.Date, Value.End);
DebounceValueChange(newValue, 500).Forget();
var range = new UTKDateRange(date.Date, Value.End);
DebounceValueChange(range, 500).Forget();
}
}
private void OnEndTextChanged(ChangeEvent<string> evt)
private void OnEndTextChanged(string newValue)
{
if (DateTime.TryParse(evt.newValue, out DateTime date))
if (DateTime.TryParse(newValue, out DateTime date))
{
var newValue = new UTKDateRange(Value.Start, date.Date);
DebounceValueChange(newValue, 500).Forget();
var range = new UTKDateRange(Value.Start, date.Date);
DebounceValueChange(range, 500).Forget();
}
}
#endregion

Some files were not shown because too many files have changed in this diff Show More