From 200a7faa6b1853dfc9acd8902d3a0f20e94df98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=ED=98=95=EC=9D=B8?= Date: Fri, 13 Feb 2026 20:27:31 +0900 Subject: [PATCH] =?UTF-8?q?UTKTopmenu=20=EA=B0=9C=EB=B0=9C=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Resources/UIToolkit/Menu.meta | 8 + .../UIToolkit/Menu/UTKMenuImageItem.uxml | 6 + .../UIToolkit/Menu/UTKMenuImageItem.uxml.meta | 10 + .../UIToolkit/Menu/UTKMenuImageItemUss.uss | 53 + .../Menu/UTKMenuImageItemUss.uss.meta | 11 + .../Resources/UIToolkit/Menu/UTKMenuItem.uxml | 7 + .../UIToolkit/Menu/UTKMenuItem.uxml.meta | 10 + .../UIToolkit/Menu/UTKMenuItemUss.uss | 46 + .../UIToolkit/Menu/UTKMenuItemUss.uss.meta | 11 + .../UIToolkit/Menu/UTKSubMenuItem.uxml | 9 + .../UIToolkit/Menu/UTKSubMenuItem.uxml.meta | 10 + .../UIToolkit/Menu/UTKSubMenuItemUss.uss | 68 + .../UIToolkit/Menu/UTKSubMenuItemUss.uss.meta | 11 + .../Resources/UIToolkit/Menu/UTKTopMenu.uxml | 5 + .../UIToolkit/Menu/UTKTopMenu.uxml.meta | 10 + .../UIToolkit/Menu/UTKTopMenuUss.uss | 25 + .../UIToolkit/Menu/UTKTopMenuUss.uss.meta | 11 + .../UIToolkit/Style/UTKThemeDark.uss | 1 + .../UIToolkit/Style/UTKThemeLight.uss | 1 + Assets/Sample/CursorManagerSample.unity | 15 + Assets/Sample/UIToolkit/UTKMenu.unity | 497 ++++++++ Assets/Sample/UIToolkit/UTKMenu.unity.meta | 7 + Assets/Sample/UIToolkit/UTKMenuSample.cs | 548 ++++++++ Assets/Sample/UIToolkit/UTKMenuSample.cs.meta | 2 + Assets/Sample/UIToolkit/UTKMenuUXML.uxml | 5 + Assets/Sample/UIToolkit/UTKMenuUXML.uxml.meta | 10 + Assets/Scripts/UVC/UIToolkit/Menu.meta | 8 + Assets/Scripts/UVC/UIToolkit/Menu/README.md | 644 ++++++++++ .../Scripts/UVC/UIToolkit/Menu/README.md.meta | 7 + .../UIToolkit/Menu/UTKMenuImageItemData.cs | 93 ++ .../Menu/UTKMenuImageItemData.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKMenuItemBase.cs | 268 ++++ .../UIToolkit/Menu/UTKMenuItemBase.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKMenuItemData.cs | 260 ++++ .../UIToolkit/Menu/UTKMenuItemData.cs.meta | 2 + .../Menu/UTKTopMenuController.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKTopMenuImageItem.cs | 321 +++++ .../Menu/UTKTopMenuImageItem.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKTopMenuItem.cs | 181 +++ .../UVC/UIToolkit/Menu/UTKTopMenuItem.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKTopMenuModel.cs | 314 +++++ .../UIToolkit/Menu/UTKTopMenuModel.cs.meta | 2 + .../UVC/UIToolkit/Menu/UTKTopMenuView.cs | 1120 +++++++++++++++++ .../UVC/UIToolkit/Menu/UTKTopMenuView.cs.meta | 2 + 작업지시서_TopMenu_UIToolkit_마이그레이션.md | 822 ++++++++++++ 45 files changed, 5451 insertions(+) create mode 100644 Assets/Resources/UIToolkit/Menu.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss create mode 100644 Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml create mode 100644 Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss create mode 100644 Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml create mode 100644 Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml.meta create mode 100644 Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss create mode 100644 Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss.meta create mode 100644 Assets/Sample/UIToolkit/UTKMenu.unity create mode 100644 Assets/Sample/UIToolkit/UTKMenu.unity.meta create mode 100644 Assets/Sample/UIToolkit/UTKMenuSample.cs create mode 100644 Assets/Sample/UIToolkit/UTKMenuSample.cs.meta create mode 100644 Assets/Sample/UIToolkit/UTKMenuUXML.uxml create mode 100644 Assets/Sample/UIToolkit/UTKMenuUXML.uxml.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/README.md create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/README.md.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuImageItemData.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuImageItemData.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuItemBase.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuItemBase.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuItemData.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKMenuItemData.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuController.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuImageItem.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuImageItem.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuItem.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuItem.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuModel.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuModel.cs.meta create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuView.cs create mode 100644 Assets/Scripts/UVC/UIToolkit/Menu/UTKTopMenuView.cs.meta create mode 100644 작업지시서_TopMenu_UIToolkit_마이그레이션.md diff --git a/Assets/Resources/UIToolkit/Menu.meta b/Assets/Resources/UIToolkit/Menu.meta new file mode 100644 index 00000000..6390afc7 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7f1d048442c47cc48ad2cb26f6cd97f4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml new file mode 100644 index 00000000..67592e23 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml @@ -0,0 +1,6 @@ + + + + + diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml.meta b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml.meta new file mode 100644 index 00000000..01641602 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItem.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6ffed7426014c8a4bb119007e0bbdae2 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss new file mode 100644 index 00000000..1b18f213 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss @@ -0,0 +1,53 @@ +.menu-item { + padding-left: 0; + padding-right: 0; + width: 40px; + height: 40px; + border-width: 0; + background-color: transparent; + transition-property: background-color; + transition-duration: 0.2s; + flex-direction: row; + align-items: center; + justify-content: center; + margin: 0; +} + +.menu-item:hover { + background-color: var(--color-bg-hover); +} + +.menu-item:active { + background-color: var(--color-bg-active); +} + +.menu-item:disabled { + opacity: 0.5; +} + +.menu-item__icon { + -unity-font-style: normal; + font-size: 20px; + color: var(--color-text-primary); + -unity-text-align: middle-center; + flex-shrink: 0; +} + +.menu-item__image { + width: 20px; + height: 20px; +} + +.menu-item__arrow { + width: 0; + height: 0; + margin-left: 4px; + border-left-width: 4px; + border-right-width: 4px; + border-top-width: 4px; + border-bottom-width: 4px; + border-left-color: transparent; + border-right-color: transparent; + border-top-color: var(--color-text-primary); + border-bottom-color: transparent; +} diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss.meta b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss.meta new file mode 100644 index 00000000..172d3c45 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuImageItemUss.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62cac8dfd8f41d14b8bf1195de5e2cc8 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml b/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml new file mode 100644 index 00000000..1ba90cc0 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml @@ -0,0 +1,7 @@ + + + + + diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml.meta b/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml.meta new file mode 100644 index 00000000..82603fb9 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuItem.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 18625630452b37a468a7deb385187c05 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss b/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss new file mode 100644 index 00000000..4e0d3371 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss @@ -0,0 +1,46 @@ +.menu-item { + padding-left: 12px; + padding-right: 12px; + height: 40px; + border-width: 0; + background-color: transparent; + transition-property: background-color; + transition-duration: 0.2s; + flex-direction: row; + align-items: center; + justify-content: flex-start; + margin: 0; +} + +.menu-item:hover { + background-color: var(--color-bg-hover); +} + +.menu-item:active { + background-color: var(--color-bg-active); +} + +.menu-item:disabled { + opacity: 0.5; +} + +.menu-item__label { + -unity-font-style: normal; + font-size: 14px; + color: var(--color-text-primary); + -unity-text-align: middle-left; +} + +.menu-item__arrow { + width: 0; + height: 0; + margin-left: 4px; + border-left-width: 4px; + border-right-width: 4px; + border-top-width: 4px; + border-bottom-width: 4px; + border-left-color: transparent; + border-right-color: transparent; + border-top-color: var(--color-text-primary); + border-bottom-color: transparent; +} diff --git a/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss.meta b/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss.meta new file mode 100644 index 00000000..1a35988c --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKMenuItemUss.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6871fdccc61fab047bc39781b65546ac +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml new file mode 100644 index 00000000..6b584606 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml.meta b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml.meta new file mode 100644 index 00000000..033d1273 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItem.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fd7d2115694c74b4ea118c851352ecb4 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss new file mode 100644 index 00000000..0d866ce8 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss @@ -0,0 +1,68 @@ +.submenu-item { + flex-direction: row; + justify-content: space-between; + align-items: center; + padding-left: 16px; + padding-right: 16px; + padding-top: 8px; + padding-bottom: 8px; + min-width: 200px; + height: 32px; + border-width: 0; + background-color: var(--color-bg-primary); + margin: 0; +} + +.submenu-item:hover { + background-color: var(--color-bg-hover); +} + +.submenu-item:active { + background-color: var(--color-bg-active); +} + +.submenu-item:disabled { + opacity: 0.5; +} + +.submenu-item__label { + font-size: 14px; + color: var(--color-text-primary); + -unity-text-align: middle-left; + flex-shrink: 0; +} + +.submenu-item__spacer { + flex-grow: 1; + min-width: 24px; +} + +.submenu-item__shortcut { + font-size: 12px; + color: var(--color-text-secondary); + -unity-text-align: middle-right; + flex-shrink: 0; +} + +.submenu-item__arrow { + width: 0; + height: 0; + margin-left: 8px; + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; + border-bottom-width: 5px; + border-left-color: var(--color-text-primary); + border-right-color: transparent; + border-top-color: transparent; + border-bottom-color: transparent; + flex-shrink: 0; +} + +/* 구분선 스타일 */ +.submenu-separator { + height: 1px; + background-color: var(--color-border); + margin-top: 4px; + margin-bottom: 4px; +} diff --git a/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss.meta b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss.meta new file mode 100644 index 00000000..e227c601 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKSubMenuItemUss.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf6082681da1aa34a84eb0ee95c92062 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml b/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml new file mode 100644 index 00000000..5115ee81 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml @@ -0,0 +1,5 @@ + + + + + diff --git a/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml.meta b/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml.meta new file mode 100644 index 00000000..1be112fc --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKTopMenu.uxml.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 22005f651aee4e2449797387a6890b5a +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss b/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss new file mode 100644 index 00000000..fe7b6884 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss @@ -0,0 +1,25 @@ +.top-menu { + position: relative; + flex-direction: column; + width: 100%; + height: 100%; + background-color: transparent; +} + +.top-menu__items { + flex-direction: row; + height: 40px; + background-color: var(--color-bg-primary); + border-bottom-width: 1px; + border-bottom-color: var(--color-border); +} + +/* 하위 메뉴 컨테이너 스타일 */ +.submenu-container { + position: absolute; + background-color: var(--color-bg-primary); + border-width: 1px; + border-color: var(--color-border); + min-width: 200px; + padding: 4px 0; +} diff --git a/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss.meta b/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss.meta new file mode 100644 index 00000000..9ec205e5 --- /dev/null +++ b/Assets/Resources/UIToolkit/Menu/UTKTopMenuUss.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61573ee2ffda4e840b52e48e36d21206 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Assets/Resources/UIToolkit/Style/UTKThemeDark.uss b/Assets/Resources/UIToolkit/Style/UTKThemeDark.uss index ff4be6ae..436e0a72 100644 --- a/Assets/Resources/UIToolkit/Style/UTKThemeDark.uss +++ b/Assets/Resources/UIToolkit/Style/UTKThemeDark.uss @@ -128,6 +128,7 @@ Semantic Colors - Background (color-bg-*) =================================== */ --color-bg-base: var(--color-base-20); + --color-bg-primary: var(--color-base-20); --color-bg-secondary: var(--color-base-19); --color-bg-tertiary: var(--color-base-13); --color-bg-elevated: var(--color-base-18); diff --git a/Assets/Resources/UIToolkit/Style/UTKThemeLight.uss b/Assets/Resources/UIToolkit/Style/UTKThemeLight.uss index 7c0ba94c..cf5b6f0f 100644 --- a/Assets/Resources/UIToolkit/Style/UTKThemeLight.uss +++ b/Assets/Resources/UIToolkit/Style/UTKThemeLight.uss @@ -139,6 +139,7 @@ 라이트 테마에서는 밝은 배경 사용 =================================== */ --color-bg-base: var(--color-base-01); + --color-bg-primary: var(--color-base-01); --color-bg-secondary: var(--color-base-03); --color-bg-tertiary: var(--color-base-06); --color-bg-elevated: var(--color-base-02); diff --git a/Assets/Sample/CursorManagerSample.unity b/Assets/Sample/CursorManagerSample.unity index 3f61b76a..b5605904 100644 --- a/Assets/Sample/CursorManagerSample.unity +++ b/Assets/Sample/CursorManagerSample.unity @@ -290,6 +290,21 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 1bbeb3b46a029fe4896da3350401b214, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::CursorManagerSample + _buttonWidth: 150 + _buttonHeight: 30 + _padding: 10 + _cursorManager: {fileID: 1239580076} +--- !u!114 &1239580076 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 4705914027077067912, guid: 42ed16ae6a9f96d4f9cd6c292a9edd85, type: 3} + m_PrefabInstance: {fileID: 1339613670} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 39e7217342f0a104a985deeef6a5dc4a, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1001 &1339613670 PrefabInstance: m_ObjectHideFlags: 0 diff --git a/Assets/Sample/UIToolkit/UTKMenu.unity b/Assets/Sample/UIToolkit/UTKMenu.unity new file mode 100644 index 00000000..c97d8d30 --- /dev/null +++ b/Assets/Sample/UIToolkit/UTKMenu.unity @@ -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: ea6c8ab095077eb4889ac1b615a0b324, 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: c2be721e7a021bf49b462c82e1f4892c, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::UVC.Sample.UIToolkit.UTKMenuSample + _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} diff --git a/Assets/Sample/UIToolkit/UTKMenu.unity.meta b/Assets/Sample/UIToolkit/UTKMenu.unity.meta new file mode 100644 index 00000000..57ac2313 --- /dev/null +++ b/Assets/Sample/UIToolkit/UTKMenu.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 78506514708184a4893eba450c0b865a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sample/UIToolkit/UTKMenuSample.cs b/Assets/Sample/UIToolkit/UTKMenuSample.cs new file mode 100644 index 00000000..57a226c4 --- /dev/null +++ b/Assets/Sample/UIToolkit/UTKMenuSample.cs @@ -0,0 +1,548 @@ +#nullable enable +using System; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UIElements; +using UVC.UIToolkit; +using UVC.UIToolkit.Menu; +using UVC.UI.Commands; +using UVC.Log; + +namespace UVC.Sample.UIToolkit +{ + /// + /// UTKMenu 샘플 코드 + /// UIToolkit 기반 TopMenu 시스템을 테스트합니다. + /// + public class UTKMenuSample : MonoBehaviour + { + [SerializeField] private UIDocument? _uiDocument; + + [SerializeField] + [Tooltip("시작 시 적용할 테마")] + private UTKTheme initialTheme = UTKTheme.Dark; + + private UTKToggle? _themeToggle; + private VisualElement? _root; + private UTKTopMenuView? _menuView; + private UTKTopMenuModel? _menuModel; + + private UTKTopMenuView? _menuView2; + private UTKTopMenuModel? _menuModel2; + + // 상태 관리용 + private bool _canUndo = false; + private bool _canRedo = false; + private bool _isFileOpen = false; + + + private void Start() + { + // UIDocument 참조 확인 + var doc = GetComponent(); + if (doc == null) + { + Debug.LogError("UIDocument가 할당되지 않았습니다."); + return; + } + _uiDocument = doc; + + var toggle = _uiDocument.rootVisualElement.Q("toggle"); + if (toggle == null) + { + Debug.LogError("UXML에서 UTKToggle을 찾을 수 없습니다."); + return; + } + _themeToggle = toggle; + + UTKThemeManager.Instance.RegisterRoot(_uiDocument.rootVisualElement); + UTKThemeManager.Instance.SetTheme(initialTheme); + + _themeToggle.OnValueChanged += (isOn) => + { + UTKThemeManager.Instance.SetTheme(!isOn ? UTKTheme.Dark : UTKTheme.Light); + }; + + _root = _uiDocument.rootVisualElement; + CreateSampleUI(); + } + + private void CreateSampleUI() + { + if (_root == null) return; + + // 1. UTKTopMenuView 생성 + _menuView = new UTKTopMenuView(); + _menuView.style.position = Position.Absolute; + _menuView.style.top = 0; + _menuView.style.left = 50; + + _menuView2 = new UTKTopMenuView(); + _menuView2.style.position = Position.Absolute; + _menuView2.style.top = 0; + _menuView2.style.left = 0; + _menuView2.SubMenuOffsetX = 30; + _root.Add(_menuView2); + _root.Add(_menuView); + + // 2. UTKTopMenuModel 생성 및 메뉴 아이템 추가 + _menuModel = new UTKTopMenuModel(); + _menuModel2 = new UTKTopMenuModel(); + + CreateMenuItems(); + + // 3. View에 메뉴 생성 + if (_menuView.MenuContainer != null) + { + _menuView.CreateMenuItems(_menuModel.MenuItems, _menuView.MenuContainer); + } + + if (_menuView2.MenuContainer != null) + { + _menuView2.CreateMenuItems(_menuModel2.MenuItems, _menuView2.MenuContainer); + } + + // 4. 이벤트 구독 + _menuView.OnMenuItemClicked += HandleMenuItemClicked; + _menuView2.OnMenuItemClicked += HandleMenuItemClicked2; + + // 5. 상태 테스트 버튼 생성 + CreateTestButtons(); + + ULog.Debug("UTKTopMenu 샘플 UI 생성 완료"); + } + + /// + /// 메뉴 아이템들을 생성합니다. + /// + private void CreateMenuItems() + { + if (_menuModel == null || _menuModel2 == null) return; + + // 파일 메뉴 + var fileMenu = new UTKMenuItemData("file", "파일"); + fileMenu.AddSubMenuItem(new UTKMenuItemData( + "file_new", + "새 파일", + new DebugLogCommand("새 파일 생성"), + shortcut: "Ctrl+N" + )); + fileMenu.AddSubMenuItem(new UTKMenuItemData( + "file_open", + "열기", + new DebugLogCommand("파일 열기"), + shortcut: "Ctrl+O" + )); + fileMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator()); + fileMenu.AddSubMenuItem(new UTKMenuItemData( + "file_save", + "저장", + new DebugLogCommand("파일 저장"), + isEnabled: false, // 초기에는 비활성화 + shortcut: "Ctrl+S" + )); + fileMenu.AddSubMenuItem(new UTKMenuItemData( + "file_save_as", + "다른 이름으로 저장", + new DebugLogCommand("다른 이름으로 저장"), + isEnabled: false, + shortcut: "Ctrl+Shift+S" + )); + fileMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator()); + fileMenu.AddSubMenuItem(new UTKMenuItemData( + "file_exit", + "종료", + new DebugLogCommand("애플리케이션 종료"), + shortcut: "Alt+F4" + )); + _menuModel.AddMenuItem(fileMenu); + + // 편집 메뉴 + var editMenu = new UTKMenuItemData("edit", "편집"); + editMenu.AddSubMenuItem(new UTKMenuItemData( + "edit_undo", + "실행 취소", + new DebugLogCommand("실행 취소"), + isEnabled: false, + shortcut: "Ctrl+Z" + )); + editMenu.AddSubMenuItem(new UTKMenuItemData( + "edit_redo", + "다시 실행", + new DebugLogCommand("다시 실행"), + isEnabled: false, + shortcut: "Ctrl+Y" + )); + editMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator()); + editMenu.AddSubMenuItem(new UTKMenuItemData( + "edit_cut", + "잘라내기", + new DebugLogCommand("잘라내기"), + shortcut: "Ctrl+X" + )); + editMenu.AddSubMenuItem(new UTKMenuItemData( + "edit_copy", + "복사", + new DebugLogCommand("복사"), + shortcut: "Ctrl+C" + )); + editMenu.AddSubMenuItem(new UTKMenuItemData( + "edit_paste", + "붙여넣기", + new DebugLogCommand("붙여넣기"), + shortcut: "Ctrl+V" + )); + _menuModel.AddMenuItem(editMenu); + + // 보기 메뉴 (하위 메뉴 테스트) + var viewMenu = new UTKMenuItemData("view", "보기"); + + var layoutMenu = new UTKMenuItemData("view_layout", "레이아웃"); + layoutMenu.AddSubMenuItem(new UTKMenuItemData("view_layout_default", "기본", new DebugLogCommand("기본 레이아웃"))); + layoutMenu.AddSubMenuItem(new UTKMenuItemData("view_layout_wide", "와이드", new DebugLogCommand("와이드 레이아웃"))); + layoutMenu.AddSubMenuItem(new UTKMenuItemData("view_layout_compact", "컴팩트", new DebugLogCommand("컴팩트 레이아웃"))); + + viewMenu.AddSubMenuItem(layoutMenu); + viewMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator()); + viewMenu.AddSubMenuItem(new UTKMenuItemData("view_fullscreen", "전체 화면", new DebugLogCommand("전체 화면 전환"), shortcut: "F11")); + _menuModel.AddMenuItem(viewMenu); + + // 도구 메뉴 + var toolsMenu = new UTKMenuItemData("tools", "도구"); + toolsMenu.AddSubMenuItem(new UTKMenuItemData("tools_options", "옵션", new DebugLogCommand("옵션 열기"))); + toolsMenu.AddSubMenuItem(new UTKMenuItemData("tools_settings", "설정", new DebugLogCommand("설정 열기"))); + _menuModel.AddMenuItem(toolsMenu); + + // 도움말 메뉴 + var helpMenu = new UTKMenuItemData("help", "도움말"); + helpMenu.AddSubMenuItem(new UTKMenuItemData("help_documentation", "문서", new DebugLogCommand("문서 열기"), shortcut: "F1")); + helpMenu.AddSubMenuItem(UTKMenuItemData.CreateSeparator()); + helpMenu.AddSubMenuItem(new UTKMenuItemData("help_about", "정보", new DebugLogCommand("정보 표시"))); + _menuModel.AddMenuItem(helpMenu); + + // 이미지 메뉴 (4 depth 테스트) + var imageMenu = new UTKMenuImageItemData( + "image_menu", + UTKMaterialIcons.Settings, // Material Icon 사용 + useMaterialIcon: true, + imageSize: 24f + ); + + // Depth 1 아이템 + var depth1Item = new UTKMenuItemData("depth1", "레벨 1 메뉴"); + var depth1Item1 = new UTKMenuItemData("depth11", "레벨 1 1메뉴"); + var depth1Item2 = new UTKMenuItemData("depth12", "레벨 1 2메뉴"); + var depth1Item3 = new UTKMenuItemData("depth13", "레벨 1 3메뉴"); + + // Depth 2 아이템 + var depth2Item = new UTKMenuItemData("depth2", "레벨 2 메뉴"); + var depth2Item1 = new UTKMenuItemData("depth21", "레벨 2 1 메뉴"); + var depth2Item2 = new UTKMenuItemData("depth22", "레벨 2 2 메뉴"); + var depth2Item3 = new UTKMenuItemData("depth23", "레벨 2 3 메뉴"); + + // Depth 3 아이템 + var depth3Item = new UTKMenuItemData("depth3", "레벨 3 메뉴"); + + // Depth 4 아이템들 (실제 액션) + var depth4Action1 = new UTKMenuItemData("depth4_action1", "액션 1", new DebugLogCommand("4 Depth 액션 1 실행")); + var depth4Action2 = new UTKMenuItemData("depth4_action2", "액션 2", new DebugLogCommand("4 Depth 액션 2 실행")); + var depth4Action3 = new UTKMenuItemData("depth4_action3", "액션 3", new DebugLogCommand("4 Depth 액션 3 실행")); + + // 계층 구조 구성 (역순으로) + depth3Item.AddSubMenuItem(depth4Action1); + depth3Item.AddSubMenuItem(depth4Action2); + depth3Item.AddSubMenuItem(depth4Action3); + depth2Item.AddSubMenuItem(depth3Item); + depth1Item.AddSubMenuItem(depth2Item); + depth1Item.AddSubMenuItem(depth2Item1); + depth1Item.AddSubMenuItem(depth2Item2); + depth1Item.AddSubMenuItem(depth2Item3); + imageMenu.AddSubMenuItem(depth1Item1); + imageMenu.AddSubMenuItem(depth1Item2); + imageMenu.AddSubMenuItem(depth1Item3); + imageMenu.AddSubMenuItem(depth1Item); + + _menuModel2.AddMenuItem(imageMenu); + + ULog.Debug($"메뉴 아이템 생성 완료: {_menuModel.MenuItems.Count}개 최상위 메뉴"); + } + + /// + /// 테스트 버튼들을 생성합니다. + /// + private void CreateTestButtons() + { + if (_root == null) return; + + // 테스트 버튼 컨테이너 + var buttonContainer = new VisualElement(); + buttonContainer.style.position = Position.Absolute; + buttonContainer.style.top = 60; + buttonContainer.style.left = 10; + buttonContainer.style.flexDirection = FlexDirection.Column; + buttonContainer.style.width = 250; + _root.Insert(0, buttonContainer); + + // 타이틀 + var title = new Label("메뉴 상태 테스트"); + title.style.fontSize = 16; + title.style.unityFontStyleAndWeight = FontStyle.Bold; + title.style.marginBottom = 10; + buttonContainer.Add(title); + + // 파일 열기/닫기 버튼 + var fileToggleBtn = new Button(() => ToggleFileOpen()) + { + text = "파일 열기 (Save 활성화)" + }; + fileToggleBtn.style.marginBottom = 5; + buttonContainer.Add(fileToggleBtn); + + // Undo/Redo 토글 버튼 + var undoToggleBtn = new Button(() => ToggleUndoRedo()) + { + text = "Undo/Redo 활성화" + }; + undoToggleBtn.style.marginBottom = 5; + buttonContainer.Add(undoToggleBtn); + + // 모든 하위 메뉴 닫기 + var closeAllBtn = new Button(() => _menuView?.CloseAllOpenSubMenus()) + { + text = "모든 하위 메뉴 닫기" + }; + closeAllBtn.style.marginBottom = 5; + buttonContainer.Add(closeAllBtn); + + // 메뉴 아이템 추가 + var addMenuBtn = new Button(() => AddDynamicMenu()) + { + text = "동적 메뉴 추가" + }; + addMenuBtn.style.marginBottom = 5; + buttonContainer.Add(addMenuBtn); + + // 메뉴 아이템 제거 + var removeMenuBtn = new Button(() => RemoveDynamicMenu()) + { + text = "동적 메뉴 제거" + }; + removeMenuBtn.style.marginBottom = 5; + buttonContainer.Add(removeMenuBtn); + + // 단축키 변경 + var changeShortcutBtn = new Button(() => ChangeShortcut()) + { + text = "Save 단축키 변경" + }; + buttonContainer.Add(changeShortcutBtn); + + // 상태 표시 레이블 + var statusLabel = new Label(); + statusLabel.style.marginTop = 20; + statusLabel.style.fontSize = 12; + statusLabel.style.color = new Color(0.7f, 0.7f, 0.7f); + buttonContainer.Add(statusLabel); + + UpdateStatusLabel(statusLabel); + } + + /// + /// 파일 열기/닫기 상태를 토글합니다. + /// + private void ToggleFileOpen() + { + _isFileOpen = !_isFileOpen; + + var saveItem = _menuModel?.FindMenuItem("file_save"); + var saveAsItem = _menuModel?.FindMenuItem("file_save_as"); + + if (saveItem != null) + { + saveItem.IsEnabled = _isFileOpen; + if (_menuView != null && _menuView.TryGetMenuItemElement("file_save", out var element)) + { + var button = element.Q