UTKReorableList 개발 중
This commit is contained in:
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) -->
|
||||
<ui:Label 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}
|
||||
56
Assets/Resources/UIToolkit/List/UTKReordableListItemUss.uss
Normal file
56
Assets/Resources/UIToolkit/List/UTKReordableListItemUss.uss
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKReordableListItemUss.uss
|
||||
* 재정렬 가능 리스트 아이템 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Item Container
|
||||
=================================== */
|
||||
|
||||
.reordable-list-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
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
|
||||
47
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss
Normal file
47
Assets/Resources/UIToolkit/List/UTKReordableListUss.uss
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ===================================
|
||||
* UTKReordableListUss.uss
|
||||
* 재정렬 가능 리스트 컨테이너 스타일
|
||||
* ===================================
|
||||
*/
|
||||
|
||||
/* ===================================
|
||||
Base Container
|
||||
=================================== */
|
||||
|
||||
.reordable-list {
|
||||
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 내부 여백 조정
|
||||
=================================== */
|
||||
|
||||
.reordable-list .utk-listview {
|
||||
flex-grow: 1;
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 중 아이템 스타일 (Unity 내장 클래스)
|
||||
=================================== */
|
||||
|
||||
.reordable-list .unity-list-view__reorderable-item__container {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ===================================
|
||||
드래그 핸들 바 (Unity 내장 reorder handle)
|
||||
커스텀 드래그 핸들 사용하므로 기본 숨김
|
||||
=================================== */
|
||||
|
||||
.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
|
||||
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}
|
||||
383
Assets/Scripts/UVC/UIToolkit/List/UTKReordableList.cs
Normal file
383
Assets/Scripts/UVC/UIToolkit/List/UTKReordableList.cs
Normal file
@@ -0,0 +1,383 @@
|
||||
#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.itemIndexChanged += OnItemIndexChanged;
|
||||
|
||||
Add(_listView);
|
||||
}
|
||||
|
||||
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<Label>("drag-handle");
|
||||
if (handle != null)
|
||||
handle.text = 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 Label(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;
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user