UTKShortcutList 개발 완료. Modal 개선

This commit is contained in:
logonkhi
2026-02-24 20:01:56 +09:00
parent b9b394935e
commit 8ca8bd0df9
72 changed files with 3466 additions and 68 deletions

View File

@@ -166,7 +166,7 @@ MonoBehaviour:
m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument
m_PanelSettings: {fileID: 11400000, guid: 5ad7007b08a97b54d927c352279a18b6, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 54e4f33c8b08cb54f97dbdb5edd79e1e, type: 3}
sourceAsset: {fileID: 9197481963319205126, guid: 6c8eae7ee21b96245b325f08111b214b, type: 3}
m_SortingOrder: 1
m_Position: 0
m_WorldSpaceSizeMode: 1
@@ -184,9 +184,9 @@ MonoBehaviour:
m_GameObject: {fileID: 1097328750}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 39265a781c40bdb4a90aa56b0fbf44a6, type: 3}
m_Script: {fileID: 11500000, guid: e88ad13f58976fb4a837242a7e1c8282, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UVC.Sample.UIToolkit.UTKToolBarSample
m_EditorClassIdentifier: Assembly-CSharp::UVC.Sample.UIToolkit.UTKSettingModalSample
_uiDocument: {fileID: 1097328754}
initialTheme: 0
--- !u!1 &1331954412

View File

@@ -5,6 +5,7 @@ using UnityEngine.UIElements;
using UVC.UIToolkit;
using UVC.UI.Commands;
using UVC.Log;
using UVC.Studio.UIToolkit.Modal;
namespace UVC.Sample.UIToolkit
{
@@ -21,6 +22,10 @@ namespace UVC.Sample.UIToolkit
private UTKToggle? _themeToggle;
private VisualElement? _root;
private UTKButton? _openButton0;
private UTKButton? _openButton1;
private UTKButton? _openButton2;
private void Start()
{
@@ -33,7 +38,7 @@ namespace UVC.Sample.UIToolkit
}
_uiDocument = doc;
_root = _uiDocument.rootVisualElement;
UTKModal.SetRoot(_root);
UTKThemeManager.Instance.RegisterRoot(_root);
UTKThemeManager.Instance.SetTheme(initialTheme);
@@ -47,6 +52,39 @@ namespace UVC.Sample.UIToolkit
};
}
_openButton0 = _root.Q<UTKButton>("openButton0");
if (_openButton0 != null) {
_openButton0.OnClicked += async () =>
{
var modal = UTKModal.Create("Settings", UTKModal.ModalSize.Large);
var content = new UTKSettingModalContent(0); // 초기 탭 인덱스 설정
modal.Add(content);
await modal.ShowAsync();
};
}
_openButton1 = _root.Q<UTKButton>("openButton1");
if (_openButton1 != null) {
_openButton1.OnClicked += async () =>
{
var modal = UTKModal.Create("Settings", UTKModal.ModalSize.Large);
var content = new UTKSettingModalContent(1); // 초기 탭 인덱스 설정
modal.Add(content);
await modal.ShowAsync();
};
}
_openButton2 = _root.Q<UTKButton>("openButton2");
if (_openButton2 != null) {
_openButton2.OnClicked += async () =>
{
var modal = UTKModal.Create("Settings", UTKModal.ModalSize.Large);
var content = new UTKSettingModalContent(2); // 초기 탭 인덱스 설정
modal.Add(content);
await modal.ShowAsync();
};
}
}
private void OnDestroy()

View File

@@ -1,6 +1,8 @@
<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;" />
<utk:UTKButton name="openButton0" text="Open0" style="position: absolute; top: 8px; left: 10px; z-index: 10;" />
<utk:UTKButton name="openButton1" text="Open1" style="position: absolute; top: 38px; left: 10px; z-index: 10;" />
<utk:UTKButton name="openButton2" text="Open2" style="position: absolute; top: 68px; left: 10px; z-index: 10;" />
</VisualElement>
</UXML>

View 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: 274352955998a6e478eb57d04c49969b, 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: 85433d2caa763104f86b4b44ded2fc2d, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::UTKShortcutListSample
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}

View File

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

View File

