From 5712da17d795dd5e0e70446f00a5955632c43c2a Mon Sep 17 00:00:00 2001 From: SOOBEEN HAN Date: Thu, 12 Mar 2026 13:53:08 +0900 Subject: [PATCH] =?UTF-8?q?leftSideBar=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 버튼 클릭 시 열고 닫히는 애니메이션 작동 - 열었을 때 메뉴 상세이름 보이도록 - 메뉴 눌렀을 때 동작은 아직 없는 상태 --- .../Main/Document/EWLKDynamicDocument.uxml | 2 - .../Main/Document/EWLKStaticDocument.uxml | 6 +- .../EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss | 166 +++++++ .../UIToolkit/Main/EWLKNavSideBarUss.uss.meta | 12 + Assets/Scenes/EnglewoodLAB_loading.unity | 45 ++ Assets/Scenes/EnglewoodLAB_main.unity | 294 ++++++++++++ .../Scripts/EnglewoodLAB/EWLKSceneContext.cs | 90 +++- Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs | 104 +++- .../EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs | 446 ++++++++++++++++++ .../UIToolkit/EWLKNavSideBar.cs.meta | 2 + 10 files changed, 1160 insertions(+), 7 deletions(-) create mode 100644 Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss create mode 100644 Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss.meta create mode 100644 Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs create mode 100644 Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs.meta diff --git a/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKDynamicDocument.uxml b/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKDynamicDocument.uxml index 7164703a..fa68c666 100644 --- a/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKDynamicDocument.uxml +++ b/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKDynamicDocument.uxml @@ -1,4 +1,2 @@ - - diff --git a/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKStaticDocument.uxml b/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKStaticDocument.uxml index a71ff2d4..df003696 100644 --- a/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKStaticDocument.uxml +++ b/Assets/Resources/EWLK/UIToolkit/Main/Document/EWLKStaticDocument.uxml @@ -3,7 +3,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" + xmlns:ewlk="UVC.EnglewoodLAB.UIToolkit" xsi:noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" > - + + \ No newline at end of file diff --git a/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss b/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss new file mode 100644 index 00000000..195bdadc --- /dev/null +++ b/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss @@ -0,0 +1,166 @@ +/* ============================================================ + EWLKNavSideBar — 메인 씬 좌측 네비게이션 사이드바 스타일 + 네이비 배경, 아이콘 + 텍스트 레이아웃 + + 수정: color:inherit → 제거 (Unity USS는 inherit 키워드 미지원, + 부모 color가 자식에게 자동 전파됨) + cursor:link → 제거 (Unity USS 미지원) + ============================================================ */ + +/* ── 사이드바 루트 ────────────────────────────────────────── */ +.ewlk-nav-sidebar { + flex-direction: column; + background-color: rgb(26, 34, 64); + overflow: hidden; +} + +/* ── 헤더 ─────────────────────────────────────────────────── */ +.ewlk-nav__header { + flex-direction: row; + align-items: center; + height: 48px; + padding-left: 8px; + padding-right: 4px; + flex-shrink: 0; +} + +.ewlk-nav__logo-icon { + width: 24px; + height: 24px; + background-image: resource("EWLK/Images/logo-white"); + background-size: contain; + flex-shrink: 0; +} + +.ewlk-nav__logo-text { + flex-grow: 1; + color: rgb(255, 255, 255); + font-size: 13px; + -unity-font-style: bold; + overflow: hidden; + white-space: nowrap; + margin-left: 8px; + margin-right: 4px; +} + +.ewlk-nav__close-btn { + width: 28px; + height: 28px; + min-width: 0; + padding: 0; + background-color: rgba(0, 0, 0, 0); + border-width: 0; + border-radius: 4px; + flex-shrink: 0; + align-items: center; + justify-content: center; + color: rgba(255, 255, 255, 0.6); +} + +.ewlk-nav__close-btn:hover { + background-color: rgba(255, 255, 255, 0.1); + color: rgb(255, 255, 255); +} + +/* ── 아이콘 레이블 공통 ───────────────────────────────────── */ +/* color는 부모(버튼)에서 자동 전파되므로 별도 지정 불필요 */ +.ewlk-nav__icon-label { + -unity-text-align: middle-center; + flex-shrink: 0; +} + +/* ── 구분선 ───────────────────────────────────────────────── */ +.ewlk-nav__divider { + height: 1px; + background-color: rgba(255, 255, 255, 0.1); + margin-left: 8px; + margin-right: 8px; + flex-shrink: 0; +} + +/* ── 메뉴 영역 ────────────────────────────────────────────── */ +.ewlk-nav__menu { + flex-grow: 1; + flex-direction: column; + padding-top: 4px; + padding-bottom: 4px; + overflow: hidden; +} + +/* ── 메뉴 항목 (Button) ────────────────────────────────────── */ +.ewlk-nav__item { + flex-direction: row; + align-items: center; + height: 40px; + min-height: 0; + padding-left: 8px; + padding-right: 8px; + padding-top: 0; + padding-bottom: 0; + background-color: rgba(0, 0, 0, 0); + border-width: 0; + border-radius: 0; + color: rgba(255, 255, 255, 0.65); + -unity-text-align: middle-left; + margin: 0; +} + +.ewlk-nav__item:hover { + background-color: rgba(255, 255, 255, 0.08); + color: rgb(255, 255, 255); +} + +.ewlk-nav__item--active { + background-color: rgba(255, 255, 255, 0.14); + color: rgb(255, 255, 255); +} + +.ewlk-nav__item--active:hover { + background-color: rgba(255, 255, 255, 0.18); +} + +/* ── 메뉴 항목 아이콘 / 레이블 ─────────────────────────────── */ +/* color는 부모(.ewlk-nav__item)에서 자동 전파 */ +.ewlk-nav__item-icon { + width: 24px; + height: 24px; + -unity-text-align: middle-center; + flex-shrink: 0; +} + +.ewlk-nav__item-label { + flex-grow: 1; + font-size: 12px; + overflow: hidden; + white-space: nowrap; + margin-left: 8px; + -unity-text-align: middle-left; +} + +/* ── 푸터 ─────────────────────────────────────────────────── */ +.ewlk-nav__footer { + flex-direction: column; + flex-shrink: 0; + padding-top: 4px; + padding-bottom: 4px; +} + +/* ── 토글 버튼 (열기/닫기) ────────────────────────────────── */ +.ewlk-nav__toggle-btn { + flex-direction: row; + align-items: center; + justify-content: center; + height: 40px; + min-height: 0; + padding: 0; + margin: 0; + background-color: rgba(0, 0, 0, 0); + border-width: 0; + border-radius: 0; + color: rgba(255, 255, 255, 0.5); +} + +.ewlk-nav__toggle-btn:hover { + background-color: rgba(255, 255, 255, 0.08); + color: rgb(255, 255, 255); +} diff --git a/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss.meta b/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss.meta new file mode 100644 index 00000000..70cb491c --- /dev/null +++ b/Assets/Resources/EWLK/UIToolkit/Main/EWLKNavSideBarUss.uss.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2773d1950271a4b478656767cf6763d2 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 + unsupportedSelectorAction: 0 diff --git a/Assets/Scenes/EnglewoodLAB_loading.unity b/Assets/Scenes/EnglewoodLAB_loading.unity index 24cf46f5..5bfde428 100644 --- a/Assets/Scenes/EnglewoodLAB_loading.unity +++ b/Assets/Scenes/EnglewoodLAB_loading.unity @@ -279,6 +279,7 @@ GameObject: - component: {fileID: 672992130} - component: {fileID: 672992129} - component: {fileID: 672992128} + - component: {fileID: 672992131} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -360,6 +361,50 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &672992131 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 672992127} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_TaaSettings: + m_Quality: 3 + m_FrameInfluence: 0.1 + m_JitterScale: 1 + m_MipBias: 0 + m_VarianceClampScale: 0.9 + m_ContrastAdaptiveSharpening: 0 + m_Version: 2 --- !u!1 &774244994 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scenes/EnglewoodLAB_main.unity b/Assets/Scenes/EnglewoodLAB_main.unity index 882c6850..0187a1e0 100644 --- a/Assets/Scenes/EnglewoodLAB_main.unity +++ b/Assets/Scenes/EnglewoodLAB_main.unity @@ -129,6 +129,7 @@ GameObject: m_Component: - component: {fileID: 39254822} - component: {fileID: 39254821} + - component: {fileID: 39254823} m_Layer: 0 m_Name: Directional Light m_TagString: Untagged @@ -216,6 +217,134 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &39254823 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 39254820} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData + 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 &557669013 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 557669015} + - component: {fileID: 557669014} + m_Layer: 0 + m_Name: StaticDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &557669014 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 557669013} + 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: 3e76559fee54d2040be8ca19a74c70e3, 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!4 &557669015 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 557669013} + 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 &883201336 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 883201337} + - component: {fileID: 883201338} + m_Layer: 0 + m_Name: SceneMain + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &883201337 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 883201336} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 897.09546, y: 577.4398, z: 32.3774} + 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 &883201338 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 883201336} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 85492bb0e96aa6946b45bd6bd762a556, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKSceneMain --- !u!1 &1079771970 GameObject: m_ObjectHideFlags: 0 @@ -308,9 +437,174 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1306527161 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1306527163} + - component: {fileID: 1306527162} + m_Layer: 0 + m_Name: DynamicDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1306527162 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1306527161} + 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: 0cf46852e8559c8439acec80a2f14fc3, type: 3} + m_SortingOrder: 1000 + m_Position: 0 + m_WorldSpaceSizeMode: 1 + m_WorldSpaceWidth: 1920 + m_WorldSpaceHeight: 1080 + m_PivotReferenceSize: 0 + m_Pivot: 0 + m_WorldSpaceCollider: {fileID: 0} +--- !u!4 &1306527163 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1306527161} + 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 &1664424032 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1664424033} + - component: {fileID: 1664424034} + m_Layer: 0 + m_Name: SceneContext + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1664424033 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1664424032} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 897.09546, y: 577.4398, z: 32.3774} + 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 &1664424034 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1664424032} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7bcd3c6511d97a94d8da2de5051ba292, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::UVC.EnglewoodLAB.EWLKSceneContext + scenePrefabs: [] + autoInjectSceneObjects: 1 + targetObjects: [] + staticUI: {fileID: 557669014} + dynamicUI: {fileID: 1306527162} + modalUI: {fileID: 1840260740} +--- !u!1 &1840260738 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1840260739} + - component: {fileID: 1840260740} + m_Layer: 0 + m_Name: ModalDocument + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1840260739 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1840260738} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 897.09546, y: 577.4398, z: 32.3774} + 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 &1840260740 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1840260738} + 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: 4f0c5b331c9f51f4387dadbbafc03e0b, type: 3} + m_SortingOrder: 2000 + m_Position: 0 + m_WorldSpaceSizeMode: 1 + m_WorldSpaceWidth: 1920 + m_WorldSpaceHeight: 1080 + m_PivotReferenceSize: 0 + m_Pivot: 0 + m_WorldSpaceCollider: {fileID: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 1079771973} - {fileID: 39254822} + - {fileID: 557669015} + - {fileID: 1306527163} + - {fileID: 1840260739} + - {fileID: 883201337} + - {fileID: 1664424033} diff --git a/Assets/Scripts/EnglewoodLAB/EWLKSceneContext.cs b/Assets/Scripts/EnglewoodLAB/EWLKSceneContext.cs index ccd69fa7..6923a8d9 100644 --- a/Assets/Scripts/EnglewoodLAB/EWLKSceneContext.cs +++ b/Assets/Scripts/EnglewoodLAB/EWLKSceneContext.cs @@ -1,6 +1,92 @@ +#nullable enable +using Cysharp.Threading.Tasks; using UnityEngine; +using UnityEngine.UIElements; +using UVC.Core; +using UVC.EnglewoodLAB.UIToolkit; +using UVC.UIToolkit; -public class EWLKSceneContext +namespace UVC.EnglewoodLAB { - + /// UIDocument 타입 충돌 방지용 마커 래퍼 — Modal UI Document + public sealed class ModalUIDocument { public readonly UIDocument Value; public ModalUIDocument(UIDocument v) { Value = v; } } + + /// + /// 메인 씬의 DI 컨텍스트. + /// UIDocument 세 개와 씬 전용 서비스를 등록합니다. + /// + public class EWLKSceneContext : InjectorSceneContext + { + /// EWLKSceneContext 인스턴스에 접근하기 위한 편의 프로퍼티 + public static new EWLKSceneContext? Instance => InjectorSceneContext.Instance as EWLKSceneContext; + + [SerializeField] private UIDocument? staticUI; + [SerializeField] private UIDocument? dynamicUI; + [SerializeField] private UIDocument? modalUI; + + /// + /// 메인 씬 서비스를 등록합니다. + /// 씬 로드 시 자동 호출되며, 씬 전환 시 자동으로 정리됩니다. + /// + protected override void RegisterSceneServices() + { + base.RegisterSceneServices(); + + Injector.RegisterSingleton(); + + if (staticUI != null) + { + staticUI.name = "StaticUI"; + Injector.RegisterInstance(new StaticUIDocument(staticUI), ServiceLifetime.Scene); + } + + if (dynamicUI != null) + { + dynamicUI.name = "DynamicUI"; + Injector.RegisterInstance(new DynamicUIDocument(dynamicUI), ServiceLifetime.Scene); + } + + if (modalUI != null) + { + modalUI.name = "ModalUI"; + Injector.RegisterInstance(new ModalUIDocument(modalUI), ServiceLifetime.Scene); + } + } + + /// + /// 비동기 서비스 등록. + /// UIDocument의 rootVisualElement가 준비된 후 NavSideBar를 추출하고, + /// Modal UI 루트를 UTK 컴포넌트에 설정합니다. + /// RegisterSceneServices()는 Awake 동 프레임에 실행되므로 rootVisualElement가 + /// null일 수 있습니다. 여기서는 한 프레임 대기 후 안전하게 접근합니다. + /// + protected override async UniTask RegisterSceneServicesAsync() + { + await base.RegisterSceneServicesAsync(); + + // UIDocument 패널 초기화 대기 (Awake → Start 이후 준비됨) + await UniTask.WaitUntil( + () => staticUI == null || staticUI.rootVisualElement != null + ); + + // NavSideBar를 UXML에서 추출하여 DI에 등록 + if (staticUI != null) + { + var navSideBar = staticUI.rootVisualElement?.Q("navSideBar"); + if (navSideBar != null) + Injector.RegisterInstance(navSideBar, ServiceLifetime.Scene); + else + Debug.LogWarning("[EWLKSceneContext] StaticUI에서 EWLKNavSideBar(navSideBar)를 찾지 못했습니다."); + } + + // 모달 UI를 사용하는 UTK 컴포넌트에 루트 설정 + if (modalUI?.rootVisualElement != null) + { + UTKAlert.SetRoot(modalUI.rootVisualElement); + UTKModal.SetRoot(modalUI.rootVisualElement); + UTKNotification.SetRoot(modalUI.rootVisualElement); + UTKToast.SetRoot(modalUI.rootVisualElement); + } + } + } } diff --git a/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs b/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs index b99483ce..c8970587 100644 --- a/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs +++ b/Assets/Scripts/EnglewoodLAB/EWLKSceneMain.cs @@ -1,9 +1,109 @@ +#nullable enable +using Cysharp.Threading.Tasks; +using System; using UnityEngine; +using UnityEngine.UIElements; +using UVC.Core; +using UVC.EnglewoodLAB.UIToolkit; +using UVC.UIToolkit; namespace UVC.EnglewoodLAB { - public class EWLKSceneMain + /// + /// 메인 씬의 초기화 컨트롤러. + /// NavSideBar 설정 및 콘텐츠 뷰 전환을 담당합니다. + /// + [DefaultExecutionOrder(90)] + public class EWLKSceneMain : SingletonScene { - + // ── DI 주입 ───────────────────────────────────── + [Inject] private StaticUIDocument? _staticUIDoc; + [Inject] private DynamicUIDocument? _dynamicUIDoc; + [Inject] private ModalUIDocument? _modalUIDoc; + [Inject] private EWLKNavSideBar? _navSideBar; + + // ── UIDocument 편의 접근자 ──────────────────────── + private UIDocument? StaticUI => _staticUIDoc?.Value; + private UIDocument? DynamicUI => _dynamicUIDoc?.Value; + private UIDocument? ModalUI => _modalUIDoc?.Value; + + /// 씬 초기화 완료 이벤트 + public Action? Initialized; + + // ──────────────────────────────────────────────── + + /// + /// SingletonScene Awake 시 호출됩니다. + /// InjectorAppContext 초기화 완료 후 씬을 세팅합니다. + /// + protected override void Init() + { + InitializeAsync() + .Forget(ex => Debug.LogError(ex)); + } + + protected override void OnDestroy() + { + if (_navSideBar != null) + _navSideBar.OnNavItemSelected -= OnNavItemSelected; + } + + // ── 초기화 ────────────────────────────────────── + + /// + /// AppContext DI 완료를 기다린 후 씬을 초기화합니다. + /// InjectorSceneContext.PerformSceneInjection()은 Start()에서 실행되므로 + /// WaitForInitializationAsync()로 [Inject] 필드 주입 완료를 보장합니다. + /// + private async UniTask InitializeAsync() + { + await InjectorAppContext.Instance.WaitForInitializationAsync(); + Initialize(); + } + + private void Initialize() + { + SetupNavSideBar(); + Initialized?.Invoke(); + } + + // ── NavSideBar 설정 ────────────────────────────── + + /// + /// NavSideBar에 메뉴 항목을 설정하고 이벤트를 구독합니다. + /// + private void SetupNavSideBar() + { + if (_navSideBar == null) + { + Debug.LogWarning("[EWLKSceneMain] EWLKNavSideBar가 주입되지 않았습니다. " + + "EWLKSceneContext에서 StaticUI UXML을 확인하세요."); + return; + } + + _navSideBar.SetMenuItems(new[] + { + new EWLKNavSideBar.NavItemData("설비목록", UTKMaterialIcons.List), + new EWLKNavSideBar.NavItemData("OverView", UTKMaterialIcons.BarChart), + new EWLKNavSideBar.NavItemData("제조지시현황", UTKMaterialIcons.Factory), + new EWLKNavSideBar.NavItemData("충포장지시현황", UTKMaterialIcons.Inventory2), + }); + + _navSideBar.OnNavItemSelected += OnNavItemSelected; + _navSideBar.SetActiveItem(0); // 첫 번째 항목 기본 활성화 + } + + // ── 이벤트 처리 ────────────────────────────────── + + /// + /// NavSideBar 항목 선택 시 호출됩니다. + /// 인덱스에 따라 DynamicUI의 콘텐츠 뷰를 전환합니다. + /// + /// 선택된 항목 인덱스 + private void OnNavItemSelected(int index) + { + // TODO: 인덱스별 콘텐츠 뷰(DynamicUI) 전환 구현 예정 + Debug.Log($"[EWLKSceneMain] 메뉴 {index} 선택됨"); + } } } \ No newline at end of file diff --git a/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs b/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs new file mode 100644 index 00000000..20d56dfa --- /dev/null +++ b/Assets/Scripts/EnglewoodLAB/UIToolkit/EWLKNavSideBar.cs @@ -0,0 +1,446 @@ +#nullable enable +using System; +using System.Collections.Generic; +using DG.Tweening; +using UnityEngine; +using UnityEngine.UIElements; +using UVC.UIToolkit; + +namespace UVC.EnglewoodLAB.UIToolkit +{ + /// + /// EnglewoodLAB 메인 씬의 좌측 네비게이션 사이드바. + /// 열기/닫기 애니메이션과 항목 선택 이벤트를 지원합니다. + /// + /// + /// + /// var sidebar = root.Q<EWLKNavSideBar>("navSideBar"); + /// sidebar.SetMenuItems(new[] + /// { + /// new EWLKNavSideBar.NavItemData("설비목록", UTKMaterialIcons.List), + /// new EWLKNavSideBar.NavItemData("OverView", UTKMaterialIcons.BarChart), + /// }); + /// sidebar.OnNavItemSelected += index => Debug.Log($"메뉴 {index} 선택"); + /// + /// + [UxmlElement] + public partial class EWLKNavSideBar : VisualElement, IDisposable + { + #region 상수 (Constants) + + private const string USS_PATH = "EWLK/UIToolkit/Main/EWLKNavSideBarUss"; + private const float ExpandedWidth = 160f; + private const float CollapsedWidth = 40f; + private const float AnimDuration = 0.25f; + + #endregion + + #region 내부 타입 (Inner Types) + + /// 내비게이션 메뉴 항목 데이터 + public sealed class NavItemData + { + /// 표시 이름 + public string Label { get; } + + /// Material Icons 유니코드 문자 (예: UTKMaterialIcons.List) + public string IconChar { get; } + + /// 표시 이름 + /// Material Icons 유니코드 문자 + public NavItemData(string label, string iconChar) + { + Label = label; + IconChar = iconChar; + } + } + + /// + /// 클릭 콜백을 대칭적으로 등록/해제하기 위한 내비게이션 항목 래퍼. + /// RegisterCallback ↔ UnregisterCallback 대칭 유지. + /// + private sealed class NavItemButton : Button, IDisposable + { + private readonly EventCallback _onClick; + private bool _disposed; + + public NavItemButton(EventCallback onClick) + { + _onClick = onClick; + RegisterCallback(_onClick); + } + + public new void Dispose() + { + if (_disposed) return; + _disposed = true; + UnregisterCallback(_onClick); + } + } + + #endregion + + #region 필드 (Fields) + + private bool _disposed; + private bool _isExpanded = true; + private Tweener? _animTween; + + // ── 헤더 요소 ───────────────────────────────── + private Label? _logoText; + private Button? _closeBtn; + private EventCallback? _onCloseBtnClick; + + // ── 메뉴 컨테이너 ───────────────────────────── + private VisualElement? _menuContainer; + + // ── 푸터 요소 ───────────────────────────────── + private Label? _toggleIcon; + private Button? _toggleBtn; + private EventCallback? _onToggleBtnClick; + + // ── 아이템 목록 ──────────────────────────────── + private readonly List _navItemBtns = new(); + private readonly List