Compare commits
3 Commits
739a62eb9b
...
9d02afd8e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d02afd8e8 | ||
|
|
b64c3e10bc | ||
|
|
ad10e24d13 |
@@ -33,7 +33,9 @@
|
||||
"Bash(cmd /c \"mkdir \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Scripts\\\\UVC\\\\UIToolkit\\\\ToolBar\\\\Data\"\" && mkdir \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Scripts\\\\UVC\\\\UIToolkit\\\\ToolBar\\\\Items\"\" && mkdir \"\"d:\\\\works\\\\2025\\\\02.Studio\\\\dev\\\\base\\\\XRBase\\\\Assets\\\\Resources\\\\UIToolkit\\\\ToolBar\"\"\")",
|
||||
"Bash(/bin/ls:*)",
|
||||
"Bash(/bin/mkdir -p:*)",
|
||||
"Bash(/bin/rm:*)"
|
||||
"Bash(/bin/rm:*)",
|
||||
"WebFetch(domain:docs.unity3d.com)",
|
||||
"Bash(ls:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
.utk-checkbox {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-self: flex-start;
|
||||
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
-->
|
||||
<ui:VisualElement name="group-header" class="utk-property-group__header">
|
||||
<utk:UTKLabel name="expand-icon" class="utk-property-group__expand-icon" />
|
||||
<utk:UTKLabel name="group-title" class="utk-property-group__title" />
|
||||
<utk:UTKLabel name="group-title" class="utk-property-group__title" size="Label2" is-bold="true" />
|
||||
<!-- <utk:UTKLabel name="group-count" class="utk-property-group__count" /> -->
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
||||
17
Assets/Resources/UIToolkit/List/UTKReordableListItem.uxml
Normal file
17
Assets/Resources/UIToolkit/List/UTKReordableListItem.uxml
Normal file
@@ -0,0 +1,17 @@
|
||||
<ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ui="UnityEngine.UIElements"
|
||||
xmlns:utk="UVC.UIToolkit"
|
||||
noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd"
|
||||
editor-extension-mode="False">
|
||||
<!-- USS는 테마 적용을 위해 C# 코드에서 로드합니다 (UXML에서 지정하지 않음) -->
|
||||
<ui:VisualElement name="item-container" class="reordable-list-item">
|
||||
<!-- 드래그 핸들 아이콘 (Material Icon: DragIndicator) -->
|
||||
<utk:UTKLabel name="drag-handle" class="reordable-list-item__drag-handle" />
|
||||
|
||||
<!-- 사용 유무 체크박스 -->
|
||||
<utk:UTKCheckBox name="active-checkbox" class="reordable-list-item__checkbox" />
|
||||
|
||||
<!-- 내용 표시/수정 입력 필드 -->
|
||||
<utk:UTKInputField name="display-text-field" class="reordable-list-item__input" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83ba2c4f240fbfb44a4ed4acd25aa5ce
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
57
Assets/Resources/UIToolkit/List/UTKReordableListItemUss.uss
Normal file
57
Assets/Resources/UIToolkit/List/UTKReordableListItemUss.uss
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKReordableListItemUss.uss
|
||||
* 재정렬 가능 리스트 아이템 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Item Container
|
||||
=================================== */
|
||||
|
||||
.reordable-list-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
min-height: 36px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Drag Handle
|
||||
=================================== */
|
||||
|
||||
.reordable-list-item__drag-handle {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
-unity-font-definition: resource('Fonts/Icons/MaterialSymbolsOutlined');
|
||||
font-size: 18px;
|
||||
color: var(--color-text-secondary);
|
||||
-unity-text-align: middle-center;
|
||||
flex-shrink: 0;
|
||||
margin-right: var(--space-xs);
|
||||
cursor: move;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reordable-list-item__drag-handle:hover {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Checkbox
|
||||
=================================== */
|
||||
|
||||
.reordable-list-item__checkbox {
|
||||
flex-shrink: 0;
|
||||
margin-right: var(--space-s);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
Input Field
|
||||
=================================== */
|
||||
|
||||
.reordable-list-item__input {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18f971ae90e8e1c4bad0afcc31efe1d1
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
61
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss
Normal file
61
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKReordableListUss.uss
|
||||
* 재정렬 가능 리스트 컨테이너 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Base Container
|
||||
=================================== */
|
||||
|
||||
.reordable-list {
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
border-width: var(--border-width);
|
||||
border-color: var(--color-border);
|
||||
padding-top: var(--space-s);
|
||||
padding-bottom: var(--space-s);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
ListView 내부 여백 조정
|
||||
=================================== */
|
||||
|
||||
.reordable-list .utk-listview {
|
||||
flex-grow: 1;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 중 아이템 스타일 (Unity 내장 클래스)
|
||||
=================================== */
|
||||
|
||||
.reordable-list .unity-list-view__reorderable-item {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.reordable-list .unity-list-view__reorderable-item__container {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 핸들 바 (Unity 내장 reorder handle)
|
||||
커스텀 드래그 핸들 사용하므로 기본 숨김
|
||||
=================================== */
|
||||
|
||||
.reordable-list .unity-list-view__reorderable-handle {
|
||||
display: none;
|
||||
width: 0;
|
||||
min-width: 0;
|
||||
max-width: 0;
|
||||
}
|
||||
|
||||
.reordable-list .unity-list-view__reorderable-handle-bar {
|
||||
display: none;
|
||||
}
|
||||
11
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss.meta
Normal file
11
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebeda1896b5d02d489342ed0c4e45698
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
8
Assets/Resources/UIToolkit/Modal/Setting.meta
Normal file
8
Assets/Resources/UIToolkit/Modal/Setting.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62b1c6b38fe308748b808fb2b576e6e2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKSettingDisplayInfoTabViewUss.uss
|
||||
* 설정 표시 정보 탭 뷰 컨테이너 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Base Container
|
||||
=================================== */
|
||||
|
||||
.setting-display-tab-view {
|
||||
flex-grow: 1;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
border-width: var(--border-width);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
ListView 내부 여백 조정
|
||||
=================================== */
|
||||
|
||||
.setting-display-tab-view .utk-listview {
|
||||
flex-grow: 1;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 중 아이템 스타일 (Unity 내장 클래스)
|
||||
=================================== */
|
||||
|
||||
.setting-display-tab-view .unity-list-view__reorderable-item__container {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 핸들 바 (Unity 내장 reorder handle)
|
||||
커스텀 드래그 핸들 사용하므로 기본 숨김
|
||||
=================================== */
|
||||
|
||||
.setting-display-tab-view .unity-list-view__reorderable-handle-bar {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 360ccda715f15bc479dec409d0ed87cf
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
@@ -11,6 +11,11 @@
|
||||
<VisualElement name="property-list-window-container" class="utk-window-sample-container" style="width: 380px;" />
|
||||
</VisualElement>
|
||||
|
||||
<VisualElement class="utk-sample-section">
|
||||
<Label class="utk-sample-section__title" text="UTKPropertyTabListWindow" />
|
||||
<VisualElement name="property-tab-list-window-container" class="utk-window-sample-container" style="width: 380px;" />
|
||||
</VisualElement>
|
||||
|
||||
<!-- Code Sample -->
|
||||
<VisualElement class="utk-code-sample-container">
|
||||
<utk:UTKCodeBlock name="code-csharp" title="C#" />
|
||||
|
||||
@@ -159,6 +159,7 @@ ListView/TreeView 항목 텍스트 스타일
|
||||
.unity-list-view__item .unity-text-element {
|
||||
color: var(--color-text-primary) ;
|
||||
font-size: var(--font-size-body2) ;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.unity-collection-view__item--selected,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
.utk-tabview {
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
flex-grow: 0;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<ui:VisualElement name="button-root" class="utk-toolbar-btn">
|
||||
<ui:Label name="icon" class="utk-toolbar-btn__icon" />
|
||||
<ui:Label name="label" class="utk-toolbar-btn__label" />
|
||||
<utk:UTKLabel name="icon" class="utk-toolbar-btn__icon" />
|
||||
<utk:UTKLabel name="label" class="utk-toolbar-btn__label" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
font-size: var(--font-size-label4);
|
||||
color: var(--color-text-secondary);
|
||||
-unity-text-align: upper-center;
|
||||
margin-top: 1px;
|
||||
display: none;
|
||||
cursor: resource('UIToolkit/Images/cursor_point_white_32') 14 5;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<ui:VisualElement name="button-root" class="utk-toolbar-btn utk-toolbar-expandable">
|
||||
<ui:Label name="icon" class="utk-toolbar-btn__icon" />
|
||||
<ui:Label name="label" class="utk-toolbar-btn__label" />
|
||||
<utk:UTKLabel name="icon" class="utk-toolbar-btn__icon" />
|
||||
<utk:UTKLabel name="label" class="utk-toolbar-btn__label" />
|
||||
<ui:VisualElement name="arrow" class="utk-toolbar-expandable__arrow" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
||||
@@ -10,25 +10,31 @@
|
||||
border-color: var(--color-border);
|
||||
border-radius: var(--radius-m);
|
||||
padding: var(--space-xs);
|
||||
min-width: 120px;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.utk-toolbar-submenu__container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.utk-toolbar-submenu TemplateContainer {
|
||||
flex-grow: 1;
|
||||
align-items: stretch;
|
||||
}
|
||||
/* 서브 메뉴 내 버튼은 가로로 펼침 */
|
||||
.utk-toolbar-submenu .utk-toolbar-btn {
|
||||
flex-direction: row;
|
||||
min-width: 100px;
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
justify-content: flex-start;
|
||||
padding: var(--space-xs) var(--space-m);
|
||||
padding: var(--space-xs) 0;
|
||||
margin: 1px 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.utk-toolbar-submenu .utk-toolbar-btn__icon {
|
||||
margin-right: var(--space-s);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.utk-toolbar-submenu .utk-toolbar-btn__label {
|
||||
@@ -36,4 +42,7 @@
|
||||
font-size: var(--font-size-body2);
|
||||
color: var(--color-text-primary);
|
||||
-unity-text-align: middle-left;
|
||||
margin-right: var(--space-m);
|
||||
flex-grow: 100;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<ui:VisualElement name="button-root" class="utk-toolbar-btn utk-toolbar-toggle">
|
||||
<ui:Label name="icon" class="utk-toolbar-btn__icon" />
|
||||
<ui:Label name="label" class="utk-toolbar-btn__label" />
|
||||
<utk:UTKLabel name="icon" class="utk-toolbar-btn__icon" />
|
||||
<utk:UTKLabel name="label" class="utk-toolbar-btn__label" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit">
|
||||
<VisualElement name="window-root" class="utk-property-tab-window">
|
||||
<VisualElement name="header" class="utk-property-tab-window__header">
|
||||
<utk:UTKLabel name="title" class="utk-property-tab-window__title" />
|
||||
<utk:UTKButton name="close-btn" class="utk-property-tab-window__close-btn" variant="Text" icon-only="true" />
|
||||
</VisualElement>
|
||||
<utk:UTKTabView name="tab-view" class="utk-property-tab-window__tab-view" />
|
||||
<utk:UTKPropertyList name="content" class="utk-property-tab-window__content" />
|
||||
</VisualElement>
|
||||
</UXML>
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22934f67f61d09a419d467bdcc086cfb
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* UTKPropertyTabListWindowUss.uss
|
||||
*
|
||||
* UTKPropertyTabListWindow 컴포넌트의 스타일 정의입니다.
|
||||
* 테마 지원: var(--color-*) 변수 사용
|
||||
*
|
||||
* UTKPropertyListWindow 스타일을 기반으로 탭 영역이 추가되었습니다.
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
윈도우 루트 (Window Root)
|
||||
============================================ */
|
||||
|
||||
.utk-property-tab-window {
|
||||
background-color: var(--color-bg-panel);
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
min-width: 390px;
|
||||
width: 390px;
|
||||
padding: 10px 20px 25px 20px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
헤더 (Header)
|
||||
============================================ */
|
||||
|
||||
.utk-property-tab-window__header {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* UTKLabel 타이틀 스타일 */
|
||||
.utk-property-tab-window__title {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.utk-property-tab-window__title .utk-label__text {
|
||||
color: var(--color-text-primary);
|
||||
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;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
닫기 버튼 (Close Button)
|
||||
============================================ */
|
||||
|
||||
.utk-property-tab-window__close-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
min-width: 22px;
|
||||
min-height: 22px;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
align-self: center;
|
||||
display: none; /* 기본 숨김, 필요시 flex로 변경 */
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
탭 뷰 (Tab View)
|
||||
============================================ */
|
||||
|
||||
.utk-property-tab-window__tab-view {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 탭 콘텐츠 영역 숨기기 (실제 콘텐츠는 외부 UTKPropertyList에 표시) */
|
||||
.utk-property-tab-window__tab-view > .unity-tab-view__content-container {
|
||||
display: none;
|
||||
flex-grow: 0;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
콘텐츠 (Content - UTKPropertyList)
|
||||
============================================ */
|
||||
|
||||
.utk-property-tab-window__content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#unity-content-viewport {
|
||||
padding-right: 4px; /* 스크롤바 여유 공간 */
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 487dd3e0868e89645b0b5cfae0108370
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
@@ -19,9 +19,10 @@ namespace UVC.Sample.UIToolkit
|
||||
[Tooltip("시작 시 적용할 테마")]
|
||||
private UTKTheme initialTheme = UTKTheme.Dark;
|
||||
|
||||
private UTKToggle _themeToggle;
|
||||
private UTKToggle? _themeToggle;
|
||||
|
||||
private UTKPropertyListWindow _propertyWindow;
|
||||
private UTKPropertyListWindow? _propertyWindow;
|
||||
private UTKPropertyTabListWindow? _propertyTabWindow;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
@@ -34,43 +35,48 @@ namespace UVC.Sample.UIToolkit
|
||||
}
|
||||
_uiDocument = doc;
|
||||
|
||||
var toggle = _uiDocument.rootVisualElement.Q<UTKToggle>("toggle");
|
||||
if (toggle == null)
|
||||
{
|
||||
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
_themeToggle = toggle;
|
||||
|
||||
var window = _uiDocument.rootVisualElement.Q<UTKPropertyListWindow>("window");
|
||||
if (window == null)
|
||||
{
|
||||
Debug.LogError("UXML에서 UTKPropertyListWindow를 찾을 수 없습니다.");
|
||||
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);
|
||||
|
||||
// PropertyListWindow 샘플
|
||||
var window = root.Q<UTKPropertyListWindow>("window");
|
||||
if (window != null)
|
||||
{
|
||||
_propertyWindow = window;
|
||||
_propertyWindow.style.position = Position.Absolute;
|
||||
_propertyWindow.style.top = 50;
|
||||
_propertyWindow.style.left = 0;
|
||||
_propertyWindow.style.bottom = 0;
|
||||
_propertyWindow.style.width = 300;
|
||||
CreateSamplePropertyWindow();
|
||||
}
|
||||
|
||||
// PropertyTabListWindow 샘플
|
||||
var tabWindow = root.Q<UTKPropertyTabListWindow>("tabWindow");
|
||||
if (tabWindow != null)
|
||||
{
|
||||
_propertyTabWindow = tabWindow;
|
||||
_propertyTabWindow.style.position = Position.Absolute;
|
||||
_propertyTabWindow.style.top = 50;
|
||||
_propertyTabWindow.style.right = 0;
|
||||
_propertyTabWindow.style.bottom = 0;
|
||||
_propertyTabWindow.style.width = 300;
|
||||
CreateSamplePropertyTabWindow();
|
||||
}
|
||||
|
||||
UTKThemeManager.Instance.OnThemeChanged += theme =>
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(_uiDocument.rootVisualElement);
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateSamplePropertyWindow(VisualElement root)
|
||||
private void CreateSamplePropertyWindow()
|
||||
{
|
||||
if (_propertyWindow == null) return;
|
||||
|
||||
// 세로 높이를 부모에 맞게 꽉 채우기
|
||||
_propertyWindow.style.position = Position.Absolute;
|
||||
_propertyWindow.style.top = 0;
|
||||
_propertyWindow.style.bottom = 0;
|
||||
_propertyWindow.style.right = 0;
|
||||
_propertyWindow.style.width = 300;
|
||||
_propertyWindow.ShowCloseButton = true;
|
||||
_propertyWindow.OnCloseClicked += () =>
|
||||
{
|
||||
Debug.Log("Property Window Close clicked");
|
||||
@@ -105,8 +111,111 @@ namespace UVC.Sample.UIToolkit
|
||||
// 샘플 데이터 생성
|
||||
var entries = CreateSampleEntries();
|
||||
_propertyWindow.LoadMixedProperties(entries);
|
||||
}
|
||||
|
||||
root.Add(_propertyWindow);
|
||||
/// <summary>
|
||||
/// UTKPropertyTabListWindow 샘플 데이터 설정
|
||||
/// 탭별로 서로 다른 데이터 타입(Flat/Grouped/Mixed)을 보여줍니다.
|
||||
/// </summary>
|
||||
private void CreateSamplePropertyTabWindow()
|
||||
{
|
||||
if (_propertyTabWindow == null) return;
|
||||
|
||||
_propertyTabWindow.ShowCloseButton = true;
|
||||
_propertyTabWindow.OnCloseClicked += () =>
|
||||
{
|
||||
Debug.Log("Tab Property Window Close clicked");
|
||||
_propertyTabWindow?.Hide();
|
||||
};
|
||||
|
||||
_propertyTabWindow.OnTabChanged += (index, data) =>
|
||||
{
|
||||
Debug.Log($"Tab Changed: index={index}, name={data?.Name ?? "All"}");
|
||||
};
|
||||
|
||||
_propertyTabWindow.OnPropertyValueChanged += args =>
|
||||
{
|
||||
Debug.Log($"[Tab] Property Changed: {args.PropertyId} {args.PropertyName} ({args.PropertyType}) = {args.NewValue}");
|
||||
};
|
||||
|
||||
// === 탭 1: 기본 속성 (Grouped) ===
|
||||
var basicTab = new TabPropertyData("기본", UTKMaterialIcons.Settings);
|
||||
var basicGroups = new List<IUTKPropertyGroup>();
|
||||
|
||||
var infoGroup = new UTKPropertyGroup("tab_info", "기본 정보");
|
||||
infoGroup.AddItem(new UTKStringPropertyItem("tab_name", "이름", "오브젝트 A"));
|
||||
infoGroup.AddItem(new UTKBoolPropertyItem("tab_active", "활성화", true));
|
||||
infoGroup.AddItem(new UTKEnumPropertyItem("tab_layer", "레이어", SampleLayer.Default));
|
||||
infoGroup.AddItem(new UTKDropdownPropertyItem("tab_tag", "태그",
|
||||
new List<string> { "Untagged", "Player", "Enemy" }, "Player"));
|
||||
basicGroups.Add(infoGroup);
|
||||
|
||||
var transformGroup = new UTKPropertyGroup("tab_transform", "Transform");
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("tab_pos", "Position", new Vector3(0, 1, 0)));
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("tab_rot", "Rotation", Vector3.zero));
|
||||
transformGroup.AddItem(new UTKVector3PropertyItem("tab_scale", "Scale", Vector3.one));
|
||||
basicGroups.Add(transformGroup);
|
||||
|
||||
basicTab.SetGroupedData(basicGroups);
|
||||
|
||||
// === 탭 2: 외관 (Grouped) ===
|
||||
var appearanceTab = new TabPropertyData("외관", UTKMaterialIcons.Palette);
|
||||
var appearanceGroups = new List<IUTKPropertyGroup>();
|
||||
|
||||
var colorGroup = new UTKPropertyGroup("tab_colors", "색상");
|
||||
colorGroup.AddItem(new UTKColorPropertyItem("tab_main_color", "메인 색상", Color.blue));
|
||||
colorGroup.AddItem(new UTKColorPropertyItem("tab_emission", "발광 색상", Color.yellow, true));
|
||||
colorGroup.AddItem(new UTKFloatPropertyItem("tab_alpha", "투명도", 1f, 0f, 1f, useSlider: true));
|
||||
appearanceGroups.Add(colorGroup);
|
||||
|
||||
var materialGroup = new UTKPropertyGroup("tab_material", "머티리얼");
|
||||
materialGroup.AddItem(new UTKDropdownPropertyItem("tab_shader", "셰이더",
|
||||
new List<string> { "Standard", "Unlit", "URP/Lit", "URP/Simple Lit" }, "URP/Lit"));
|
||||
materialGroup.AddItem(new UTKFloatPropertyItem("tab_metallic", "메탈릭", 0.5f, 0f, 1f, useSlider: true));
|
||||
materialGroup.AddItem(new UTKFloatPropertyItem("tab_smoothness", "부드러움", 0.5f, 0f, 1f, useSlider: true));
|
||||
appearanceGroups.Add(materialGroup);
|
||||
|
||||
appearanceTab.SetGroupedData(appearanceGroups);
|
||||
|
||||
// === 탭 3: 고급 설정 (Flat) ===
|
||||
var advancedTab = new TabPropertyData("고급", UTKMaterialIcons.Tune);
|
||||
var advancedItems = new List<IUTKPropertyItem>
|
||||
{
|
||||
new UTKBoolPropertyItem("tab_debug", "디버그 모드", false),
|
||||
new UTKIntPropertyItem("tab_priority", "우선순위", 0, -10, 10, useStepper: true),
|
||||
new UTKFloatPropertyItem("tab_lod_bias", "LOD Bias", 1.0f, 0.1f, 5.0f, useSlider: true, useStepper: true),
|
||||
new UTKMultiSelectDropdownPropertyItem("tab_flags", "플래그",
|
||||
new List<string> { "Static", "Batching", "Navigation", "Occluder", "Occludee" },
|
||||
new List<string> { "Static", "Batching" }),
|
||||
new UTKRadioPropertyItem("tab_quality", "품질",
|
||||
new List<string> { "Low", "Medium", "High", "Ultra" }, 2),
|
||||
};
|
||||
advancedTab.SetFlatData(advancedItems);
|
||||
|
||||
// === 탭 4: 일정 (Mixed) ===
|
||||
var scheduleTab = new TabPropertyData("일정", UTKMaterialIcons.CalendarMonth);
|
||||
var scheduleEntries = new List<IUTKPropertyEntry>();
|
||||
|
||||
scheduleEntries.Add(new UTKDatePropertyItem("tab_created", "생성일", DateTime.Today.AddDays(-30)));
|
||||
scheduleEntries.Add(new UTKDateTimePropertyItem("tab_modified", "수정일", DateTime.Now));
|
||||
|
||||
var periodGroup = new UTKPropertyGroup("tab_period", "유효 기간");
|
||||
periodGroup.AddItem(new UTKDateRangePropertyItem("tab_valid", "유효 기간",
|
||||
DateTime.Today, DateTime.Today.AddMonths(1)));
|
||||
periodGroup.AddItem(new UTKDateTimeRangePropertyItem("tab_session", "세션 기간",
|
||||
DateTime.Now, DateTime.Now.AddHours(2)));
|
||||
scheduleEntries.Add(periodGroup);
|
||||
|
||||
scheduleTab.SetMixedData(scheduleEntries);
|
||||
|
||||
// 탭 데이터 설정
|
||||
_propertyTabWindow.SetTabData(new List<TabPropertyData>
|
||||
{
|
||||
basicTab,
|
||||
appearanceTab,
|
||||
advancedTab,
|
||||
scheduleTab
|
||||
});
|
||||
}
|
||||
|
||||
private List<IUTKPropertyEntry> CreateSampleEntries()
|
||||
@@ -621,6 +730,8 @@ namespace UVC.Sample.UIToolkit
|
||||
{
|
||||
_propertyWindow?.Dispose();
|
||||
_propertyWindow = null;
|
||||
_propertyTabWindow?.Dispose();
|
||||
_propertyTabWindow = null;
|
||||
}
|
||||
|
||||
// 샘플 열거형
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<VisualElement style="width: 100%; height: 100%;">
|
||||
<utk:UTKPropertyListWindow name="window" />
|
||||
<utk:UTKPropertyTabListWindow name="tabWindow" />
|
||||
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; left: 10px;" />
|
||||
</VisualElement>
|
||||
</UXML>
|
||||
|
||||
497
Assets/Sample/UIToolkit/UTKReordableList.unity
Normal file
497
Assets/Sample/UIToolkit/UTKReordableList.unity
Normal file
@@ -0,0 +1,497 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &1097328750
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1097328752}
|
||||
- component: {fileID: 1097328754}
|
||||
- component: {fileID: 1097328755}
|
||||
m_Layer: 0
|
||||
m_Name: Sample
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1097328752
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1097328754
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
|
||||
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
|
||||
m_ParentUI: {fileID: 0}
|
||||
sourceAsset: {fileID: 9197481963319205126, guid: 4bb0d1734d5c1b647ae0ffdb7879a92a, type: 3}
|
||||
m_SortingOrder: 0
|
||||
m_Position: 0
|
||||
m_WorldSpaceSizeMode: 1
|
||||
m_WorldSpaceWidth: 1920
|
||||
m_WorldSpaceHeight: 1080
|
||||
m_PivotReferenceSize: 0
|
||||
m_Pivot: 0
|
||||
m_WorldSpaceCollider: {fileID: 0}
|
||||
--- !u!114 &1097328755
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4274ed098fc4bf048bb92836e8982c8f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UTKReordableListSample
|
||||
uiDocument: {fileID: 1097328754}
|
||||
initialTheme: 0
|
||||
--- !u!1 &1331954412
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1331954415}
|
||||
- component: {fileID: 1331954414}
|
||||
- component: {fileID: 1331954413}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1331954413
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_MoveRepeatDelay: 0.5
|
||||
m_MoveRepeatRate: 0.1
|
||||
m_XRTrackingOrigin: {fileID: 0}
|
||||
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_DeselectOnBackgroundClick: 0
|
||||
m_PointerBehavior: 0
|
||||
m_CursorLockBehavior: 0
|
||||
m_ScrollDeltaPerTick: 6
|
||||
--- !u!114 &1331954414
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &1331954415
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1414861612
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1414861614}
|
||||
- component: {fileID: 1414861613}
|
||||
- component: {fileID: 1414861615}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1414861613
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 11
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1414861614
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!114 &1414861615
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!1 &2136621999
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2136622002}
|
||||
- component: {fileID: 2136622001}
|
||||
- component: {fileID: 2136622000}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &2136622000
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &2136622001
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &2136622002
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 2136622002}
|
||||
- {fileID: 1414861614}
|
||||
- {fileID: 1331954415}
|
||||
- {fileID: 1097328752}
|
||||
7
Assets/Sample/UIToolkit/UTKReordableList.unity.meta
Normal file
7
Assets/Sample/UIToolkit/UTKReordableList.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b1b2ce854a8a3d47ac1fc46dfaf615f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
152
Assets/Sample/UIToolkit/UTKReordableListSample.cs
Normal file
152
Assets/Sample/UIToolkit/UTKReordableListSample.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit;
|
||||
|
||||
/// <summary>
|
||||
/// UTKReordableList의 기능을 테스트하기 위한 샘플 MonoBehaviour입니다.
|
||||
/// Dictionary 기반 SetData/ToDictionary, 이벤트 핸들러, 데이터 CRUD를 확인합니다.
|
||||
/// </summary>
|
||||
public class UTKReordableListSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public UIDocument uiDocument;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("시작 시 적용할 테마")]
|
||||
private UTKTheme initialTheme = UTKTheme.Dark;
|
||||
|
||||
private UTKToggle _themeToggle;
|
||||
private UTKReordableList _reordableList;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// UIDocument 참조 확인
|
||||
var doc = GetComponent<UIDocument>();
|
||||
if (doc == null)
|
||||
{
|
||||
Debug.LogError("UIDocument가 할당되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
uiDocument = doc;
|
||||
|
||||
var root = uiDocument.rootVisualElement;
|
||||
|
||||
// 테마 토글
|
||||
_themeToggle = root.Q<UTKToggle>("toggle");
|
||||
if (_themeToggle == null)
|
||||
{
|
||||
Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ReordableList
|
||||
_reordableList = root.Q<UTKReordableList>("window");
|
||||
if (_reordableList == null)
|
||||
{
|
||||
Debug.LogError("UXML에서 UTKReordableList를 찾을 수 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 테마 초기화
|
||||
UTKThemeManager.Instance.RegisterRoot(root);
|
||||
UTKThemeManager.Instance.SetTheme(initialTheme);
|
||||
|
||||
_themeToggle.OnValueChanged += (isOn) =>
|
||||
{
|
||||
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
|
||||
};
|
||||
|
||||
// 이벤트 핸들러 등록
|
||||
_reordableList.OnOrderChanged += () => Debug.Log("[Sample] 순서 변경됨");
|
||||
_reordableList.OnDataChanged += () => Debug.Log("[Sample] 데이터 변경됨");
|
||||
|
||||
// 샘플 데이터 설정 (Dictionary 방식)
|
||||
SetSampleData();
|
||||
|
||||
// 하단 버튼 영역 생성
|
||||
CreateButtons(root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary 기반으로 샘플 데이터를 설정합니다.
|
||||
/// </summary>
|
||||
private void SetSampleData()
|
||||
{
|
||||
var listDict = new List<Dictionary<string, string>>
|
||||
{
|
||||
new() { ["order"] = "0", ["active"] = "True", ["text"] = "온도" },
|
||||
new() { ["order"] = "1", ["active"] = "False", ["text"] = "습도" },
|
||||
new() { ["order"] = "2", ["active"] = "True", ["text"] = "압력" },
|
||||
new() { ["order"] = "3", ["active"] = "True", ["text"] = "풍속" },
|
||||
new() { ["order"] = "4", ["active"] = "False", ["text"] = "조도" },
|
||||
};
|
||||
_reordableList.SetData(listDict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 테스트 버튼들을 생성합니다.
|
||||
/// </summary>
|
||||
private void CreateButtons(VisualElement root)
|
||||
{
|
||||
var buttonContainer = new VisualElement();
|
||||
buttonContainer.style.flexDirection = FlexDirection.Row;
|
||||
buttonContainer.style.justifyContent = Justify.Center;
|
||||
buttonContainer.style.paddingTop = 8;
|
||||
buttonContainer.style.paddingBottom = 8;
|
||||
|
||||
// ToDictionary 버튼
|
||||
var toDictBtn = new UTKButton("ToDictionary", variant: UTKButton.ButtonVariant.Primary);
|
||||
toDictBtn.OnClicked += OnToDictionaryClicked;
|
||||
toDictBtn.style.marginRight = 4;
|
||||
buttonContainer.Add(toDictBtn);
|
||||
|
||||
// 데이터 리셋 버튼
|
||||
var resetBtn = new UTKButton("리셋", variant: UTKButton.ButtonVariant.Normal);
|
||||
resetBtn.OnClicked += () => SetSampleData();
|
||||
resetBtn.style.marginRight = 4;
|
||||
buttonContainer.Add(resetBtn);
|
||||
|
||||
// 아이템 추가 버튼
|
||||
var addBtn = new UTKButton("추가", variant: UTKButton.ButtonVariant.OutlinePrimary);
|
||||
addBtn.OnClicked += OnAddItemClicked;
|
||||
buttonContainer.Add(addBtn);
|
||||
|
||||
root.Add(buttonContainer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ToDictionary를 호출하여 현재 데이터를 콘솔에 출력합니다.
|
||||
/// </summary>
|
||||
private void OnToDictionaryClicked()
|
||||
{
|
||||
var result = _reordableList.ToDictionary();
|
||||
Debug.Log($"[Sample] ToDictionary 결과 ({result.Count}건):");
|
||||
foreach (var dict in result)
|
||||
{
|
||||
Debug.Log($" order={dict["order"]}, active={dict["active"]}, text={dict["text"]}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 새 아이템을 추가합니다.
|
||||
/// </summary>
|
||||
private void OnAddItemClicked()
|
||||
{
|
||||
var currentData = _reordableList.ToDictionary();
|
||||
var newIndex = currentData.Count;
|
||||
currentData.Add(new Dictionary<string, string>
|
||||
{
|
||||
["order"] = newIndex.ToString(),
|
||||
["active"] = "True",
|
||||
["text"] = $"항목 {newIndex}"
|
||||
});
|
||||
_reordableList.SetData(currentData);
|
||||
Debug.Log($"[Sample] 아이템 추가됨 (총 {currentData.Count}건)");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_reordableList?.Dispose();
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/UIToolkit/UTKReordableListSample.cs.meta
Normal file
2
Assets/Sample/UIToolkit/UTKReordableListSample.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4274ed098fc4bf048bb92836e8982c8f
|
||||
6
Assets/Sample/UIToolkit/UTKReordableListUXML.uxml
Normal file
6
Assets/Sample/UIToolkit/UTKReordableListUXML.uxml
Normal file
@@ -0,0 +1,6 @@
|
||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<VisualElement style="width: 100%; height: 100%;">
|
||||
<utk:UTKReordableList name="window" style="width: 300px; height: 200px;" />
|
||||
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 10px; right: 10px;" />
|
||||
</VisualElement>
|
||||
</UXML>
|
||||
10
Assets/Sample/UIToolkit/UTKReordableListUXML.uxml.meta
Normal file
10
Assets/Sample/UIToolkit/UTKReordableListUXML.uxml.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bb0d1734d5c1b647ae0ffdb7879a92a
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
497
Assets/Sample/UIToolkit/UTKSettingModal.unity
Normal file
497
Assets/Sample/UIToolkit/UTKSettingModal.unity
Normal file
@@ -0,0 +1,497 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &1097328750
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1097328752}
|
||||
- component: {fileID: 1097328754}
|
||||
- component: {fileID: 1097328755}
|
||||
m_Layer: 0
|
||||
m_Name: Sample
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1097328752
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1097328754
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
|
||||
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
|
||||
m_ParentUI: {fileID: 0}
|
||||
sourceAsset: {fileID: 9197481963319205126, guid: 54e4f33c8b08cb54f97dbdb5edd79e1e, type: 3}
|
||||
m_SortingOrder: 1
|
||||
m_Position: 0
|
||||
m_WorldSpaceSizeMode: 1
|
||||
m_WorldSpaceWidth: 1920
|
||||
m_WorldSpaceHeight: 1080
|
||||
m_PivotReferenceSize: 0
|
||||
m_Pivot: 0
|
||||
m_WorldSpaceCollider: {fileID: 0}
|
||||
--- !u!114 &1097328755
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1097328750}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 39265a781c40bdb4a90aa56b0fbf44a6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::UVC.Sample.UIToolkit.UTKToolBarSample
|
||||
_uiDocument: {fileID: 1097328754}
|
||||
initialTheme: 0
|
||||
--- !u!1 &1331954412
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1331954415}
|
||||
- component: {fileID: 1331954414}
|
||||
- component: {fileID: 1331954413}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1331954413
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_MoveRepeatDelay: 0.5
|
||||
m_MoveRepeatRate: 0.1
|
||||
m_XRTrackingOrigin: {fileID: 0}
|
||||
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_DeselectOnBackgroundClick: 0
|
||||
m_PointerBehavior: 0
|
||||
m_CursorLockBehavior: 0
|
||||
m_ScrollDeltaPerTick: 6
|
||||
--- !u!114 &1331954414
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &1331954415
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1331954412}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1414861612
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1414861614}
|
||||
- component: {fileID: 1414861613}
|
||||
- component: {fileID: 1414861615}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1414861613
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 11
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1414861614
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!114 &1414861615
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1414861612}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!1 &2136621999
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2136622002}
|
||||
- component: {fileID: 2136622001}
|
||||
- component: {fileID: 2136622000}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &2136622000
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &2136622001
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &2136622002
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2136621999}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 2136622002}
|
||||
- {fileID: 1414861614}
|
||||
- {fileID: 1331954415}
|
||||
- {fileID: 1097328752}
|
||||
7
Assets/Sample/UIToolkit/UTKSettingModal.unity.meta
Normal file
7
Assets/Sample/UIToolkit/UTKSettingModal.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cce97e0d24c7794da00973b5ead99be
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
59
Assets/Sample/UIToolkit/UTKSettingModalSample.cs
Normal file
59
Assets/Sample/UIToolkit/UTKSettingModalSample.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.UIToolkit;
|
||||
using UVC.UI.Commands;
|
||||
using UVC.Log;
|
||||
|
||||
namespace UVC.Sample.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// UTKSettingModalSample 독립 실행 샘플 코드입니다.
|
||||
/// </summary>
|
||||
public class UTKSettingModalSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private UIDocument? _uiDocument;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("시작 시 적용할 테마")]
|
||||
private UTKTheme initialTheme = UTKTheme.Dark;
|
||||
|
||||
private UTKToggle? _themeToggle;
|
||||
private VisualElement? _root;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// UIDocument 참조 확인
|
||||
var doc = GetComponent<UIDocument>();
|
||||
if (doc == null)
|
||||
{
|
||||
Debug.LogError("UIDocument가 할당되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
_uiDocument = doc;
|
||||
_root = _uiDocument.rootVisualElement;
|
||||
|
||||
UTKThemeManager.Instance.RegisterRoot(_root);
|
||||
UTKThemeManager.Instance.SetTheme(initialTheme);
|
||||
|
||||
// 테마 토글
|
||||
_themeToggle = _root.Q<UTKToggle>("toggle");
|
||||
if (_themeToggle != null)
|
||||
{
|
||||
_themeToggle.OnValueChanged += (isOn) =>
|
||||
{
|
||||
UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
|
||||
|
||||
ULog.Debug("UTKSettingModalSample 정리 완료");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/UIToolkit/UTKSettingModalSample.cs.meta
Normal file
2
Assets/Sample/UIToolkit/UTKSettingModalSample.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e88ad13f58976fb4a837242a7e1c8282
|
||||
6
Assets/Sample/UIToolkit/UTKSettingModalUXML.uxml
Normal file
6
Assets/Sample/UIToolkit/UTKSettingModalUXML.uxml
Normal file
@@ -0,0 +1,6 @@
|
||||
<UXML xmlns="UnityEngine.UIElements" xmlns:utk="UVC.UIToolkit" editor-extension-mode="False">
|
||||
<VisualElement style="width: 100%; height: 100%; flex-direction: column;">
|
||||
<VisualElement name="toolbar-area" style="flex-grow: 1; flex-direction: column; padding: 8px;" />
|
||||
<utk:UTKToggle name="toggle" label="테마 변경" style="position: absolute; top: 8px; right: 10px; z-index: 10;" />
|
||||
</VisualElement>
|
||||
</UXML>
|
||||
10
Assets/Sample/UIToolkit/UTKSettingModalUXML.uxml.meta
Normal file
10
Assets/Sample/UIToolkit/UTKSettingModalUXML.uxml.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c8eae7ee21b96245b325f08111b214b
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
@@ -371,14 +371,11 @@ public async UniTask SaveDataAsync()
|
||||
}
|
||||
|
||||
SetCodeSamples(root,
|
||||
csharpCode: @"// 1. 초기화 (앱 시작 시 한 번)
|
||||
UTKTooltipManager.Instance.Initialize(rootVisualElement);
|
||||
|
||||
// 2. 버튼에 툴팁 연결
|
||||
csharpCode: @"// 1. 버튼에 툴팁 연결
|
||||
var saveButton = new UTKButton("""", UTKMaterialIcons.Save);
|
||||
UTKTooltipManager.Instance.AttachTooltip(saveButton, ""저장 (Ctrl+S)"");
|
||||
|
||||
// 3. 다국어 키로 툴팁 연결
|
||||
// 2. 다국어 키로 툴팁 연결
|
||||
UTKTooltipManager.Instance.AttachTooltip(settingsButton, ""tooltip_settings"");
|
||||
|
||||
// 4. 아이콘 버튼에 툴팁
|
||||
|
||||
@@ -205,7 +205,6 @@ public partial class UTKStyleGuideSample : MonoBehaviour
|
||||
}
|
||||
|
||||
UTKThemeManager.Instance.SetTheme(initialTheme);
|
||||
UTKTooltipManager.Instance.Initialize(_root);
|
||||
|
||||
CreateUI();
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace UVC.Sample.UIToolkit
|
||||
/// </summary>
|
||||
private void CreateHorizontalToolBar(VisualElement parent)
|
||||
{
|
||||
var label = new Label("Horizontal Toolbar");
|
||||
var label = new Label("Horizontal Toolbar");
|
||||
label.style.fontSize = 14;
|
||||
label.style.marginTop = 8;
|
||||
label.style.marginBottom = 4;
|
||||
@@ -93,30 +93,30 @@ namespace UVC.Sample.UIToolkit
|
||||
_horizontalModel = new UTKToolBarModel();
|
||||
|
||||
// Standard
|
||||
_horizontalModel.AddStandardButton("저장", UTKMaterialIcons.Save, new DebugLogCommand("저장"), "파일 저장");
|
||||
_horizontalModel.AddStandardButton("실행 취소", UTKMaterialIcons.Undo, new DebugLogCommand("실행 취소"));
|
||||
_horizontalModel.AddStandardButton("다시 실행", UTKMaterialIcons.Redo, new DebugLogCommand("다시 실행"));
|
||||
_horizontalModel.AddStandardButton("", UTKMaterialIcons.Save, new DebugLogCommand("저장"), "파일 저장");
|
||||
_horizontalModel.AddStandardButton("", UTKMaterialIcons.Undo, new DebugLogCommand("실행 취소"));
|
||||
_horizontalModel.AddStandardButton("", UTKMaterialIcons.Redo, new DebugLogCommand("다시 실행"));
|
||||
|
||||
_horizontalModel.AddSeparator();
|
||||
|
||||
// Toggle
|
||||
_horizontalModel.AddToggleButton("그리드", false, UTKMaterialIcons.GridOn, UTKMaterialIcons.GridOff, tooltip: "그리드 표시/숨김");
|
||||
_horizontalModel.AddToggleButton("스냅", false, UTKMaterialIcons.FilterCenterFocus, UTKMaterialIcons.CenterFocusWeak, tooltip: "스냅 활성화");
|
||||
_horizontalModel.AddToggleButton("", false, UTKMaterialIcons.GridOn, UTKMaterialIcons.GridOff, tooltip: "그리드 표시/숨김");
|
||||
_horizontalModel.AddToggleButton("", false, UTKMaterialIcons.FilterCenterFocus, UTKMaterialIcons.CenterFocusWeak, tooltip: "스냅 활성화");
|
||||
|
||||
_horizontalModel.AddSeparator();
|
||||
|
||||
// Radio
|
||||
_horizontalModel.AddRadioButton("tool", "선택", true, UTKMaterialIcons.NearMe, tooltip: "선택 도구");
|
||||
_horizontalModel.AddRadioButton("tool", "이동", false, UTKMaterialIcons.OpenWith, tooltip: "이동 도구");
|
||||
_horizontalModel.AddRadioButton("tool", "회전", false, UTKMaterialIcons.Refresh, tooltip: "회전 도구");
|
||||
_horizontalModel.AddRadioButton("tool", "", true, UTKMaterialIcons.NearMe, tooltip: "선택 도구");
|
||||
_horizontalModel.AddRadioButton("tool", "", false, UTKMaterialIcons.OpenWith, tooltip: "이동 도구");
|
||||
_horizontalModel.AddRadioButton("tool", "", false, UTKMaterialIcons.Refresh, tooltip: "회전 도구");
|
||||
|
||||
_horizontalModel.AddSeparator();
|
||||
|
||||
// Expandable
|
||||
var shapeBtn = _horizontalModel.AddExpandableButton("도형", UTKMaterialIcons.Category, tooltip: "도형 추가", updateIconOnSelection: true);
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "사각형", IconPath = UTKMaterialIcons.CropSquare, UseMaterialIcon = true });
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "원형", IconPath = UTKMaterialIcons.Circle, UseMaterialIcon = true });
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "삼각형", IconPath = UTKMaterialIcons.ChangeHistory, UseMaterialIcon = true });
|
||||
var shapeBtn = _horizontalModel.AddExpandableButton("", UTKMaterialIcons.Category, tooltip: "도형 추가", updateIconOnSelection: true);
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "", IconPath = UTKMaterialIcons.CropSquare, UseMaterialIcon = true, Tooltip = "사각형 추가" });
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "", IconPath = UTKMaterialIcons.Circle, UseMaterialIcon = true, Tooltip = "원형 추가" });
|
||||
shapeBtn.SubButtons.Add(new UTKToolBarStandardButtonData { Text = "", IconPath = UTKMaterialIcons.ChangeHistory, UseMaterialIcon = true, Tooltip = "삼각형 추가" });
|
||||
|
||||
_horizontalToolBar = new UTKToolBar();
|
||||
_horizontalToolBar.Orientation = UTKToolBarOrientation.Horizontal;
|
||||
|
||||
@@ -10,17 +10,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// VisualElement에 툴팁을 설정합니다.
|
||||
/// UTKTooltipManager가 초기화되어 있어야 합니다.
|
||||
/// </summary>
|
||||
/// <param name="element">대상 요소</param>
|
||||
/// <param name="tooltip">툴팁 텍스트 또는 다국어 키</param>
|
||||
/// <returns>체이닝을 위한 원본 요소</returns>
|
||||
public static T SetTooltip<T>(this T element, string tooltip) where T : VisualElement
|
||||
{
|
||||
if (UTKTooltipManager.Instance.IsInitialized)
|
||||
{
|
||||
UTKTooltipManager.Instance.AttachTooltip(element, tooltip);
|
||||
}
|
||||
UTKTooltipManager.Instance.AttachTooltip(element, tooltip);
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -31,10 +27,7 @@ namespace UVC.UIToolkit
|
||||
/// <returns>체이닝을 위한 원본 요소</returns>
|
||||
public static T ClearTooltip<T>(this T element) where T : VisualElement
|
||||
{
|
||||
if (UTKTooltipManager.Instance.IsInitialized)
|
||||
{
|
||||
UTKTooltipManager.Instance.DetachTooltip(element);
|
||||
}
|
||||
UTKTooltipManager.Instance.DetachTooltip(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
@@ -46,10 +39,7 @@ namespace UVC.UIToolkit
|
||||
/// <returns>체이닝을 위한 원본 요소</returns>
|
||||
public static T UpdateTooltip<T>(this T element, string tooltip) where T : VisualElement
|
||||
{
|
||||
if (UTKTooltipManager.Instance.IsInitialized)
|
||||
{
|
||||
UTKTooltipManager.Instance.UpdateTooltip(element, tooltip);
|
||||
}
|
||||
UTKTooltipManager.Instance.UpdateTooltip(element, tooltip);
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace UVC.UIToolkit
|
||||
/// <summary>
|
||||
/// UIToolkit 기반 툴팁 매니저.
|
||||
/// VisualElement에 마우스 오버 시 툴팁을 표시하는 싱글톤 관리자입니다.
|
||||
/// panel.visualTree를 사용하여 모든 UI 위에 툴팁을 표시합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>Tooltip(툴팁)이란?</b></para>
|
||||
@@ -25,7 +26,7 @@ namespace UVC.UIToolkit
|
||||
/// <para>
|
||||
/// UTKTooltipManager는 싱글톤으로 구현되어 있습니다.
|
||||
/// <c>UTKTooltipManager.Instance</c>로 접근하며, 앱 전체에서 하나의 툴팁 UI를 공유합니다.
|
||||
/// 사용 전에 반드시 <c>Initialize(root)</c>를 호출해야 합니다.
|
||||
/// panel.visualTree를 사용하므로 별도 Initialize 호출이 필요 없습니다.
|
||||
/// </para>
|
||||
///
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
@@ -38,7 +39,6 @@ namespace UVC.UIToolkit
|
||||
///
|
||||
/// <para><b>주요 메서드:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description><c>Initialize(root)</c> - 초기화 (루트 요소 지정)</description></item>
|
||||
/// <item><description><c>AttachTooltip(element, text)</c> - 요소에 툴팁 연결</description></item>
|
||||
/// <item><description><c>DetachTooltip(element)</c> - 툴팁 제거</description></item>
|
||||
/// <item><description><c>Show(text, position)</c> - 즉시 표시</description></item>
|
||||
@@ -56,20 +56,17 @@ namespace UVC.UIToolkit
|
||||
/// <example>
|
||||
/// <para><b>C# 코드에서 사용:</b></para>
|
||||
/// <code>
|
||||
/// // 1. 초기화 (앱 시작 시 한 번)
|
||||
/// UTKTooltipManager.Instance.Initialize(rootVisualElement);
|
||||
///
|
||||
/// // 2. 버튼에 툴팁 연결
|
||||
/// // 1. 버튼에 툴팁 연결 (Initialize 불필요)
|
||||
/// var saveButton = new UTKButton("", UTKMaterialIcons.Save);
|
||||
/// UTKTooltipManager.Instance.AttachTooltip(saveButton, "저장 (Ctrl+S)");
|
||||
///
|
||||
/// // 3. 다국어 키로 툴팁 연결
|
||||
/// // 2. 다국어 키로 툴팁 연결
|
||||
/// UTKTooltipManager.Instance.AttachTooltip(settingsButton, "tooltip_settings");
|
||||
///
|
||||
/// // 4. 툴팁 업데이트
|
||||
/// // 3. 툴팁 업데이트
|
||||
/// UTKTooltipManager.Instance.UpdateTooltip(button, "새로운 설명");
|
||||
///
|
||||
/// // 5. 툴팁 제거
|
||||
/// // 4. 툴팁 제거
|
||||
/// UTKTooltipManager.Instance.DetachTooltip(button);
|
||||
/// </code>
|
||||
/// </example>
|
||||
@@ -90,12 +87,11 @@ namespace UVC.UIToolkit
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private VisualElement? _root;
|
||||
private VisualElement? _tooltipContainer;
|
||||
private Label? _tooltipLabel;
|
||||
private bool _isInitialized;
|
||||
private bool _isVisible;
|
||||
private bool _disposed;
|
||||
private StyleSheet? _loadedUss;
|
||||
|
||||
private CancellationTokenSource? _showDelayCts;
|
||||
private readonly Dictionary<VisualElement, string> _tooltipRegistry = new();
|
||||
@@ -105,24 +101,19 @@ namespace UVC.UIToolkit
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public bool IsInitialized => _isInitialized;
|
||||
public bool IsVisible => _isVisible;
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
/// <summary>
|
||||
/// 툴팁 매니저를 초기화합니다.
|
||||
/// 툴팁 UI를 생성합니다 (아직 visual tree에 추가하지 않음).
|
||||
/// </summary>
|
||||
/// <param name="root">VisualElement 트리의 루트</param>
|
||||
public void Initialize(VisualElement root)
|
||||
private void EnsureTooltipUI()
|
||||
{
|
||||
if (_isInitialized)
|
||||
{
|
||||
Debug.LogWarning("[UTKTooltipManager] Already initialized.");
|
||||
return;
|
||||
}
|
||||
if (_tooltipContainer != null) return;
|
||||
|
||||
_root = root;
|
||||
// USS 로드
|
||||
_loadedUss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
|
||||
// UXML 로드 시도
|
||||
var visualTree = Resources.Load<VisualTreeAsset>(UXML_PATH);
|
||||
@@ -143,13 +134,11 @@ namespace UVC.UIToolkit
|
||||
_tooltipContainer.style.position = Position.Absolute;
|
||||
_tooltipContainer.style.display = DisplayStyle.None;
|
||||
_tooltipContainer.pickingMode = PickingMode.Ignore;
|
||||
_root.Add(_tooltipContainer);
|
||||
}
|
||||
|
||||
// 테마 변경 이벤트 구독
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,16 +163,6 @@ namespace UVC.UIToolkit
|
||||
pickingMode = PickingMode.Ignore
|
||||
};
|
||||
|
||||
// 테마 적용
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(_tooltipContainer);
|
||||
|
||||
// USS 스타일시트 로드
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
_tooltipContainer.styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
// USS 클래스로 스타일 적용
|
||||
_tooltipContainer.AddToClassList("utk-tooltip-container");
|
||||
|
||||
@@ -196,6 +175,33 @@ namespace UVC.UIToolkit
|
||||
|
||||
_tooltipContainer.Add(_tooltipLabel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 툴팁 컨테이너를 대상 요소의 panel.visualTree에 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="element">대상 요소 (panel 접근용)</param>
|
||||
private void AttachToPanel(VisualElement element)
|
||||
{
|
||||
if (_tooltipContainer == null || element.panel == null) return;
|
||||
|
||||
var visualTree = element.panel.visualTree;
|
||||
|
||||
// 이미 해당 visualTree에 추가되어 있으면 스킵
|
||||
if (_tooltipContainer.parent == visualTree) return;
|
||||
|
||||
// 다른 곳에 붙어 있으면 제거
|
||||
_tooltipContainer.RemoveFromHierarchy();
|
||||
|
||||
// panel.visualTree에 추가
|
||||
visualTree.Add(_tooltipContainer);
|
||||
|
||||
// 테마/USS 재적용
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(_tooltipContainer);
|
||||
if (_loadedUss != null)
|
||||
{
|
||||
_tooltipContainer.styleSheets.Add(_loadedUss);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
@@ -203,10 +209,10 @@ namespace UVC.UIToolkit
|
||||
/// 툴팁을 즉시 표시합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">표시할 텍스트</param>
|
||||
/// <param name="position">화면 좌표</param>
|
||||
/// <param name="position">월드 좌표</param>
|
||||
public void Show(string text, Vector2 position)
|
||||
{
|
||||
if (!_isInitialized || _tooltipContainer == null || _tooltipLabel == null)
|
||||
if (_tooltipContainer == null || _tooltipLabel == null)
|
||||
return;
|
||||
|
||||
// 다국어 처리
|
||||
@@ -230,7 +236,7 @@ namespace UVC.UIToolkit
|
||||
/// 지연 후 툴팁을 표시합니다.
|
||||
/// </summary>
|
||||
/// <param name="text">표시할 텍스트</param>
|
||||
/// <param name="position">화면 좌표</param>
|
||||
/// <param name="position">월드 좌표</param>
|
||||
/// <param name="delayMs">지연 시간 (밀리초)</param>
|
||||
public async UniTaskVoid ShowDelayed(string text, Vector2 position, int delayMs = SHOW_DELAY_MS)
|
||||
{
|
||||
@@ -275,21 +281,22 @@ namespace UVC.UIToolkit
|
||||
// 기존 등록 제거
|
||||
DetachTooltip(element);
|
||||
|
||||
// 툴팁 UI 생성 보장
|
||||
EnsureTooltipUI();
|
||||
|
||||
_tooltipRegistry[element] = tooltip;
|
||||
|
||||
// 이벤트 콜백 생성 및 등록
|
||||
// 참고: evt.position은 로컬 좌표이므로, 패널 기준 좌표로 변환 필요
|
||||
EventCallback<PointerEnterEvent> enterCallback = evt =>
|
||||
{
|
||||
if (_tooltipRegistry.TryGetValue(element, out var text))
|
||||
{
|
||||
// 로컬 좌표를 root 좌표로 변환
|
||||
var rootPosition = element.LocalToWorld(evt.localPosition);
|
||||
if (_root != null)
|
||||
{
|
||||
rootPosition = _root.WorldToLocal(rootPosition);
|
||||
}
|
||||
ShowDelayed(text, rootPosition).Forget();
|
||||
// panel.visualTree에 툴팁 컨테이너 추가
|
||||
AttachToPanel(element);
|
||||
|
||||
// worldBound 기준 좌표 사용
|
||||
var worldPos = element.LocalToWorld(evt.localPosition);
|
||||
ShowDelayed(text, worldPos).Forget();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -299,13 +306,9 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_isVisible)
|
||||
{
|
||||
// 로컬 좌표를 root 좌표로 변환
|
||||
var rootPosition = element.LocalToWorld(evt.localPosition);
|
||||
if (_root != null)
|
||||
{
|
||||
rootPosition = _root.WorldToLocal(rootPosition);
|
||||
}
|
||||
AdjustPosition(rootPosition);
|
||||
// worldBound 기준 좌표 사용
|
||||
var worldPos = element.LocalToWorld(evt.localPosition);
|
||||
AdjustPosition(worldPos);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -406,37 +409,39 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 화면 경계 내에서 위치 조정
|
||||
/// 화면 경계 내에서 위치 조정 (월드 좌표 기준)
|
||||
/// </summary>
|
||||
private void AdjustPosition(Vector2 mousePosition)
|
||||
private void AdjustPosition(Vector2 worldPosition)
|
||||
{
|
||||
if (_tooltipContainer == null || _root == null)
|
||||
if (_tooltipContainer == null || _tooltipContainer.panel == null)
|
||||
return;
|
||||
|
||||
var panelRoot = _tooltipContainer.panel.visualTree;
|
||||
|
||||
var tooltipSize = new Vector2(
|
||||
_tooltipContainer.resolvedStyle.width,
|
||||
_tooltipContainer.resolvedStyle.height
|
||||
);
|
||||
|
||||
var rootSize = new Vector2(
|
||||
_root.resolvedStyle.width,
|
||||
_root.resolvedStyle.height
|
||||
var panelSize = new Vector2(
|
||||
panelRoot.resolvedStyle.width,
|
||||
panelRoot.resolvedStyle.height
|
||||
);
|
||||
|
||||
// 기본 위치: 마우스 오른쪽 아래
|
||||
float x = mousePosition.x + POSITION_OFFSET;
|
||||
float y = mousePosition.y + POSITION_OFFSET;
|
||||
float x = worldPosition.x + POSITION_OFFSET;
|
||||
float y = worldPosition.y + POSITION_OFFSET;
|
||||
|
||||
// 오른쪽 경계 체크
|
||||
if (x + tooltipSize.x > rootSize.x)
|
||||
if (x + tooltipSize.x > panelSize.x)
|
||||
{
|
||||
x = mousePosition.x - tooltipSize.x - POSITION_OFFSET;
|
||||
x = worldPosition.x - tooltipSize.x - POSITION_OFFSET;
|
||||
}
|
||||
|
||||
// 아래쪽 경계 체크
|
||||
if (y + tooltipSize.y > rootSize.y)
|
||||
if (y + tooltipSize.y > panelSize.y)
|
||||
{
|
||||
y = mousePosition.y - tooltipSize.y - POSITION_OFFSET;
|
||||
y = worldPosition.y - tooltipSize.y - POSITION_OFFSET;
|
||||
}
|
||||
|
||||
// 왼쪽/위쪽 경계 체크
|
||||
@@ -476,9 +481,7 @@ namespace UVC.UIToolkit
|
||||
_tooltipContainer?.RemoveFromHierarchy();
|
||||
_tooltipContainer = null;
|
||||
_tooltipLabel = null;
|
||||
_root = null;
|
||||
|
||||
_isInitialized = false;
|
||||
_isVisible = false;
|
||||
_instance = null;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using UVC.Extention;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
@@ -233,6 +234,7 @@ namespace UVC.UIToolkit
|
||||
if (_label != null)
|
||||
{
|
||||
_label.selection.isSelectable = value;
|
||||
_label.pickingMode = value ? PickingMode.Position : PickingMode.Ignore;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,7 +458,7 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
AddToClassList("utk-label");
|
||||
|
||||
_label = new Label { name = "label" };
|
||||
_label = new Label { name = "label", pickingMode = PickingMode.Ignore };
|
||||
_label.AddToClassList("utk-label__text");
|
||||
Add(_label);
|
||||
|
||||
@@ -621,14 +623,14 @@ namespace UVC.UIToolkit
|
||||
// 아이콘과 텍스트 사이의 간격을 적용
|
||||
if (_iconLabel != null)
|
||||
{
|
||||
_iconLabel.style.marginRight = _iconPosition == IconPosition.Left ? _gap : 0;
|
||||
_iconLabel.style.marginLeft = _iconPosition == IconPosition.Right ? _gap : 0;
|
||||
_iconLabel.style.marginRight = _iconPosition == IconPosition.Left ? (_text.IsNullOrEmpty() ? 0 : _gap) : 0;
|
||||
_iconLabel.style.marginLeft = _iconPosition == IconPosition.Right ? (_text.IsNullOrEmpty() ? 0 : _gap) : 0;
|
||||
}
|
||||
|
||||
if (_imageIcon != null)
|
||||
{
|
||||
_imageIcon.style.marginRight = _iconPosition == IconPosition.Left ? _gap : 0;
|
||||
_imageIcon.style.marginLeft = _iconPosition == IconPosition.Right ? _gap : 0;
|
||||
_imageIcon.style.marginRight = _iconPosition == IconPosition.Left ? (_text.IsNullOrEmpty() ? 0 : _gap) : 0;
|
||||
_imageIcon.style.marginLeft = _iconPosition == IconPosition.Right ? (_text.IsNullOrEmpty() ? 0 : _gap) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,7 +671,7 @@ namespace UVC.UIToolkit
|
||||
_iconLabel.style.display = DisplayStyle.Flex;
|
||||
UTKMaterialIcons.ApplyIconStyle(_iconLabel, fontSize ?? GetEffectiveIconSize());
|
||||
}
|
||||
|
||||
if(_text.IsNullOrEmpty()) TextAlignment = TextAlign.Center; // 텍스트가 없는 경우 아이콘 중앙 정렬
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
@@ -693,6 +695,7 @@ namespace UVC.UIToolkit
|
||||
await UTKMaterialIcons.ApplyIconStyleAsync(_iconLabel, ct, fontSize ?? GetEffectiveIconSize());
|
||||
}
|
||||
|
||||
if(_text.IsNullOrEmpty()) TextAlignment = TextAlign.Center; // 텍스트가 없는 경우 아이콘 중앙 정렬
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
@@ -816,6 +819,7 @@ namespace UVC.UIToolkit
|
||||
_imageIcon.style.backgroundImage = new StyleBackground(texture);
|
||||
_imageIcon.style.display = DisplayStyle.Flex;
|
||||
|
||||
if(_text.IsNullOrEmpty()) TextAlignment = TextAlign.Center; // 텍스트가 없는 경우 아이콘 중앙 정렬
|
||||
EnableInClassList("utk-label--has-icon", true);
|
||||
UpdateIconPosition();
|
||||
UpdateGap();
|
||||
|
||||
@@ -72,6 +72,18 @@ namespace UVC.UIToolkit
|
||||
public event Action<string, string>? OnPropertyButtonClicked;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// 현재 검색어를 가져오거나 설정합니다.
|
||||
/// 설정 시 검색 필드의 값만 변경하고 검색은 실행하지 않습니다.
|
||||
/// </summary>
|
||||
public string SearchQuery
|
||||
{
|
||||
get => _searchField?.value ?? string.Empty;
|
||||
set { if (_searchField != null) _searchField.value = value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKPropertyList()
|
||||
{
|
||||
@@ -874,7 +886,8 @@ namespace UVC.UIToolkit
|
||||
|
||||
title = new UTKLabel();
|
||||
title.AddToClassList("utk-property-group__title");
|
||||
|
||||
title.Size = UTKLabel.LabelSize.Label2;
|
||||
title.IsBold = true;
|
||||
// count = new UTKLabel();
|
||||
// count.AddToClassList("utk-property-group__count");
|
||||
|
||||
@@ -887,8 +900,7 @@ namespace UVC.UIToolkit
|
||||
// 데이터 바인딩
|
||||
expandIcon.SetMaterialIcon(group.IsExpanded ? UTKMaterialIcons.ExpandMore : UTKMaterialIcons.ChevronRight, 16);
|
||||
title.Text = group.GroupName;
|
||||
title.Size = UTKLabel.LabelSize.Label1;
|
||||
title.IsBold = true;
|
||||
|
||||
// count.Text = $"({group.ItemCount})";
|
||||
// count.Variant = UTKLabel.LabelVariant.Secondary;
|
||||
|
||||
@@ -985,6 +997,28 @@ namespace UVC.UIToolkit
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 검색 필드의 값으로 검색을 실행합니다.
|
||||
/// </summary>
|
||||
public void ApplySearch()
|
||||
{
|
||||
OnSearch(_searchField?.value ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 검색어로 검색을 실행합니다.
|
||||
/// 검색 필드의 값도 함께 업데이트됩니다.
|
||||
/// </summary>
|
||||
/// <param name="query">검색어</param>
|
||||
public void ApplySearch(string query)
|
||||
{
|
||||
if (_searchField != null)
|
||||
{
|
||||
_searchField.value = query;
|
||||
}
|
||||
OnSearch(query);
|
||||
}
|
||||
|
||||
private void OnSearch(string newValue)
|
||||
{
|
||||
_searchText = newValue ?? string.Empty;
|
||||
|
||||
407
Assets/Scripts/UVC/UIToolkit/List/UTKReordableList.cs
Normal file
407
Assets/Scripts/UVC/UIToolkit/List/UTKReordableList.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 설정 표시 정보 아이템 데이터.
|
||||
/// </summary>
|
||||
public class ReordableListItemData
|
||||
{
|
||||
/// <summary>표시 순서 (ListView 인덱스 기준)</summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>사용 유무</summary>
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
/// <summary>표시 내용</summary>
|
||||
public string DisplayText { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 설정 표시 정보 탭 뷰.
|
||||
/// UTKListView를 활용하여 마우스 드래그로 항목 순서를 변경할 수 있는 설정 목록 뷰입니다.
|
||||
/// 각 항목은 드래그 핸들, 체크박스(사용 유무), 입력 필드(내용 수정)로 구성됩니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class UTKReordableList : VisualElement, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/List/UTKReordableListUss";
|
||||
private const string ITEM_UXML_PATH = "UIToolkit/List/UTKReordableListItem";
|
||||
private const string ITEM_USS_PATH = "UIToolkit/List/UTKReordableListItemUss";
|
||||
private const float ITEM_HEIGHT = 36f;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private UTKListView? _listView;
|
||||
private List<ReordableListItemData> _items = new();
|
||||
private VisualTreeAsset? _itemTemplate;
|
||||
private StyleSheet? _itemStyleSheet;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>순서 변경 시 발생</summary>
|
||||
public event Action? OnOrderChanged;
|
||||
|
||||
/// <summary>데이터(체크/텍스트) 변경 시 발생</summary>
|
||||
public event Action? OnDataChanged;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKReordableList() : base()
|
||||
{
|
||||
// 1. 테마 적용
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
|
||||
// 2. USS 로드
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
// 3. UI 생성
|
||||
CreateUI();
|
||||
|
||||
// 4. 테마 변경 구독
|
||||
SubscribeToThemeChanges();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
private void CreateUI()
|
||||
{
|
||||
AddToClassList("reordable-list");
|
||||
SetupListView();
|
||||
}
|
||||
|
||||
private void SetupListView()
|
||||
{
|
||||
_listView = new UTKListView();
|
||||
_listView.makeItem = MakeItem;
|
||||
_listView.bindItem = BindItem;
|
||||
_listView.unbindItem = UnbindItem;
|
||||
_listView.fixedItemHeight = ITEM_HEIGHT;
|
||||
_listView.selectionType = SelectionType.Single;
|
||||
_listView.reorderable = true;
|
||||
_listView.reorderMode = ListViewReorderMode.Animated;
|
||||
_listView.selectionType = SelectionType.None; // 선택 비활성화 (체크박스 사용)
|
||||
_listView.itemIndexChanged += OnItemIndexChanged;
|
||||
|
||||
// Unity 내장 reorderable-handle 숨김 (CSS 선택자가 매칭되지 않는 경우 대비)
|
||||
_listView.RegisterCallback<AttachToPanelEvent>(_ =>
|
||||
{
|
||||
_listView.schedule.Execute(() => HideBuiltInHandles()).ExecuteLater(50);
|
||||
});
|
||||
|
||||
Add(_listView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity ListView 내장 드래그 핸들을 숨깁니다.
|
||||
/// 커스텀 드래그 핸들(DragIndicator 아이콘)을 사용하므로 내장 핸들은 불필요합니다.
|
||||
/// </summary>
|
||||
private void HideBuiltInHandles()
|
||||
{
|
||||
if (_listView == null) return;
|
||||
_listView.Query(className: "unity-list-view__reorderable-handle").ForEach(el =>
|
||||
{
|
||||
el.style.display = DisplayStyle.None;
|
||||
el.style.width = 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
}
|
||||
|
||||
private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
|
||||
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
|
||||
private void OnThemeChanged(UTKTheme theme)
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ListView Callbacks
|
||||
private VisualElement MakeItem()
|
||||
{
|
||||
// UXML 캐싱
|
||||
_itemTemplate ??= Resources.Load<VisualTreeAsset>(ITEM_UXML_PATH);
|
||||
// USS 캐싱 (코드에서 로드 - UXML에서 지정하지 않음)
|
||||
_itemStyleSheet ??= Resources.Load<StyleSheet>(ITEM_USS_PATH);
|
||||
|
||||
if (_itemTemplate != null)
|
||||
{
|
||||
var root = _itemTemplate.Instantiate();
|
||||
|
||||
// USS 적용 (코드에서 적용)
|
||||
if (_itemStyleSheet != null)
|
||||
root.styleSheets.Add(_itemStyleSheet);
|
||||
|
||||
// 드래그 핸들 아이콘 설정
|
||||
var handle = root.Q<UTKLabel>("drag-handle");
|
||||
if (handle != null)
|
||||
handle.SetMaterialIcon(UTKMaterialIcons.DragIndicator);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
return CreateItemFallback();
|
||||
}
|
||||
|
||||
private VisualElement CreateItemFallback()
|
||||
{
|
||||
var container = new VisualElement();
|
||||
container.name = "item-container";
|
||||
container.AddToClassList("reordable-list-item");
|
||||
container.style.flexDirection = FlexDirection.Row;
|
||||
container.style.alignItems = Align.Center;
|
||||
container.style.minHeight = ITEM_HEIGHT;
|
||||
|
||||
var handle = new UTKLabel("", UTKMaterialIcons.DragIndicator);
|
||||
handle.name = "drag-handle";
|
||||
handle.AddToClassList("reordable-list-item__drag-handle");
|
||||
container.Add(handle);
|
||||
|
||||
var checkbox = new UTKCheckBox();
|
||||
checkbox.name = "active-checkbox";
|
||||
checkbox.AddToClassList("reordable-list-item__checkbox");
|
||||
container.Add(checkbox);
|
||||
|
||||
var inputField = new UTKInputField();
|
||||
inputField.name = "display-text-field";
|
||||
inputField.AddToClassList("reordable-list-item__input");
|
||||
container.Add(inputField);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private void BindItem(VisualElement element, int index)
|
||||
{
|
||||
if (index < 0 || index >= _items.Count) return;
|
||||
|
||||
// ListView가 makeItem 반환 후 inline flex-grow:0을 강제하므로 bindItem에서 덮어씀
|
||||
element.style.flexGrow = 1;
|
||||
|
||||
var data = _items[index];
|
||||
|
||||
// 요소 참조
|
||||
var container = element.Q<VisualElement>("item-container");
|
||||
var checkbox = container?.Q<UTKCheckBox>("active-checkbox");
|
||||
var inputField = container?.Q<UTKInputField>("display-text-field");
|
||||
|
||||
if (checkbox == null || inputField == null) return;
|
||||
|
||||
// 이전 콜백 해제 (재바인딩 시 중복 방지)
|
||||
CleanupItemCallbacks(element);
|
||||
|
||||
// 값 설정 (notify: false로 이벤트 발생 방지)
|
||||
checkbox.SetChecked(data.IsActive, notify: false);
|
||||
inputField.SetValue(data.DisplayText, notify: false);
|
||||
|
||||
// 새 콜백 등록
|
||||
Action<bool> onChecked = (value) =>
|
||||
{
|
||||
data.IsActive = value;
|
||||
OnDataChanged?.Invoke();
|
||||
};
|
||||
Action<string> onText = (value) =>
|
||||
{
|
||||
data.DisplayText = value;
|
||||
OnDataChanged?.Invoke();
|
||||
};
|
||||
|
||||
checkbox.OnValueChanged += onChecked;
|
||||
inputField.OnValueChanged += onText;
|
||||
|
||||
// 콜백 정보 저장 (해제용)
|
||||
element.userData = new ItemCallbackInfo(checkbox, inputField, onChecked, onText);
|
||||
}
|
||||
|
||||
private void UnbindItem(VisualElement element, int index)
|
||||
{
|
||||
CleanupItemCallbacks(element);
|
||||
}
|
||||
|
||||
private void CleanupItemCallbacks(VisualElement element)
|
||||
{
|
||||
if (element.userData is ItemCallbackInfo info)
|
||||
{
|
||||
info.Checkbox.OnValueChanged -= info.OnCheckedHandler;
|
||||
info.InputField.OnValueChanged -= info.OnTextHandler;
|
||||
element.userData = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnItemIndexChanged(int oldIndex, int newIndex)
|
||||
{
|
||||
// ListView가 내부적으로 itemsSource 순서를 변경함
|
||||
// Order 값만 현재 인덱스에 맞게 재계산
|
||||
SyncOrderValues();
|
||||
OnOrderChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void SyncOrderValues()
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
_items[i].Order = i;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// 데이터를 설정하고 ListView를 갱신합니다.
|
||||
/// </summary>
|
||||
/// <param name="items">표시할 아이템 데이터 목록.</param>
|
||||
public void SetData(List<ReordableListItemData> items)
|
||||
{
|
||||
_items = items ?? new List<ReordableListItemData>();
|
||||
SyncOrderValues();
|
||||
|
||||
if (_listView != null)
|
||||
{
|
||||
_listView.itemsSource = _items;
|
||||
_listView.RefreshItems();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List<Dictionary>로부터 데이터를 변환하여 설정합니다.
|
||||
/// Dictionary 키: "order" (순서), "active" (사용 유무), "text" (표시 내용)
|
||||
/// </summary>
|
||||
/// <param name="listDict">변환할 Dictionary 목록.</param>
|
||||
public void SetData(List<Dictionary<string, string>> listDict)
|
||||
{
|
||||
var items = new List<ReordableListItemData>();
|
||||
|
||||
if (listDict != null)
|
||||
{
|
||||
for (int i = 0; i < listDict.Count; i++)
|
||||
{
|
||||
var dict = listDict[i];
|
||||
var item = new ReordableListItemData();
|
||||
|
||||
item.Order = dict.TryGetValue("order", out var orderStr) && int.TryParse(orderStr, out var order)
|
||||
? order
|
||||
: i;
|
||||
|
||||
item.IsActive = dict.TryGetValue("active", out var activeStr) && bool.TryParse(activeStr, out var active)
|
||||
? active
|
||||
: true;
|
||||
|
||||
item.DisplayText = dict.TryGetValue("text", out var text)
|
||||
? text ?? ""
|
||||
: "";
|
||||
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
SetData(items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order 값이 동기화된 데이터 목록을 반환합니다.
|
||||
/// </summary>
|
||||
/// <returns>현재 ListView 순서가 반영된 데이터 목록.</returns>
|
||||
public List<ReordableListItemData> GetData()
|
||||
{
|
||||
SyncOrderValues();
|
||||
return new List<ReordableListItemData>(_items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 전체 아이템을 List<Dictionary<string, string>>로 변환하여 반환합니다.
|
||||
/// Dictionary 키: "order" (순서), "active" (사용 유무), "text" (표시 내용)
|
||||
/// </summary>
|
||||
/// <returns>각 아이템을 Dictionary로 변환한 목록.</returns>
|
||||
public List<Dictionary<string, string>> ToDictionary()
|
||||
{
|
||||
SyncOrderValues();
|
||||
var result = new List<Dictionary<string, string>>(_items.Count);
|
||||
foreach (var item in _items)
|
||||
{
|
||||
result.Add(new Dictionary<string, string>
|
||||
{
|
||||
["order"] = item.Order.ToString(),
|
||||
["active"] = item.IsActive.ToString(),
|
||||
["text"] = item.DisplayText
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Types
|
||||
/// <summary>바인딩 콜백 참조 추적</summary>
|
||||
private class ItemCallbackInfo
|
||||
{
|
||||
public readonly UTKCheckBox Checkbox;
|
||||
public readonly UTKInputField InputField;
|
||||
public readonly Action<bool> OnCheckedHandler;
|
||||
public readonly Action<string> OnTextHandler;
|
||||
|
||||
public ItemCallbackInfo(
|
||||
UTKCheckBox checkbox,
|
||||
UTKInputField inputField,
|
||||
Action<bool> onCheckedHandler,
|
||||
Action<string> onTextHandler)
|
||||
{
|
||||
Checkbox = checkbox;
|
||||
InputField = inputField;
|
||||
OnCheckedHandler = onCheckedHandler;
|
||||
OnTextHandler = onTextHandler;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
// 테마 구독 해제
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
|
||||
// ListView 이벤트 해제
|
||||
if (_listView != null)
|
||||
{
|
||||
_listView.itemIndexChanged -= OnItemIndexChanged;
|
||||
_listView.Dispose();
|
||||
}
|
||||
|
||||
// 이벤트 정리
|
||||
OnOrderChanged = null;
|
||||
OnDataChanged = null;
|
||||
|
||||
// 캐시 정리
|
||||
_itemTemplate = null;
|
||||
_itemStyleSheet = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fed060569bd98e748b45857977376b63
|
||||
8
Assets/Scripts/UVC/UIToolkit/Modal/Setting.meta
Normal file
8
Assets/Scripts/UVC/UIToolkit/Modal/Setting.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9210d67d228d2814eb32cc841227db5b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,193 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 설정 표시 정보 탭 뷰.
|
||||
/// UTKReordableList를 사용하여 표시 항목의 순서, 사용 유무, 내용을 관리합니다.
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class UTKSettingDisplayInfoTabView : VisualElement, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string USS_PATH = "UIToolkit/Modal/Setting/UTKSettingDisplayInfoTabViewUss";
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
private UTKReordableList? _reordableList;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>순서 변경 시 발생</summary>
|
||||
public event Action? OnOrderChanged;
|
||||
|
||||
/// <summary>데이터(체크/텍스트) 변경 시 발생</summary>
|
||||
public event Action? OnDataChanged;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKSettingDisplayInfoTabView() : base()
|
||||
{
|
||||
// 1. 테마 적용
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
|
||||
// 2. USS 로드
|
||||
var uss = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (uss != null)
|
||||
{
|
||||
styleSheets.Add(uss);
|
||||
}
|
||||
|
||||
// 3. UI 생성
|
||||
CreateUI();
|
||||
|
||||
// 4. 테마 변경 구독
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
SampleSetAndGetWithDictionary();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Setup
|
||||
private void CreateUI()
|
||||
{
|
||||
AddToClassList("setting-display-tab-view");
|
||||
|
||||
_reordableList = new UTKReordableList();
|
||||
_reordableList.style.flexGrow = 1;
|
||||
_reordableList.OnOrderChanged += () => OnOrderChanged?.Invoke();
|
||||
_reordableList.OnDataChanged += () => OnDataChanged?.Invoke();
|
||||
Add(_reordableList);
|
||||
|
||||
// 하단 버튼 영역
|
||||
var buttonContainer = new VisualElement();
|
||||
buttonContainer.style.flexDirection = FlexDirection.Row;
|
||||
buttonContainer.style.justifyContent = Justify.FlexEnd;
|
||||
buttonContainer.style.paddingTop = 8;
|
||||
|
||||
var saveBtn = new UTKButton("저장", variant: UTKButton.ButtonVariant.Primary);
|
||||
saveBtn.OnClicked += OnSaveButtonClicked;
|
||||
buttonContainer.Add(saveBtn);
|
||||
|
||||
Add(buttonContainer);
|
||||
}
|
||||
|
||||
private void OnSaveButtonClicked()
|
||||
{
|
||||
List<Dictionary<string, string>> result = ToDictionary();
|
||||
foreach (var dict in result)
|
||||
{
|
||||
Debug.Log($"order={dict["order"]}, active={dict["active"]}, text={dict["text"]}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
}
|
||||
|
||||
private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
|
||||
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
|
||||
private void OnThemeChanged(UTKTheme theme)
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
/// <summary>
|
||||
/// 데이터를 설정하고 리스트를 갱신합니다.
|
||||
/// </summary>
|
||||
/// <param name="items">표시할 아이템 데이터 목록.</param>
|
||||
public void SetData(List<ReordableListItemData> items)
|
||||
{
|
||||
_reordableList?.SetData(items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List<Dictionary>로부터 데이터를 변환하여 설정합니다.
|
||||
/// Dictionary 키: "order" (순서), "active" (사용 유무), "text" (표시 내용)
|
||||
/// </summary>
|
||||
/// <param name="listDict">변환할 Dictionary 목록.</param>
|
||||
public void SetData(List<Dictionary<string, string>> listDict)
|
||||
{
|
||||
_reordableList?.SetData(listDict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order 값이 동기화된 데이터 목록을 반환합니다.
|
||||
/// </summary>
|
||||
/// <returns>현재 리스트 순서가 반영된 데이터 목록.</returns>
|
||||
public List<ReordableListItemData> GetData()
|
||||
{
|
||||
return _reordableList?.GetData() ?? new List<ReordableListItemData>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 전체 아이템을 List<Dictionary<string, string>>로 변환하여 반환합니다.
|
||||
/// Dictionary 키: "order" (순서), "active" (사용 유무), "text" (표시 내용)
|
||||
/// </summary>
|
||||
/// <returns>각 아이템을 Dictionary로 변환한 목록.</returns>
|
||||
public List<Dictionary<string, string>> ToDictionary()
|
||||
{
|
||||
return _reordableList?.ToDictionary() ?? new List<Dictionary<string, string>>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Sample
|
||||
/// <summary>
|
||||
/// Dictionary 기반 데이터 설정 및 조회 샘플.
|
||||
/// </summary>
|
||||
public void SampleSetAndGetWithDictionary()
|
||||
{
|
||||
// 1. Dictionary 데이터로 설정
|
||||
var listDict = new List<Dictionary<string, string>>
|
||||
{
|
||||
new() { ["order"] = "0", ["active"] = "True", ["text"] = "온도" },
|
||||
new() { ["order"] = "1", ["active"] = "False", ["text"] = "습도" },
|
||||
new() { ["order"] = "2", ["active"] = "True", ["text"] = "압력" },
|
||||
};
|
||||
SetData(listDict);
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
|
||||
// 테마 구독 해제
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
|
||||
// ReordableList 정리
|
||||
_reordableList?.Dispose();
|
||||
_reordableList = null;
|
||||
|
||||
// 이벤트 정리
|
||||
OnOrderChanged = null;
|
||||
OnDataChanged = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69a4b7a0f627e79418a0ff355aab75d4
|
||||
103
Assets/Scripts/UVC/UIToolkit/Property/Core/TabPropertyData.cs
Normal file
103
Assets/Scripts/UVC/UIToolkit/Property/Core/TabPropertyData.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// 탭의 프로퍼티 데이터 로드 유형
|
||||
/// </summary>
|
||||
public enum TabPropertyDataType
|
||||
{
|
||||
/// <summary>평면 속성 목록 (그룹 없이)</summary>
|
||||
Flat,
|
||||
/// <summary>그룹화된 속성 목록</summary>
|
||||
Grouped,
|
||||
/// <summary>그룹과 개별 아이템이 혼합된 목록</summary>
|
||||
Mixed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 탭별 프로퍼티 설정 데이터 클래스입니다.
|
||||
/// 탭의 메타데이터(이름, 아이콘, 활성화 상태)와
|
||||
/// 해당 탭에 표시할 프로퍼티 데이터를 보유합니다.
|
||||
/// </summary>
|
||||
public class TabPropertyData
|
||||
{
|
||||
#region Properties
|
||||
/// <summary>탭 이름 (표시 텍스트)</summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>탭 아이콘 (Material Icon 유니코드, null이면 아이콘 없음)</summary>
|
||||
public string? Icon { get; set; }
|
||||
|
||||
/// <summary>탭 활성화 상태</summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>탭 툴팁</summary>
|
||||
public string? Tooltip { get; set; }
|
||||
|
||||
/// <summary>데이터 로드 유형</summary>
|
||||
public TabPropertyDataType DataType { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Data Fields
|
||||
private List<IUTKPropertyItem>? _flatItems;
|
||||
private List<IUTKPropertyGroup>? _groupedItems;
|
||||
private List<IUTKPropertyEntry>? _mixedItems;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>기본 생성자</summary>
|
||||
public TabPropertyData(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>아이콘 포함 생성자</summary>
|
||||
public TabPropertyData(string name, string? icon) : this(name)
|
||||
{
|
||||
Icon = icon;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Data Setters
|
||||
/// <summary>평면 속성 목록 설정 (Flat 타입)</summary>
|
||||
public void SetFlatData(List<IUTKPropertyItem> items)
|
||||
{
|
||||
DataType = TabPropertyDataType.Flat;
|
||||
_flatItems = items;
|
||||
_groupedItems = null;
|
||||
_mixedItems = null;
|
||||
}
|
||||
|
||||
/// <summary>그룹화된 속성 목록 설정 (Grouped 타입)</summary>
|
||||
public void SetGroupedData(List<IUTKPropertyGroup> groups)
|
||||
{
|
||||
DataType = TabPropertyDataType.Grouped;
|
||||
_flatItems = null;
|
||||
_groupedItems = groups;
|
||||
_mixedItems = null;
|
||||
}
|
||||
|
||||
/// <summary>혼합 속성 목록 설정 (Mixed 타입)</summary>
|
||||
public void SetMixedData(List<IUTKPropertyEntry> entries)
|
||||
{
|
||||
DataType = TabPropertyDataType.Mixed;
|
||||
_flatItems = null;
|
||||
_groupedItems = null;
|
||||
_mixedItems = entries;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Data Getters
|
||||
/// <summary>평면 데이터 반환 (Flat 타입이 아니면 null)</summary>
|
||||
public List<IUTKPropertyItem>? GetFlatData() => _flatItems;
|
||||
|
||||
/// <summary>그룹화된 데이터 반환 (Grouped 타입이 아니면 null)</summary>
|
||||
public List<IUTKPropertyGroup>? GetGroupedData() => _groupedItems;
|
||||
|
||||
/// <summary>혼합 데이터 반환 (Mixed 타입이 아니면 null)</summary>
|
||||
public List<IUTKPropertyEntry>? GetMixedData() => _mixedItems;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3dae65c9584b6444a75fc9835a6333c
|
||||
@@ -15,10 +15,10 @@ namespace UVC.UIToolkit
|
||||
#region Fields
|
||||
|
||||
/// <summary>아이콘 요소 (Material Icon Label 또는 Image)</summary>
|
||||
protected Label? _iconLabel;
|
||||
protected UTKLabel? _iconLabel;
|
||||
|
||||
/// <summary>텍스트 라벨</summary>
|
||||
protected Label? _textLabel;
|
||||
protected UTKLabel? _textLabel;
|
||||
|
||||
/// <summary>루트 버튼 요소</summary>
|
||||
protected VisualElement? _rootButton;
|
||||
@@ -103,12 +103,13 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
var root = asset.Instantiate();
|
||||
_rootButton = root.Q<VisualElement>("button-root");
|
||||
_iconLabel = root.Q<Label>("icon");
|
||||
_textLabel = root.Q<Label>("label");
|
||||
_iconLabel = root.Q<UTKLabel>("icon");
|
||||
_textLabel = root.Q<UTKLabel>("label");
|
||||
_textLabel.Size = UTKLabel.LabelSize.Caption; // UXML에서 기본 크기를 설정하므로 코드에서 다시 지정
|
||||
|
||||
// TemplateContainer가 아이콘 정렬을 방해하지 않도록 설정
|
||||
root.style.flexGrow = 1;
|
||||
root.style.alignItems = Align.Center;
|
||||
root.style.alignItems = Align.Stretch;
|
||||
root.style.justifyContent = Justify.Center;
|
||||
|
||||
Add(root);
|
||||
@@ -129,12 +130,13 @@ namespace UVC.UIToolkit
|
||||
_rootButton = new VisualElement();
|
||||
_rootButton.AddToClassList("utk-toolbar-btn");
|
||||
|
||||
_iconLabel = new Label();
|
||||
_iconLabel = new UTKLabel();
|
||||
_iconLabel.AddToClassList("utk-toolbar-btn__icon");
|
||||
_rootButton.Add(_iconLabel);
|
||||
|
||||
_textLabel = new Label();
|
||||
_textLabel = new UTKLabel();
|
||||
_textLabel.AddToClassList("utk-toolbar-btn__label");
|
||||
_textLabel.Size = UTKLabel.LabelSize.Caption;
|
||||
_rootButton.Add(_textLabel);
|
||||
|
||||
Add(_rootButton);
|
||||
@@ -164,6 +166,7 @@ namespace UVC.UIToolkit
|
||||
UpdateIcon(_data.IconPath, _data.UseMaterialIcon);
|
||||
UpdateText(_data.Text);
|
||||
UpdateEnabled(_data.IsEnabled);
|
||||
UpdateTooltip(_data.Tooltip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -216,29 +219,15 @@ namespace UVC.UIToolkit
|
||||
|
||||
if (useMaterialIcon)
|
||||
{
|
||||
// Material Icon (폰트 기반)
|
||||
// Material Icon (폰트 기반) - UTKLabel의 SetMaterialIcon 사용
|
||||
_iconLabel.RemoveFromClassList("utk-toolbar-btn__icon--image");
|
||||
_iconLabel.text = iconPath;
|
||||
_iconLabel.style.backgroundImage = StyleKeyword.None;
|
||||
_iconLabel.SetMaterialIcon(iconPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 이미지 아이콘
|
||||
// 이미지 아이콘 - UTKLabel의 SetImageIcon 사용
|
||||
_iconLabel.AddToClassList("utk-toolbar-btn__icon--image");
|
||||
_iconLabel.text = "";
|
||||
var sprite = Resources.Load<Sprite>(iconPath);
|
||||
if (sprite != null)
|
||||
{
|
||||
_iconLabel.style.backgroundImage = new StyleBackground(sprite);
|
||||
}
|
||||
else
|
||||
{
|
||||
var texture = Resources.Load<Texture2D>(iconPath);
|
||||
if (texture != null)
|
||||
{
|
||||
_iconLabel.style.backgroundImage = new StyleBackground(texture);
|
||||
}
|
||||
}
|
||||
_iconLabel.SetImageIcon(iconPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +239,26 @@ namespace UVC.UIToolkit
|
||||
{
|
||||
if (_textLabel != null)
|
||||
{
|
||||
_textLabel.text = text;
|
||||
_textLabel.Text = text;
|
||||
_textLabel.style.display = string.IsNullOrEmpty(text) ? DisplayStyle.None : DisplayStyle.Flex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 툴팁을 업데이트합니다.
|
||||
/// </summary>
|
||||
/// <param name="tooltipText">툴팁 텍스트</param>
|
||||
protected void UpdateTooltip(string? tooltipText)
|
||||
{
|
||||
if (_rootButton == null) return;
|
||||
|
||||
if (string.IsNullOrEmpty(tooltipText))
|
||||
{
|
||||
UTKTooltipManager.Instance.DetachTooltip(_rootButton);
|
||||
}
|
||||
else
|
||||
{
|
||||
UTKTooltipManager.Instance.UpdateTooltip(_rootButton, tooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +290,7 @@ namespace UVC.UIToolkit
|
||||
UpdateIcon(_data.IconPath, _data.UseMaterialIcon);
|
||||
UpdateText(_data.Text);
|
||||
UpdateEnabled(_data.IsEnabled);
|
||||
UpdateTooltip(_data.Tooltip);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -354,6 +363,12 @@ namespace UVC.UIToolkit
|
||||
// 데이터 바인딩 해제
|
||||
UnbindData();
|
||||
|
||||
// 툴팁 해제
|
||||
if (_rootButton != null)
|
||||
{
|
||||
UTKTooltipManager.Instance.DetachTooltip(_rootButton);
|
||||
}
|
||||
|
||||
// 테마 구독 해제
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
|
||||
871
Assets/Scripts/UVC/UIToolkit/Window/UTKPropertyTabListWindow.cs
Normal file
871
Assets/Scripts/UVC/UIToolkit/Window/UTKPropertyTabListWindow.cs
Normal file
@@ -0,0 +1,871 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace UVC.UIToolkit
|
||||
{
|
||||
/// <summary>
|
||||
/// UTKPropertyList와 UTKTabView를 결합한 탭 기반 프로퍼티 윈도우입니다.
|
||||
/// 헤더(타이틀 + 닫기 버튼), 탭 뷰, 프로퍼티 리스트로 구성되며,
|
||||
/// 탭별로 서로 다른 프로퍼티 데이터를 설정할 수 있습니다.
|
||||
///
|
||||
/// <para><b>주요 기능:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item>윈도우 프레임 (헤더, 타이틀, 닫기 버튼)</item>
|
||||
/// <item>UTKTabView 기반 탭 전환</item>
|
||||
/// <item>탭별 프로퍼티 데이터 관리 (Flat/Grouped/Mixed 지원)</item>
|
||||
/// <item>탭별 검색어 저장/복원</item>
|
||||
/// <item>선택적 "전체(All)" 탭</item>
|
||||
/// <item>헤더 드래그로 위치 이동</item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>사용 예 (C#):</b></para>
|
||||
/// <code>
|
||||
/// var window = new UTKPropertyTabListWindow("속성 편집기");
|
||||
/// window.ShowCloseButton = true;
|
||||
/// window.ShowAllTab = false;
|
||||
///
|
||||
/// // 탭 데이터 설정
|
||||
/// var generalTab = new TabPropertyData("일반");
|
||||
/// generalTab.SetGroupedData(new List<IUTKPropertyGroup> { transformGroup, renderGroup });
|
||||
///
|
||||
/// var advancedTab = new TabPropertyData("고급", "\ue8b8");
|
||||
/// advancedTab.SetFlatData(new List<IUTKPropertyItem> { debugItem, logItem });
|
||||
///
|
||||
/// window.SetTabData(new List<TabPropertyData> { generalTab, advancedTab });
|
||||
///
|
||||
/// // 이벤트 구독
|
||||
/// window.OnTabChanged += (index, data) => Debug.Log($"탭 변경: {data?.Name}");
|
||||
/// window.OnPropertyValueChanged += args => Debug.Log($"{args.PropertyId} = {args.NewValue}");
|
||||
///
|
||||
/// root.Add(window);
|
||||
/// </code>
|
||||
/// </summary>
|
||||
[UxmlElement]
|
||||
public partial class UTKPropertyTabListWindow : VisualElement, IDisposable
|
||||
{
|
||||
#region Constants
|
||||
private const string UXML_PATH = "UIToolkit/Window/UTKPropertyTabListWindow";
|
||||
private const string USS_PATH = "UIToolkit/Window/UTKPropertyTabListWindowUss";
|
||||
private const int ALL_TAB_INDEX = -1;
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private bool _disposed;
|
||||
|
||||
// UI 요소 참조
|
||||
private VisualElement? _header;
|
||||
private UTKLabel? _titleLabel;
|
||||
private UTKButton? _closeButton;
|
||||
private UTKTabView? _tabView;
|
||||
private UTKPropertyList? _propertyList;
|
||||
|
||||
// 탭 데이터
|
||||
private readonly List<TabPropertyData> _tabDataList = new();
|
||||
private int _selectedTabIndex = ALL_TAB_INDEX;
|
||||
private bool _showAllTab = true;
|
||||
|
||||
// 탭별 검색어 저장
|
||||
private readonly Dictionary<int, string> _tabSearchQueries = new();
|
||||
|
||||
// 드래그 상태
|
||||
private bool _isDragging;
|
||||
private Vector2 _dragStartPosition;
|
||||
private Vector2 _dragStartMousePosition;
|
||||
|
||||
// 윈도우 속성
|
||||
private string _title = "Properties";
|
||||
private bool _showCloseButton = false;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>윈도우 타이틀</summary>
|
||||
[UxmlAttribute("title")]
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
_titleLabel.Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>닫기 버튼 표시 여부</summary>
|
||||
[UxmlAttribute("show-close-button")]
|
||||
public bool ShowCloseButton
|
||||
{
|
||||
get => _showCloseButton;
|
||||
set
|
||||
{
|
||||
_showCloseButton = value;
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.style.display = value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>"전체(All)" 탭 표시 여부</summary>
|
||||
[UxmlAttribute("show-all-tab")]
|
||||
public bool ShowAllTab
|
||||
{
|
||||
get => _showAllTab;
|
||||
set
|
||||
{
|
||||
if (_showAllTab == value) return;
|
||||
_showAllTab = value;
|
||||
if (_tabDataList.Count > 0)
|
||||
{
|
||||
RebuildTabs();
|
||||
SelectTab(_showAllTab ? ALL_TAB_INDEX : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>현재 선택된 탭 인덱스 (-1: All 탭)</summary>
|
||||
public int SelectedTabIndex => _selectedTabIndex;
|
||||
|
||||
/// <summary>내부 UTKPropertyList 접근</summary>
|
||||
public UTKPropertyList PropertyList => _propertyList ??= new UTKPropertyList();
|
||||
|
||||
/// <summary>내부 UTKTabView 접근</summary>
|
||||
public UTKTabView? TabView => _tabView;
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>닫기 버튼 클릭 이벤트</summary>
|
||||
public event Action? OnCloseClicked;
|
||||
|
||||
/// <summary>탭 변경 이벤트 (탭 인덱스, TabPropertyData)</summary>
|
||||
public event Action<int, TabPropertyData?>? OnTabChanged;
|
||||
|
||||
/// <summary>속성 값 변경 이벤트 (PropertyList 위임)</summary>
|
||||
public event Action<UTKPropertyValueChangedEventArgs>? OnPropertyValueChanged
|
||||
{
|
||||
add => PropertyList.OnPropertyValueChanged += value;
|
||||
remove => PropertyList.OnPropertyValueChanged -= value;
|
||||
}
|
||||
|
||||
/// <summary>그룹 펼침/접힘 이벤트 (PropertyList 위임)</summary>
|
||||
public event Action<IUTKPropertyGroup, bool>? OnGroupExpandedChanged
|
||||
{
|
||||
add => PropertyList.OnGroupExpandedChanged += value;
|
||||
remove => PropertyList.OnGroupExpandedChanged -= value;
|
||||
}
|
||||
|
||||
/// <summary>속성 클릭 이벤트 (PropertyList 위임)</summary>
|
||||
public event Action<IUTKPropertyItem>? OnPropertyClicked
|
||||
{
|
||||
add => PropertyList.OnPropertyClicked += value;
|
||||
remove => PropertyList.OnPropertyClicked -= value;
|
||||
}
|
||||
|
||||
/// <summary>버튼 클릭 이벤트 (PropertyList 위임)</summary>
|
||||
public event Action<string, string>? OnPropertyButtonClicked
|
||||
{
|
||||
add => PropertyList.OnPropertyButtonClicked += value;
|
||||
remove => PropertyList.OnPropertyButtonClicked -= value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
public UTKPropertyTabListWindow()
|
||||
{
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
SubscribeToThemeChanges();
|
||||
|
||||
var styleSheet = Resources.Load<StyleSheet>(USS_PATH);
|
||||
if (styleSheet != null)
|
||||
{
|
||||
styleSheets.Add(styleSheet);
|
||||
}
|
||||
|
||||
CreateUI();
|
||||
}
|
||||
|
||||
public UTKPropertyTabListWindow(string title) : this()
|
||||
{
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public UTKPropertyTabListWindow(string title, bool showAllTab) : this(title)
|
||||
{
|
||||
_showAllTab = showAllTab;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region UI Creation
|
||||
private void CreateUI()
|
||||
{
|
||||
AddToClassList("utk-property-tab-window");
|
||||
|
||||
var asset = Resources.Load<VisualTreeAsset>(UXML_PATH);
|
||||
if (asset != null)
|
||||
{
|
||||
CreateUIFromUxml(asset);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateUIFallback();
|
||||
}
|
||||
|
||||
// 드래그 이벤트
|
||||
if (_header != null)
|
||||
{
|
||||
_header.RegisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header.RegisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header.RegisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
}
|
||||
|
||||
// UTKTabView 탭 변경 이벤트 구독
|
||||
if (_tabView != null)
|
||||
{
|
||||
_tabView.OnTabChanged += OnTabViewTabChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUIFromUxml(VisualTreeAsset asset)
|
||||
{
|
||||
var root = asset.Instantiate();
|
||||
var windowRoot = root.Q<VisualElement>("window-root");
|
||||
|
||||
if (windowRoot != null)
|
||||
{
|
||||
foreach (var child in windowRoot.Children().ToArray())
|
||||
{
|
||||
Add(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(root);
|
||||
}
|
||||
|
||||
// 요소 참조 가져오기
|
||||
_header = this.Q<VisualElement>("header");
|
||||
_titleLabel = this.Q<UTKLabel>("title");
|
||||
_closeButton = this.Q<UTKButton>("close-btn");
|
||||
_tabView = this.Q<UTKTabView>("tab-view");
|
||||
_propertyList = this.Q<UTKPropertyList>("content");
|
||||
|
||||
// 타이틀 설정
|
||||
if (_titleLabel != null)
|
||||
{
|
||||
_titleLabel.Text = _title;
|
||||
_titleLabel.Size = UTKLabel.LabelSize.Label3;
|
||||
}
|
||||
|
||||
// 닫기 버튼 설정
|
||||
if (_closeButton != null)
|
||||
{
|
||||
_closeButton.SetMaterialIcon(UTKMaterialIcons.Close, 14);
|
||||
_closeButton.IconOnly = true;
|
||||
_closeButton.OnClicked += () => OnCloseClicked?.Invoke();
|
||||
_closeButton.style.display = _showCloseButton ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// TabView가 없으면 생성
|
||||
if (_tabView == null)
|
||||
{
|
||||
_tabView = new UTKTabView();
|
||||
_tabView.name = "tab-view";
|
||||
_tabView.AddToClassList("utk-property-tab-window__tab-view");
|
||||
// PropertyList 앞에 삽입
|
||||
if (_propertyList != null)
|
||||
{
|
||||
int index = IndexOf(_propertyList);
|
||||
Insert(index, _tabView);
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(_tabView);
|
||||
}
|
||||
}
|
||||
|
||||
// PropertyList가 없으면 생성
|
||||
if (_propertyList == null)
|
||||
{
|
||||
_propertyList = new UTKPropertyList();
|
||||
_propertyList.name = "content";
|
||||
_propertyList.AddToClassList("utk-property-tab-window__content");
|
||||
Add(_propertyList);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateUIFallback()
|
||||
{
|
||||
// 헤더
|
||||
_header = new VisualElement();
|
||||
_header.name = "header";
|
||||
_header.AddToClassList("utk-property-tab-window__header");
|
||||
|
||||
_titleLabel = new UTKLabel(_title, UTKLabel.LabelSize.Label3);
|
||||
_titleLabel.name = "title";
|
||||
_titleLabel.AddToClassList("utk-property-tab-window__title");
|
||||
_header.Add(_titleLabel);
|
||||
|
||||
_closeButton = new UTKButton("", UTKMaterialIcons.Close, UTKButton.ButtonVariant.Text, 14);
|
||||
_closeButton.name = "close-btn";
|
||||
_closeButton.IconOnly = true;
|
||||
_closeButton.AddToClassList("utk-property-tab-window__close-btn");
|
||||
_closeButton.OnClicked += () => OnCloseClicked?.Invoke();
|
||||
_closeButton.style.display = _showCloseButton ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
_header.Add(_closeButton);
|
||||
|
||||
Add(_header);
|
||||
|
||||
// 탭 뷰
|
||||
_tabView = new UTKTabView();
|
||||
_tabView.name = "tab-view";
|
||||
_tabView.AddToClassList("utk-property-tab-window__tab-view");
|
||||
Add(_tabView);
|
||||
|
||||
// PropertyList
|
||||
_propertyList = new UTKPropertyList();
|
||||
_propertyList.name = "content";
|
||||
_propertyList.AddToClassList("utk-property-tab-window__content");
|
||||
Add(_propertyList);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Tab Data Management
|
||||
|
||||
/// <summary>
|
||||
/// 탭 데이터 목록을 설정합니다.
|
||||
/// 기존 탭을 모두 제거하고 새로운 탭을 생성합니다.
|
||||
/// </summary>
|
||||
/// <param name="tabDataList">탭 설정 데이터 목록</param>
|
||||
public void SetTabData(List<TabPropertyData> tabDataList)
|
||||
{
|
||||
_tabDataList.Clear();
|
||||
_tabDataList.AddRange(tabDataList);
|
||||
_tabSearchQueries.Clear();
|
||||
|
||||
RebuildTabs();
|
||||
|
||||
// 기본 탭 선택
|
||||
if (_showAllTab)
|
||||
SelectTab(ALL_TAB_INDEX);
|
||||
else if (_tabDataList.Count > 0)
|
||||
SelectTab(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 탭 데이터를 추가합니다.
|
||||
/// </summary>
|
||||
/// <param name="tabData">추가할 탭 데이터</param>
|
||||
public void AddTabData(TabPropertyData tabData)
|
||||
{
|
||||
_tabDataList.Add(tabData);
|
||||
RebuildTabs();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 인덱스의 탭 데이터를 제거합니다.
|
||||
/// </summary>
|
||||
/// <param name="index">제거할 탭 인덱스 (0-based)</param>
|
||||
public void RemoveTabData(int index)
|
||||
{
|
||||
if (index < 0 || index >= _tabDataList.Count) return;
|
||||
|
||||
_tabDataList.RemoveAt(index);
|
||||
_tabSearchQueries.Remove(index);
|
||||
RebuildTabs();
|
||||
|
||||
// 현재 선택된 탭이 제거된 경우 기본 탭 선택
|
||||
if (_selectedTabIndex == index)
|
||||
{
|
||||
SelectTab(_showAllTab ? ALL_TAB_INDEX : 0);
|
||||
}
|
||||
else if (_selectedTabIndex > index)
|
||||
{
|
||||
// 인덱스 보정
|
||||
_selectedTabIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 특정 인덱스의 탭 데이터를 반환합니다.
|
||||
/// </summary>
|
||||
/// <param name="index">탭 인덱스 (0-based)</param>
|
||||
/// <returns>탭 데이터 또는 null</returns>
|
||||
public TabPropertyData? GetTabData(int index)
|
||||
{
|
||||
if (index >= 0 && index < _tabDataList.Count)
|
||||
return _tabDataList[index];
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 탭을 선택하고 해당 데이터를 PropertyList에 로드합니다.
|
||||
/// </summary>
|
||||
/// <param name="tabIndex">탭 인덱스 (-1: All 탭, 0+: 개별 탭)</param>
|
||||
public void SelectTab(int tabIndex)
|
||||
{
|
||||
// 유효성 검사
|
||||
if (tabIndex != ALL_TAB_INDEX && (tabIndex < 0 || tabIndex >= _tabDataList.Count))
|
||||
return;
|
||||
if (tabIndex == ALL_TAB_INDEX && !_showAllTab)
|
||||
return;
|
||||
|
||||
// 1. 이전 탭의 검색어 저장
|
||||
SaveCurrentSearchQuery();
|
||||
|
||||
// 2. 탭 인덱스 변경
|
||||
_selectedTabIndex = tabIndex;
|
||||
|
||||
// 3. UTKTabView 선택 동기화
|
||||
SyncTabViewSelection(tabIndex);
|
||||
|
||||
// 4. 데이터 로드
|
||||
LoadDataForTab(tabIndex);
|
||||
|
||||
// 5. 검색어 복원
|
||||
RestoreSearchQuery(tabIndex);
|
||||
|
||||
// 6. 탭 변경 이벤트 발생
|
||||
var tabData = tabIndex >= 0 && tabIndex < _tabDataList.Count
|
||||
? _tabDataList[tabIndex]
|
||||
: null;
|
||||
OnTabChanged?.Invoke(tabIndex, tabData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 탭의 데이터를 다시 로드합니다.
|
||||
/// </summary>
|
||||
public void RefreshCurrentTab()
|
||||
{
|
||||
LoadDataForTab(_selectedTabIndex);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Group Management (PropertyList 위임)
|
||||
/// <summary>그룹을 추가합니다.</summary>
|
||||
public void AddGroup(IUTKPropertyGroup group) => PropertyList.AddGroup(group);
|
||||
|
||||
/// <summary>지정한 ID의 그룹과 내부 아이템을 모두 제거합니다.</summary>
|
||||
public void RemoveGroup(string groupId) => PropertyList.RemoveGroup(groupId);
|
||||
|
||||
/// <summary>지정한 ID의 그룹을 반환합니다.</summary>
|
||||
public IUTKPropertyGroup? GetGroup(string groupId) => PropertyList.GetGroup(groupId);
|
||||
|
||||
/// <summary>그룹의 펼침/접힘 상태를 설정합니다.</summary>
|
||||
public void SetGroupExpanded(string groupId, bool expanded) => PropertyList.SetGroupExpanded(groupId, expanded);
|
||||
|
||||
/// <summary>그룹의 펼침/접힘 상태를 토글합니다.</summary>
|
||||
public void ToggleGroupExpanded(string groupId) => PropertyList.ToggleGroupExpanded(groupId);
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Property Management (PropertyList 위임)
|
||||
/// <summary>최상위 속성 아이템을 추가합니다.</summary>
|
||||
public void AddProperty(IUTKPropertyItem item) => PropertyList.AddProperty(item);
|
||||
|
||||
/// <summary>지정한 그룹에 속성 아이템을 추가합니다.</summary>
|
||||
public void AddPropertyToGroup(string groupId, IUTKPropertyItem item) => PropertyList.AddPropertyToGroup(groupId, item);
|
||||
|
||||
/// <summary>지정한 ID의 속성 아이템을 제거합니다.</summary>
|
||||
public void RemoveProperty(string itemId) => PropertyList.RemoveProperty(itemId);
|
||||
|
||||
/// <summary>지정한 ID의 속성 아이템을 반환합니다.</summary>
|
||||
public IUTKPropertyItem? GetProperty(string itemId) => PropertyList.GetProperty(itemId);
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Value Management (PropertyList 위임)
|
||||
/// <summary>속성 값을 변경합니다.</summary>
|
||||
public void UpdatePropertyValue(string propertyId, object newValue, bool notify = false) => PropertyList.UpdatePropertyValue(propertyId, newValue, notify);
|
||||
|
||||
/// <summary>속성 값을 변경합니다. UpdatePropertyValue의 별칭입니다.</summary>
|
||||
public void SetPropertyValue(string propertyId, object value, bool notify = false) => PropertyList.SetPropertyValue(propertyId, value, notify);
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Visibility & ReadOnly (PropertyList 위임)
|
||||
/// <summary>속성 아이템의 가시성을 변경합니다.</summary>
|
||||
public void SetPropertyVisibility(string propertyId, bool visible) => PropertyList.SetPropertyVisibility(propertyId, visible);
|
||||
|
||||
/// <summary>여러 속성의 가시성을 일괄 변경합니다.</summary>
|
||||
public void SetPropertyVisibilityBatch(IEnumerable<(string propertyId, bool visible)> changes) => PropertyList.SetPropertyVisibilityBatch(changes);
|
||||
|
||||
/// <summary>그룹의 가시성을 변경합니다.</summary>
|
||||
public void SetGroupVisibility(string groupId, bool visible) => PropertyList.SetGroupVisibility(groupId, visible);
|
||||
|
||||
/// <summary>속성 아이템의 읽기 전용 상태를 변경합니다.</summary>
|
||||
public void SetPropertyReadOnly(string propertyId, bool isReadOnly) => PropertyList.SetPropertyReadOnly(propertyId, isReadOnly);
|
||||
|
||||
/// <summary>그룹 내 모든 아이템의 읽기 전용 상태를 일괄 변경합니다.</summary>
|
||||
public void SetGroupReadOnly(string groupId, bool isReadOnly) => PropertyList.SetGroupReadOnly(groupId, isReadOnly);
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Utilities (PropertyList 위임)
|
||||
/// <summary>모든 엔트리(그룹 + 아이템)를 제거하고 초기화합니다.</summary>
|
||||
public new void Clear() => PropertyList.Clear();
|
||||
|
||||
/// <summary>현재 데이터를 기반으로 TreeView를 다시 빌드합니다.</summary>
|
||||
public void Refresh() => PropertyList.Refresh();
|
||||
#endregion
|
||||
|
||||
#region Public Methods - Window
|
||||
/// <summary>윈도우를 표시합니다.</summary>
|
||||
public void Show()
|
||||
{
|
||||
style.display = DisplayStyle.Flex;
|
||||
}
|
||||
|
||||
/// <summary>윈도우를 숨깁니다.</summary>
|
||||
public void Hide()
|
||||
{
|
||||
style.display = DisplayStyle.None;
|
||||
}
|
||||
|
||||
/// <summary>윈도우의 위치를 설정합니다 (absolute 포지셔닝).</summary>
|
||||
public void SetPosition(float x, float y)
|
||||
{
|
||||
style.left = x;
|
||||
style.top = y;
|
||||
}
|
||||
|
||||
/// <summary>윈도우의 크기를 설정합니다.</summary>
|
||||
public void SetSize(float width, float height)
|
||||
{
|
||||
style.width = width;
|
||||
style.height = height;
|
||||
}
|
||||
|
||||
/// <summary>부모 요소 기준으로 윈도우를 중앙에 배치합니다.</summary>
|
||||
public void CenterOnScreen()
|
||||
{
|
||||
schedule.Execute(() =>
|
||||
{
|
||||
var parent = this.parent;
|
||||
if (parent == null) return;
|
||||
|
||||
float parentWidth = parent.resolvedStyle.width;
|
||||
float parentHeight = parent.resolvedStyle.height;
|
||||
float selfWidth = resolvedStyle.width;
|
||||
float selfHeight = resolvedStyle.height;
|
||||
|
||||
style.left = (parentWidth - selfWidth) / 2;
|
||||
style.top = (parentHeight - selfHeight) / 2;
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods - Tab Management
|
||||
|
||||
/// <summary>
|
||||
/// UTKTabView에 탭을 재구성합니다.
|
||||
/// </summary>
|
||||
private void RebuildTabs()
|
||||
{
|
||||
if (_tabView == null) return;
|
||||
|
||||
// 이벤트 일시 해제 (재구성 중 탭 변경 이벤트 방지)
|
||||
_tabView.OnTabChanged -= OnTabViewTabChanged;
|
||||
_tabView.ClearTabs();
|
||||
|
||||
// "All" 탭 생성 (옵션)
|
||||
if (_showAllTab)
|
||||
{
|
||||
_tabView.AddUTKTab("All");
|
||||
}
|
||||
|
||||
// 개별 탭 생성
|
||||
for (int i = 0; i < _tabDataList.Count; i++)
|
||||
{
|
||||
var data = _tabDataList[i];
|
||||
var tabName = data.Name;
|
||||
|
||||
// 아이콘이 있으면 탭 이름 앞에 추가
|
||||
if (!string.IsNullOrEmpty(data.Icon))
|
||||
{
|
||||
tabName = $"{data.Icon} {data.Name}";
|
||||
}
|
||||
|
||||
var tab = _tabView.AddUTKTab(tabName);
|
||||
tab.IsEnabled = data.IsEnabled;
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Tooltip))
|
||||
{
|
||||
tab.tooltip = data.Tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
// 이벤트 재구독
|
||||
_tabView.OnTabChanged += OnTabViewTabChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UTKTabView의 선택 상태를 동기화합니다.
|
||||
/// </summary>
|
||||
private void SyncTabViewSelection(int tabIndex)
|
||||
{
|
||||
if (_tabView == null) return;
|
||||
|
||||
int viewIndex;
|
||||
if (_showAllTab)
|
||||
viewIndex = tabIndex == ALL_TAB_INDEX ? 0 : tabIndex + 1;
|
||||
else
|
||||
viewIndex = tabIndex;
|
||||
|
||||
if (viewIndex >= 0 && viewIndex < _tabView.UTKTabs.Count)
|
||||
_tabView.SelectedIndex = viewIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UTKTabView에서 탭 변경 이벤트 발생 시 처리
|
||||
/// </summary>
|
||||
private void OnTabViewTabChanged(int viewIndex, Tab? tab)
|
||||
{
|
||||
int dataIndex;
|
||||
if (_showAllTab)
|
||||
dataIndex = viewIndex == 0 ? ALL_TAB_INDEX : viewIndex - 1;
|
||||
else
|
||||
dataIndex = viewIndex;
|
||||
|
||||
// 이미 선택된 탭이면 무시
|
||||
if (dataIndex == _selectedTabIndex) return;
|
||||
|
||||
SelectTab(dataIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 탭에 해당하는 데이터를 UTKPropertyList에 로드합니다.
|
||||
/// </summary>
|
||||
private void LoadDataForTab(int tabIndex)
|
||||
{
|
||||
if (_propertyList == null) return;
|
||||
|
||||
if (tabIndex == ALL_TAB_INDEX)
|
||||
{
|
||||
LoadAllTabData();
|
||||
}
|
||||
else if (tabIndex >= 0 && tabIndex < _tabDataList.Count)
|
||||
{
|
||||
LoadSingleTabData(_tabDataList[tabIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 단일 탭의 데이터를 UTKPropertyList에 로드합니다.
|
||||
/// </summary>
|
||||
private void LoadSingleTabData(TabPropertyData tabData)
|
||||
{
|
||||
if (_propertyList == null) return;
|
||||
|
||||
switch (tabData.DataType)
|
||||
{
|
||||
case TabPropertyDataType.Flat:
|
||||
var flatItems = tabData.GetFlatData();
|
||||
if (flatItems != null)
|
||||
_propertyList.LoadProperties(flatItems);
|
||||
else
|
||||
_propertyList.Clear();
|
||||
break;
|
||||
|
||||
case TabPropertyDataType.Grouped:
|
||||
var groupedItems = tabData.GetGroupedData();
|
||||
if (groupedItems != null)
|
||||
_propertyList.LoadGroupedProperties(groupedItems);
|
||||
else
|
||||
_propertyList.Clear();
|
||||
break;
|
||||
|
||||
case TabPropertyDataType.Mixed:
|
||||
var mixedItems = tabData.GetMixedData();
|
||||
if (mixedItems != null)
|
||||
_propertyList.LoadMixedProperties(mixedItems);
|
||||
else
|
||||
_propertyList.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 탭의 데이터를 병합하여 UTKPropertyList에 로드합니다.
|
||||
/// 병합 전략: Mixed 방식으로 통합 (IUTKPropertyEntry 리스트로 변환)
|
||||
/// </summary>
|
||||
private void LoadAllTabData()
|
||||
{
|
||||
if (_propertyList == null) return;
|
||||
|
||||
var allEntries = new List<IUTKPropertyEntry>();
|
||||
|
||||
foreach (var tabData in _tabDataList)
|
||||
{
|
||||
switch (tabData.DataType)
|
||||
{
|
||||
case TabPropertyDataType.Flat:
|
||||
var flatItems = tabData.GetFlatData();
|
||||
if (flatItems != null)
|
||||
{
|
||||
foreach (var item in flatItems)
|
||||
allEntries.Add(item);
|
||||
}
|
||||
break;
|
||||
|
||||
case TabPropertyDataType.Grouped:
|
||||
var groupedItems = tabData.GetGroupedData();
|
||||
if (groupedItems != null)
|
||||
{
|
||||
foreach (var group in groupedItems)
|
||||
allEntries.Add(group);
|
||||
}
|
||||
break;
|
||||
|
||||
case TabPropertyDataType.Mixed:
|
||||
var mixedItems = tabData.GetMixedData();
|
||||
if (mixedItems != null)
|
||||
{
|
||||
allEntries.AddRange(mixedItems);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allEntries.Count > 0)
|
||||
_propertyList.LoadMixedProperties(allEntries);
|
||||
else
|
||||
_propertyList.Clear();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods - Search Persistence
|
||||
|
||||
/// <summary>
|
||||
/// 현재 탭의 검색어를 저장합니다.
|
||||
/// </summary>
|
||||
private void SaveCurrentSearchQuery()
|
||||
{
|
||||
if (_propertyList == null) return;
|
||||
|
||||
var currentQuery = _propertyList.SearchQuery;
|
||||
if (!string.IsNullOrEmpty(currentQuery))
|
||||
_tabSearchQueries[_selectedTabIndex] = currentQuery;
|
||||
else
|
||||
_tabSearchQueries.Remove(_selectedTabIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 지정된 탭의 저장된 검색어를 복원합니다.
|
||||
/// </summary>
|
||||
private void RestoreSearchQuery(int tabIndex)
|
||||
{
|
||||
if (_propertyList == null) return;
|
||||
|
||||
if (_tabSearchQueries.TryGetValue(tabIndex, out var savedQuery) && !string.IsNullOrEmpty(savedQuery))
|
||||
_propertyList.ApplySearch(savedQuery);
|
||||
else
|
||||
_propertyList.ApplySearch(string.Empty);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Dragging
|
||||
private void OnHeaderPointerDown(PointerDownEvent evt)
|
||||
{
|
||||
if (evt.button != 0) return;
|
||||
|
||||
_isDragging = true;
|
||||
_dragStartPosition = new Vector2(resolvedStyle.left, resolvedStyle.top);
|
||||
_dragStartMousePosition = evt.position;
|
||||
_header?.CapturePointer(evt.pointerId);
|
||||
}
|
||||
|
||||
private void OnHeaderPointerMove(PointerMoveEvent evt)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
Vector2 delta = (Vector2)evt.position - _dragStartMousePosition;
|
||||
style.left = _dragStartPosition.x + delta.x;
|
||||
style.top = _dragStartPosition.y + delta.y;
|
||||
}
|
||||
|
||||
private void OnHeaderPointerUp(PointerUpEvent evt)
|
||||
{
|
||||
if (!_isDragging) return;
|
||||
|
||||
_isDragging = false;
|
||||
_header?.ReleasePointer(evt.pointerId);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 테마 (Theme)
|
||||
|
||||
private void SubscribeToThemeChanges()
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
RegisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
RegisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
}
|
||||
|
||||
private void OnAttachToPanelForTheme(AttachToPanelEvent evt)
|
||||
{
|
||||
UTKThemeManager.Instance.OnThemeChanged -= OnThemeChanged;
|
||||
UTKThemeManager.Instance.OnThemeChanged += OnThemeChanged;
|
||||
UTKThemeManager.Instance.ApplyThemeToElement(this);
|
||||
}
|
||||
|
||||
private void OnDetachFromPanelForTheme(DetachFromPanelEvent evt)
|
||||
{
|
||||
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;
|
||||
UnregisterCallback<AttachToPanelEvent>(OnAttachToPanelForTheme);
|
||||
UnregisterCallback<DetachFromPanelEvent>(OnDetachFromPanelForTheme);
|
||||
|
||||
// 드래그 이벤트 해제
|
||||
if (_header != null)
|
||||
{
|
||||
_header.UnregisterCallback<PointerDownEvent>(OnHeaderPointerDown);
|
||||
_header.UnregisterCallback<PointerMoveEvent>(OnHeaderPointerMove);
|
||||
_header.UnregisterCallback<PointerUpEvent>(OnHeaderPointerUp);
|
||||
}
|
||||
|
||||
// TabView 이벤트 해제 및 정리
|
||||
if (_tabView != null)
|
||||
{
|
||||
_tabView.OnTabChanged -= OnTabViewTabChanged;
|
||||
_tabView.Dispose();
|
||||
}
|
||||
|
||||
// PropertyList 정리
|
||||
_propertyList?.Dispose();
|
||||
_propertyList = null;
|
||||
|
||||
// 이벤트 정리
|
||||
OnCloseClicked = null;
|
||||
OnTabChanged = null;
|
||||
|
||||
// 데이터 정리
|
||||
_tabDataList.Clear();
|
||||
_tabSearchQueries.Clear();
|
||||
|
||||
// UI 참조 정리
|
||||
_header = null;
|
||||
_titleLabel = null;
|
||||
_closeButton = null;
|
||||
_tabView = null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d88092ac39914443bea15b2fd68853d
|
||||
Reference in New Issue
Block a user