@@ -0,0 +1,213 @@
#nullable enable
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UVC.UIToolkit;
/// <summary>
/// UTKShortcutList의 기능을 테스트하기 위한 샘플 MonoBehaviour입니다.
/// <list type="bullet">
/// <item>Studio 단축키 목록 SetData</item>
/// <item>OnDataChanged 이벤트 핸들링</item>
/// <item>GetData / 리셋 / 항목 추가 버튼</item>
/// <item>라이트/다크 테마 토글</item>
/// </list>
/// </summary>
public class UTKShortcutListSample : MonoBehaviour
{
[SerializeField]
public UIDocument uiDocument;
[SerializeField]
[Tooltip("시작 시 적용할 테마")]
private UTKTheme initialTheme = UTKTheme.Dark;
private UTKToggle? _themeToggle;
private UTKShortcutList? _shortcutList;
// ── 샘플 데이터 원본 (리셋용) ──────────────────────────────────────────
private static readonly List<UTKShortcutItemData> DefaultShortcuts = new()
{
new() { Id = "file.new_project", CommandName = "File > New Project", UseCtrl = true, UseShift = false, UseAlt = false, Key = "N" },
new() { Id = "file.open_project", CommandName = "File > Open Project", UseCtrl = true, UseShift = true, UseAlt = false, Key = "O" },
new() { Id = "file.save_project", CommandName = "File > Save Project", UseCtrl = true, UseShift = false, UseAlt = true, Key = "S" },
new() { Id = "file.save_as", CommandName = "File > Save As...", UseCtrl = true, UseShift = true, UseAlt = true, Key = "S" },
new() { Id = "file.insert_database", CommandName = "File > Insert Database", UseCtrl = true, UseShift = true, UseAlt = false, Key = "I" },
new() { Id = "file.export_layout", CommandName = "File > Export > Layout", UseCtrl = true, UseShift = true, UseAlt = false, Key = "L" },
new() { Id = "file.export_metadata", CommandName = "File > Export > Metadata",UseCtrl = true, UseShift = true, UseAlt = false, Key = "M" },
new() { Id = "file.export_gltf", CommandName = "File > Export > glTF", UseCtrl = true, UseShift = true, UseAlt = false, Key = "G" },
new() { Id = "edit.undo", CommandName = "Edit > Undo", UseCtrl = true, UseShift = true, UseAlt = false, Key = "Z" },
new() { Id = "edit.redo", CommandName = "Edit > Redo", UseCtrl = true, UseShift = true, UseAlt = false, Key = "Y" },
new() { Id = "edit.duplicate", CommandName = "Edit > Duplicate", UseCtrl = true, UseShift = true, UseAlt = false, Key = "D" },
new() { Id = "edit.delete", CommandName = "Edit > Delete", UseCtrl = false, UseShift = true, UseAlt = false, Key = "Delete" },
new() { Id = "create.plane", CommandName = "Create > Plane", UseCtrl = true, UseShift = true, UseAlt = false, Key = "V" },
new() { Id = "tool.select", CommandName = "Select Tool", UseCtrl = false, UseShift = false, UseAlt = false, Key = "1" },
new() { Id = "tool.move", CommandName = "Move Tool", UseCtrl = false, UseShift = false, UseAlt = false, Key = "2" },
new() { Id = "tool.rotate", CommandName = "Rotate Tool", UseCtrl = false, UseShift = false, UseAlt = false, Key = "3" },
new() { Id = "tool.scale", CommandName = "Scale Tool", UseCtrl = false, UseShift = false, UseAlt = false, Key = "4" },
new() { Id = "view.focus", CommandName = "View > Focus Selected", UseCtrl = false, UseShift = false, UseAlt = false, Key = "F" },
new() { Id = "view.toggle_grid", CommandName = "View > Toggle Grid", UseCtrl = false, UseShift = false, UseAlt = false, Key = "G" },
new() { Id = "view.toggle_wireframe", CommandName = "View > Toggle Wireframe", UseCtrl = false, UseShift = false, UseAlt = false, Key = "W" },
};
// ─────────────────────────────────────────────────────────────────────────
private void Start()
{
// UIDocument 참조
var doc = GetComponent<UIDocument>();
if (doc == null)
{
Debug.LogError("[UTKShortcutListSample] UIDocument 컴포넌트가 없습니다.");
return;
}
uiDocument = doc;
var root = uiDocument.rootVisualElement;
// 테마 토글
_themeToggle = root.Q<UTKToggle>("toggle");
if (_themeToggle == null)
Debug.LogWarning("[UTKShortcutListSample] UXML에서 UTKToggle(name='toggle')을 찾을 수 없습니다.");
// ShortcutList
_shortcutList = root.Q<UTKShortcutList>("shortcut-list");
if (_shortcutList == null)
{
// 이름을 못 찾으면 타입으로 검색
_shortcutList = root.Q<UTKShortcutList>();
}
if (_shortcutList == null)
{
Debug.LogError("[UTKShortcutListSample] UXML에서 UTKShortcutList를 찾을 수 없습니다.");
return;
}
// 테마 초기화
UTKThemeManager.Instance.RegisterRoot(root);
UTKThemeManager.Instance.SetTheme(initialTheme);
_themeToggle!.OnValueChanged += (isOn) =>
{
UTKThemeManager.Instance.SetTheme(isOn ? UTKTheme.Light : UTKTheme.Dark);
};
// 이벤트 핸들러
_shortcutList.OnDataChanged += OnShortcutDataChanged;
// 기본 데이터 로드 (DeepCopy 로 원본 보존)
LoadDefaultData();
// 테스트 버튼 생성
CreateButtons(root);
}
// ── 이벤트 핸들러 ─────────────────────────────────────────────────────────
/// <summary>단축키 데이터가 변경될 때 호출됩니다.</summary>
private void OnShortcutDataChanged(UTKShortcutItemData item)
{
Debug.Log($"[UTKShortcutListSample] 변경 → id={item.Id} " +
$"Ctrl={item.UseCtrl} Shift={item.UseShift} Alt={item.UseAlt} Key={item.Key}");
}
// ── 데이터 관리 ───────────────────────────────────────────────────────────
/// <summary>DefaultShortcuts 의 복사본을 리스트에 로드합니다.</summary>
private void LoadDefaultData()
{
var copy = new List<UTKShortcutItemData>(DefaultShortcuts.Count);
foreach (var src in DefaultShortcuts)
{
copy.Add(new UTKShortcutItemData
{
Id = src.Id,
CommandName = src.CommandName,
UseCtrl = src.UseCtrl,
UseShift = src.UseShift,
UseAlt = src.UseAlt,
Key = src.Key,
});
}
_shortcutList?.SetData(copy);
Debug.Log($"[UTKShortcutListSample] {copy.Count}개 단축키 로드 완료");
}
// ── 테스트 버튼 ───────────────────────────────────────────────────────────
private void CreateButtons(VisualElement root)
{
var bar = new VisualElement();
bar.style.flexDirection = FlexDirection.Row;
bar.style.justifyContent = Justify.Center;
bar.style.paddingTop = 8;
bar.style.paddingBottom = 8;
bar.style.flexWrap = Wrap.Wrap;
// GetData 버튼: 현재 데이터를 콘솔에 출력
var getDataBtn = new UTKButton("GetData", variant: UTKButton.ButtonVariant.Primary);
getDataBtn.OnClicked += OnGetDataClicked;
getDataBtn.style.marginRight = 4;
bar.Add(getDataBtn);
// 리셋 버튼: 기본값으로 복원
var resetBtn = new UTKButton("리셋", variant: UTKButton.ButtonVariant.Normal);
resetBtn.OnClicked += LoadDefaultData;
resetBtn.style.marginRight = 4;
bar.Add(resetBtn);
// 항목 추가 버튼: 빈 항목 하나 추가
var addBtn = new UTKButton("항목 추가", variant: UTKButton.ButtonVariant.OutlinePrimary);
addBtn.OnClicked += OnAddItemClicked;
bar.Add(addBtn);
root.Add(bar);
}
/// <summary>현재 최종 데이터를 콘솔에 출력합니다.</summary>
private void OnGetDataClicked()
{
if (_shortcutList == null) return;
var data = _shortcutList.GetData();
Debug.Log($"[UTKShortcutListSample] ── GetData ({data.Count}개) ──");
foreach (var item in data)
{
var mods = $"{(item.UseCtrl ? "Ctrl+" : "")}" +
$"{(item.UseShift ? "Shift+" : "")}" +
$"{(item.UseAlt ? "Alt+" : "")}";
Debug.Log($" [{item.Id}] {item.CommandName} → {mods}{item.Key}");
}
}
/// <summary>빈 항목을 목록 끝에 추가하고 리스트를 갱신합니다.</summary>
private void OnAddItemClicked()
{
if (_shortcutList == null) return;
var current = _shortcutList.GetData();
var newItem = new UTKShortcutItemData
{
Id = $"custom.{current.Count + 1}",
CommandName = $"Custom Command {current.Count + 1}",
UseCtrl = false,
UseShift = false,
UseAlt = false,
Key = "",
};
current.Add(newItem);
_shortcutList.SetData(current);
Debug.Log($"[UTKShortcutListSample] 항목 추가: {newItem.CommandName}");
}
// ─────────────────────────────────────────────────────────────────────────
private void OnDestroy()
{
if (_shortcutList != null)
{
_shortcutList.OnDataChanged -= OnShortcutDataChanged;
_shortcutList.Dispose();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 85433d2caa763104f86b4b44ded2fc2d

View File

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

View File

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

View File

@@ -102,7 +102,11 @@ input.ErrorMessage = """"; // 오류 제거
// 비활성화 / 읽기 전용
input.IsEnabled = false;
input.isReadOnly = true;
input.multiline = true;",
input.multiline = true;
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
input.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -124,6 +128,9 @@ input.multiline = true;",
<!-- 비활성화 -->
<utk:UTKInputField label=""읽기전용"" is-enabled=""false"" value=""수정 불가"" />
<!-- label min-width 설정 -->
<utk:UTKInputField label=""이름"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -200,7 +207,10 @@ ageField.ClearError();
// 5) 에러 메시지 직접 설정 (Validation 없이, 서버 오류 등)
intField.ErrorMessage = ""서버 오류가 발생했습니다."";
intField.ErrorMessage = """"; // 오류 제거",
intField.ErrorMessage = """"; // 오류 제거
// ── Label Min-Width ──────────────────────────
intField.LabelMinWidth = 120f; // label의 min-width 설정",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -213,6 +223,9 @@ intField.ErrorMessage = """"; // 오류 제거",
<!-- 비활성화 -->
<utk:UTKIntegerField label=""읽기전용"" is-enabled=""false"" value=""50"" />
<!-- label min-width 설정 -->
<utk:UTKIntegerField label=""수량"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -276,7 +289,10 @@ sizeField.Validation = () => sizeField.Value >= 0 && sizeField.Value <= 1000000;
bool isValid = sizeField.Validate();
// 3) 에러 수동 해제
sizeField.ClearError();",
sizeField.ClearError();
// ── Label Min-Width ──────────────────────────
longField.LabelMinWidth = 120f; // label의 min-width 설정",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -289,6 +305,9 @@ sizeField.ClearError();",
<!-- 비활성화 -->
<utk:UTKLongField label=""읽기전용"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKLongField label=""ID"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -353,7 +372,10 @@ speedField.Validation = () => speedField.Value >= 0f && speedField.Value <= 100f
bool isValid = speedField.Validate();
// 3) 에러 수동 해제
speedField.ClearError();",
speedField.ClearError();
// ── Label Min-Width ──────────────────────────
floatField.LabelMinWidth = 120f; // label의 min-width 설정",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -366,6 +388,9 @@ speedField.ClearError();",
<!-- 비활성화 -->
<utk:UTKFloatField label=""읽기전용"" is-enabled=""false"" value=""3.14"" />
<!-- label min-width 설정 -->
<utk:UTKFloatField label=""속도"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -430,7 +455,10 @@ percentField.Validation = () => percentField.Value >= 0.0 && percentField.Value
bool isValid = percentField.Validate();
// 3) 에러 수동 해제
percentField.ClearError();",
percentField.ClearError();
// ── Label Min-Width ──────────────────────────
doubleField.LabelMinWidth = 120f; // label의 min-width 설정",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -443,6 +471,9 @@ percentField.ClearError();",
<!-- 비활성화 -->
<utk:UTKDoubleField label=""읽기전용"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKDoubleField label=""정밀도"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -695,7 +726,11 @@ dirField.ErrorMessage = ""방향 벡터는 (0,0)이 될 수 없습니다."";
dirField.Validation = () => dirField.Value != Vector2.zero;
bool isValid = dirField.Validate();
dirField.ClearError();",
dirField.ClearError();
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
positionField.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -708,6 +743,9 @@ dirField.ClearError();",
<!-- 비활성화 -->
<utk:UTKVector2Field label=""ReadOnly"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKVector2Field label=""Position"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -767,7 +805,11 @@ scaleField.ErrorMessage = ""스케일 벡터는 (0,0,0)이 될 수 없습니다.
scaleField.Validation = () => scaleField.Value != Vector3.zero;
bool isValid = scaleField.Validate();
scaleField.ClearError();",
scaleField.ClearError();
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
positionField.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -780,6 +822,9 @@ scaleField.ClearError();",
<!-- 비활성화 -->
<utk:UTKVector3Field label=""Disabled"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKVector3Field label=""Position"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -853,7 +898,11 @@ rgbaField.Validation = () =>
};
bool isValid = rgbaField.Validate();
rgbaField.ClearError();",
rgbaField.ClearError();
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
colorField.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -866,6 +915,9 @@ rgbaField.ClearError();",
<!-- 비활성화 -->
<utk:UTKVector4Field label=""Disabled"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKVector4Field label=""Color"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -927,7 +979,11 @@ viewportField.Validation = () =>
viewportField.Value.width > 0 && viewportField.Value.height > 0;
bool isValid = viewportField.Validate();
viewportField.ClearError();",
viewportField.ClearError();
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
areaField.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -940,6 +996,9 @@ viewportField.ClearError();",
<!-- 비활성화 -->
<utk:UTKRectField label=""Disabled"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKRectField label=""Area"" label-min-width=""120"" />
</ui:UXML>");
}
@@ -1009,7 +1068,11 @@ colliderField.Validation = () =>
};
bool isValid = colliderField.Validate();
colliderField.ClearError();",
colliderField.ClearError();
// ── Label Min-Width ──────────────────────────
// label이 있을 때 .unity-label의 min-width를 설정
boundsField.LabelMinWidth = 120f; // 120px (-1이면 미설정)",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -1022,6 +1085,9 @@ colliderField.ClearError();",
<!-- 비활성화 -->
<utk:UTKBoundsField label=""Disabled"" is-enabled=""false"" />
<!-- label min-width 설정 -->
<utk:UTKBoundsField label=""Bounds"" label-min-width=""120"" />
</ui:UXML>");
}

View File

@@ -456,6 +456,72 @@ treeView.OnItemSelected += (index) => Debug.Log($""선택된 항목: {index}"");
<!-- C#에서 컴럼과 데이터 설정 필요 -->");
}
private void InitializeShortcutListSample(VisualElement root)
{
var shortcutList = root.Q<UTKShortcutList>("shortcut-list-sample");
if (shortcutList != null)
{
shortcutList.SetData(new System.Collections.Generic.List<UTKShortcutItemData>
{
new() { Id = "file.new_project", CommandName = "File > New Project", UseCtrl = true, UseShift = false, UseAlt = false, Key = "N" },
new() { Id = "file.open_project", CommandName = "File > Open Project", UseCtrl = true, UseShift = true, UseAlt = false, Key = "O" },
new() { Id = "file.save_project", CommandName = "File > Save Project", UseCtrl = true, UseShift = false, UseAlt = true, Key = "S" },
new() { Id = "file.save_as", CommandName = "File > Save As...", UseCtrl = true, UseShift = true, UseAlt = true, Key = "S" },
new() { Id = "file.export_layout", CommandName = "File > Export > Layout", UseCtrl = true, UseShift = true, UseAlt = false, Key = "L" },
new() { Id = "file.export_gltf", CommandName = "File > Export > glTF", UseCtrl = true, UseShift = true, UseAlt = false, Key = "G" },
new() { Id = "edit.undo", CommandName = "Edit > Undo", UseCtrl = true, UseShift = true, UseAlt = false, Key = "Z" },
new() { Id = "edit.redo", CommandName = "Edit > Redo", UseCtrl = true, UseShift = true, UseAlt = false, Key = "Y" },
new() { Id = "edit.duplicate", CommandName = "Edit > Duplicate", UseCtrl = true, UseShift = true, UseAlt = false, Key = "D" },
new() { Id = "edit.delete", CommandName = "Edit > Delete", UseCtrl = false, UseShift = true, UseAlt = false, Key = "Delete" },
new() { Id = "create.plane", CommandName = "Create > Plane", UseCtrl = true, UseShift = true, UseAlt = false, Key = "V" },
new() { Id = "tool.select", CommandName = "Select Tool", UseCtrl = false, UseShift = false, UseAlt = false, Key = "1" },
});
shortcutList.OnDataChanged += item =>
Debug.Log($"[StyleGuide] 단축키 변경: {item.CommandName} → {(item.UseCtrl ? "Ctrl+" : "")}{(item.UseShift ? "Shift+" : "")}{(item.UseAlt ? "Alt+" : "")}{item.Key}");
}
SetCodeSamples(root,
csharpCode:
@"// 데이터 생성
var data = new List<UTKShortcutItemData>
{
new() { Id = ""file.new"", CommandName = ""File > New Project"", UseCtrl = true, UseShift = false, UseAlt = false, Key = ""N"" },
new() { Id = ""edit.undo"", CommandName = ""Edit > Undo"", UseCtrl = true, UseShift = true, UseAlt = false, Key = ""Z"" },
new() { Id = ""edit.delete"", CommandName = ""Edit > Delete"", UseCtrl = false, UseShift = true, UseAlt = false, Key = ""Delete"" },
};
// 리스트 생성 & 데이터 설정
var list = new UTKShortcutList();
list.SetData(data);
// 변경 이벤트 (Ctrl/Shift/Alt 체크 또는 Key 캡처 시 발생)
list.OnDataChanged += item =>
{
Debug.Log($""{item.CommandName}: Ctrl={item.UseCtrl} Key={item.Key}"");
};
// Key 캡처 방법:
// 1. Key 필드 클릭 → ""···"" 표시
// 2. 원하는 키 입력 → 자동 저장
// 3. Escape → 취소 (이전 값 복원)
// 현재 데이터 가져오기
var current = list.GetData();
// 업데이트된 데이터 다시 적용
current[0].Key = ""F1"";
list.SetData(current);",
uxmlCode:
@"<!-- 기본 사용 -->
<utk:UTKShortcutList name=""shortcut-list"" />
<!-- 고정 크기 -->
<utk:UTKShortcutList style=""height: 300px;"" />
<!-- 데이터와 이벤트는 C#에서 설정 -->");
}
private void InitializeScrollViewSample(VisualElement root)
{
// Horizontal scroll

View File

@@ -430,7 +430,7 @@ for (int i = 0; i < buttons.Length; i++)
var btnFooter = root.Q<UTKButton>("btn-modal-footer");
btnFooter?.RegisterCallback<ClickEvent>(_ =>
{
var modal = UTKModal.Show("푸터 버튼 모달", UTKModal.ModalSize.Medium);
var modal = UTKModal.Create("푸터 버튼 모달", UTKModal.ModalSize.Medium);
modal.Add(new Label("확인 또는 취소를 선택하세요."));
var confirmBtn = new UTKButton("확인") { Variant = UTKButton.ButtonVariant.Primary };
@@ -449,29 +449,33 @@ for (int i = 0; i < buttons.Length; i++)
modal.AddToFooter(cancelBtn);
modal.AddToFooter(confirmBtn);
modal.Show();
});
var btnNoClose = root.Q<UTKButton>("btn-modal-no-close");
btnNoClose?.RegisterCallback<ClickEvent>(_ =>
{
var modal = UTKModal.Show("닫기 버튼 없음", UTKModal.ModalSize.Small);
var modal = UTKModal.Create("닫기 버튼 없음", UTKModal.ModalSize.Small);
modal.ShowCloseButton = false;
modal.CloseOnBackdropClick = true;
modal.Add(new Label("닫기 버튼이 없습니다.\n배경을 클릭하면 닫힙니다."));
modal.Show();
});
var btnNoBackdrop = root.Q<UTKButton>("btn-modal-no-backdrop");
btnNoBackdrop?.RegisterCallback<ClickEvent>(_ =>
{
var modal = UTKModal.Show("배경 클릭 비활성화", UTKModal.ModalSize.Small);
var modal = UTKModal.Create("배경 클릭 비활성화", UTKModal.ModalSize.Small);
modal.CloseOnBackdropClick = false;
modal.Add(new Label("배경을 클릭해도 닫히지 않습니다.\nX 버튼으로만 닫을 수 있습니다."));
modal.Show();
});
// Custom Content - Form Modal
var btnForm = root.Q<UTKButton>("btn-modal-form");
btnForm?.RegisterCallback<ClickEvent>(_ =>
{
var modal = UTKModal.Show("사용자 정보 입력", UTKModal.ModalSize.Medium);
var modal = UTKModal.Create("사용자 정보 입력", UTKModal.ModalSize.Medium);
var nameField = new UTKInputField("이름");
var emailField = new UTKInputField("이메일");
@@ -490,6 +494,7 @@ for (int i = 0; i < buttons.Length; i++)
modal.AddToFooter(cancelBtn);
modal.AddToFooter(submitBtn);
modal.Show();
});
SetCodeSamples(root,
@@ -500,33 +505,40 @@ UTKModal.SetRoot(rootVisualElement);
// 1. 기본 모달 표시
// ========================================
var modal = UTKModal.Show(""제목"", UTKModal.ModalSize.Medium);
var modal = UTKModal.Create(""제목"", UTKModal.ModalSize.Medium);
modal.Add(new Label(""모달 내용""));
// 닫힘 이벤트
modal.OnClosed += () => Debug.Log(""모달 닫힘"");
// 화면에 표시
modal.Show();
// ========================================
// 2. 모달 크기
// ========================================
// Small
UTKModal.Show(""Small"", UTKModal.ModalSize.Small);
var small = UTKModal.Create(""Small"", UTKModal.ModalSize.Small);
small.Show();
// Medium (기본)
UTKModal.Show(""Medium"", UTKModal.ModalSize.Medium);
var medium = UTKModal.Create(""Medium"", UTKModal.ModalSize.Medium);
medium.Show();
// Large
UTKModal.Show(""Large"", UTKModal.ModalSize.Large);
var large = UTKModal.Create(""Large"", UTKModal.ModalSize.Large);
large.Show();
// FullScreen
UTKModal.Show(""FullScreen"", UTKModal.ModalSize.FullScreen);
var full = UTKModal.Create(""FullScreen"", UTKModal.ModalSize.FullScreen);
full.Show();
// ========================================
// 3. 옵션 설정
// ========================================
var modal = UTKModal.Show(""설정"");
var modal = UTKModal.Create(""설정"");
// 닫기 버튼 숨기기
modal.ShowCloseButton = false;
@@ -537,11 +549,14 @@ modal.CloseOnBackdropClick = false;
// 푸터 숨기기
modal.SetFooterVisible(false);
// 옵션 설정 후 표시
modal.Show();
// ========================================
// 4. 푸터 버튼 추가
// ========================================
var modal = UTKModal.Show(""확인 모달"");
var modal = UTKModal.Create(""확인 모달"");
modal.Add(new Label(""정말 삭제하시겠습니까?""));
var confirmBtn = new UTKButton(""삭제"") { Variant = UTKButton.ButtonVariant.Danger };
@@ -556,12 +571,13 @@ cancelBtn.RegisterCallback<ClickEvent>(_ => modal.Close());
modal.AddToFooter(cancelBtn);
modal.AddToFooter(confirmBtn);
modal.Show();
// ========================================
// 5. 폼 모달
// ========================================
var modal = UTKModal.Show(""사용자 정보"");
var modal = UTKModal.Create(""사용자 정보"");
var nameField = new UTKInputField(""이름"");
var emailField = new UTKInputField(""이메일"");
@@ -575,18 +591,83 @@ submitBtn.RegisterCallback<ClickEvent>(_ =>
modal.Close();
});
modal.AddToFooter(submitBtn);
modal.Show();
// ========================================
// 6. 프로그래밍 방식 닫기
// ========================================
var modal = UTKModal.Show(""타이머 모달"");
var modal = UTKModal.Create(""타이머 모달"");
modal.Add(new Label(""3초 후 자동으로 닫힙니다.""));
modal.ShowCloseButton = false;
modal.CloseOnBackdropClick = false;
modal.Show();
// 3초 후 닫기
modal.schedule.Execute(() => modal.Close()).StartingIn(3000);",
modal.schedule.Execute(() => modal.Close()).StartingIn(3000);
// ========================================
// 7. Async/Await 방식 (닫힐 때까지 대기)
// ========================================
var modal = UTKModal.Create(""설정"");
modal.Add(new Label(""모달 내용""));
var closeBtn = new UTKButton(""닫기"") { Variant = UTKButton.ButtonVariant.Primary };
closeBtn.OnClicked += () => modal.Close();
modal.AddToFooter(closeBtn);
// 모달이 닫힐 때까지 대기
await modal.ShowAsync();
Debug.Log(""모달이 닫혔습니다."");
// Async 폼 예시
var formModal = UTKModal.Create(""사용자 정보"");
var nameField = new UTKInputField(""이름"");
formModal.Add(nameField);
var submitBtn = new UTKButton(""제출"") { Variant = UTKButton.ButtonVariant.Primary };
submitBtn.OnClicked += () => formModal.Close();
formModal.AddToFooter(submitBtn);
await formModal.ShowAsync();
Debug.Log($""입력된 이름: {nameField.value}"");
// ========================================
// 8. IUTKModalContent<T> 결과 반환
// ========================================
// IUTKModalContent<T>를 구현한 콘텐츠 클래스 정의
// public class UserFormContent : VisualElement, IUTKModalContent<UserData>
// {
// private UTKInputField _nameField = new(""이름"");
// private UTKInputField _emailField = new(""이메일"");
//
// public UserFormContent()
// {
// Add(_nameField);
// Add(_emailField);
// }
//
// public UserData? GetResult()
// {
// return new UserData(_nameField.value, _emailField.value);
// }
// }
// 사용
var modal = UTKModal.Create(""사용자 정보"");
var form = new UserFormContent();
modal.Add(form);
var submitBtn = new UTKButton(""제출"") { Variant = UTKButton.ButtonVariant.Primary };
submitBtn.OnClicked += () => modal.Close();
modal.AddToFooter(submitBtn);
// Close() 시 자동으로 form.GetResult() 호출
UserData? result = await modal.ShowAsync<UserData>();
if (result != null)
Debug.Log($""이름: {result.Name}, 이메일: {result.Email}"");",
uxmlCode: @"<!-- 네임스페이스 선언 -->
<ui:UXML xmlns:utk=""UVC.UIToolkit"">
@@ -612,9 +693,10 @@ modal.schedule.Execute(() => modal.Close()).StartingIn(3000);",
{
if (_root == null) return;
var modal = UTKModal.Show($"{size} Modal", size);
var modal = UTKModal.Create($"{size} Modal", size);
modal.Add(new Label($"이것은 {size} 크기의 모달입니다."));
modal.OnClosed += () => Debug.Log($"{size} modal closed");
modal.Show();
}
#endregion

View File

@@ -88,6 +88,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
["UTKMultiColumnTreeView"] = "UIToolkit/Sample/List/UTKMultiColumnTreeViewSample",
["UTKFoldout"] = "UIToolkit/Sample/List/UTKFoldoutSample",
["UTKScrollView"] = "UIToolkit/Sample/List/UTKScrollViewSample",
["UTKShortcutList"] = "UIToolkit/Sample/List/UTKShortcutListSample",
// Card
["UTKCard"] = "UIToolkit/Sample/Card/UTKCardSample",
["UTKPanel"] = "UIToolkit/Sample/Card/UTKPanelSample",
@@ -123,7 +124,7 @@ public partial class UTKStyleGuideSample : MonoBehaviour
["Slider"] = new[] { "UTKSlider", "UTKSliderInt", "UTKMinMaxSlider", "UTKProgressBar" },
["Dropdown"] = new[] { "UTKDropdown", "UTKEnumDropDown", "UTKMultiSelectDropdown" },
["Label"] = new[] { "UTKLabel", "UTKHelpBox" },
["List"] = new[] { "UTKListView", "UTKTreeView", "UTKMultiColumnListView", "UTKMultiColumnTreeView", "UTKFoldout", "UTKScrollView" },
["List"] = new[] { "UTKListView", "UTKTreeView", "UTKMultiColumnListView", "UTKMultiColumnTreeView", "UTKFoldout", "UTKScrollView", "UTKShortcutList" },
["Card"] = new[] { "UTKCard", "UTKPanel" },
["Tab"] = new[] { "UTKTabView" },
["Modal"] = new[] { "UTKAlert", "UTKToast", "UTKTooltip", "UTKNotification", "UTKModal" },
@@ -527,6 +528,9 @@ public partial class UTKStyleGuideSample : MonoBehaviour
case "UTKScrollView":
InitializeScrollViewSample(root);
break;
case "UTKShortcutList":
InitializeShortcutListSample(root);
break;
// Card
case "UTKCard":
InitializeCardSample(root);