From 08968b2df285509ac51c3036f26c7eb65e9be114 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 30 Apr 2026 20:23:23 +0800 Subject: [PATCH] refactor editor product architecture --- ...ditor-authoritative-state-refactor-plan.md | 870 ----------------- ...ture-colocation-framework-refactor-plan.md | 181 ++++ ...indowing-runtime-boundary-refactor-plan.md | 171 ++++ editor/CMakeLists.txt | 214 ++-- editor/app/Bootstrap/Application.cpp | 323 ++----- editor/app/Bootstrap/Application.h | 33 +- .../app/Bootstrap/EditorCompositionRoot.cpp | 231 +++++ editor/app/Bootstrap/EditorCompositionRoot.h | 82 ++ editor/app/{ => Bootstrap}/main.cpp | 0 editor/app/Composition/EditorContext.cpp | 177 ---- editor/app/Composition/EditorContext.h | 73 -- .../Composition/EditorFrameStatusController.h | 45 - .../EditorShellDefinitionService.cpp | 98 -- .../EditorShellHostedPanelCoordinator.cpp | 36 - .../EditorShellHostedPanelCoordinator.h | 35 - .../EditorShellSessionCoordinator.cpp | 28 - .../EditorShellSessionCoordinator.h | 33 - .../EditorUtilityWindowRegistry.cpp | 25 - .../Composition/EditorUtilityWindowRegistry.h | 15 - .../EditorWorkspacePanelRegistry.cpp | 763 --------------- .../EditorWorkspacePanelRegistry.h | 36 - editor/app/Core/Panels/EditorPanelIds.h | 23 - .../Core/Product/EditorProductManifest.cpp | 184 ---- .../app/Core/Product/EditorProductManifest.h | 65 -- .../EditorUtilityWindowRegistry.cpp | 55 -- .../EditorUtilityWindowRegistry.h | 10 - .../Contracts/EditorFrameStatusService.h | 35 - .../Contracts/EditorFrameValidation.h | 15 - .../Contracts/EditorShellDefinitionProvider.h | 23 - .../app/Core/Windowing/EditorFrameContracts.h | 5 - .../EditorWorkspacePanelRuntime.cpp | 205 ---- .../EditorWorkspacePanelRuntime.h | 165 ---- .../Interfaces/EditorWindowHostCoordinator.h | 2 +- .../Interfaces/EditorWindowHostInterfaces.h | 8 +- .../Host/Interfaces/EditorWindowHostTypes.h | 2 +- .../Chrome/EditorWindowChromeController.cpp | 4 +- .../app/Host/Win32/Windowing/EditorWindow.cpp | 4 +- .../app/Host/Win32/Windowing/EditorWindow.h | 4 - .../Windowing/EditorWindowMessageDispatcher.h | 2 +- .../Win32/Windowing/EditorWindowSession.h | 2 +- .../Host/Win32/Windowing/EditorWindowState.h | 2 +- .../Win32/Windowing/EditorWindowSupport.h | 2 +- .../Viewport/ViewportObjectPickerService.h | 3 - .../Runtime/EditorRuntimeCoordinator.cpp | 592 ------------ .../Runtime/EditorRuntimeCoordinator.h | 101 -- .../Content/EditorWindowContentController.cpp | 36 - .../EditorUtilityWindowCoordinator.cpp | 154 --- .../EditorUtilityWindowCoordinator.h | 45 - .../EditorWindowLifecycleCoordinator.cpp | 184 ---- .../EditorWindowLifecycleCoordinator.h | 33 - .../EditorWindowWorkspaceCoordinator.cpp | 912 ------------------ .../EditorWindowWorkspaceCoordinator.h | 132 --- editor/app/Windowing/EditorWindowManager.cpp | 348 ------- .../Windowing/System/EditorWindowSystem.h | 65 +- .../Workspace/UIEditorWorkspaceController.h | 28 +- editor/src/Product/Commands/EditorCommand.cpp | 78 ++ editor/src/Product/Commands/EditorCommand.h | 41 + .../Commands/EditorHostCommandBridge.cpp | 2 +- .../Commands/EditorHostCommandBridge.h | 4 +- .../Product}/Core/Assets/EditorIconService.h | 0 .../Core/Commands/EditorEditCommandRoute.h | 0 .../Core/Engine/EditorEngineLifecycle.h | 0 .../Core/Engine/EditorSceneBackendFactory.h | 2 +- .../Core/Engine/EditorShaderProvider.h | 0 .../Core/Engine/GameViewportEngineBridge.h | 0 .../Core/Engine/SceneViewportEngineBridge.h | 2 +- .../Core/Environment/EditorRuntimePaths.h | 0 .../Product}/Core/Scene/EditorSceneBackend.h | 0 .../Core/Scene/SceneViewportRenderRequest.h | 2 +- .../Core/Viewport/EditorViewportPicking.h | 0 .../Viewport/EditorViewportRuntimeServices.h | 8 +- .../Core/Viewport/EditorViewportTypes.h | 0 .../Core/Windowing/EditorShellVariant.h | 0 .../Core/Windowing/EditorWindowGeometry.h | 0 .../Core/Windowing/EditorWindowMetrics.h | 0 .../Windowing/EditorWindowTransferRequests.h | 4 +- .../Core/Windowing/EditorWindowTypes.h | 0 editor/src/Product/Effects/EditorEffect.cpp | 17 + editor/src/Product/Effects/EditorEffect.h | 24 + .../AddComponent}/AddComponentPanel.cpp | 8 +- .../Utility/AddComponent}/AddComponentPanel.h | 6 +- .../AddComponentUtilityFeature.cpp | 47 + .../AddComponent/AddComponentUtilityFeature.h | 9 + .../Utility}/ColorPicker/ColorPickerPanel.cpp | 4 +- .../Utility}/ColorPicker/ColorPickerPanel.h | 4 +- .../ColorPicker/ColorPickerUtilityFeature.cpp | 47 + .../ColorPicker/ColorPickerUtilityFeature.h | 9 + .../Workspace}/Console/ConsolePanel.cpp | 2 +- .../Workspace}/Console/ConsolePanel.h | 2 +- .../Console/ConsoleWorkspaceFeature.cpp | 89 ++ .../Console/ConsoleWorkspaceFeature.h | 13 + .../Game/GameViewportController.cpp | 14 +- .../Workspace}/Game/GameViewportController.h | 0 .../Workspace}/Game/GameViewportFeature.cpp | 2 +- .../Workspace}/Game/GameViewportFeature.h | 2 +- .../Workspace/Game/GameWorkspaceFeature.cpp | 95 ++ .../Workspace/Game/GameWorkspaceFeature.h | 13 + .../Workspace}/Hierarchy/HierarchyModel.cpp | 4 +- .../Workspace}/Hierarchy/HierarchyModel.h | 2 +- .../Workspace}/Hierarchy/HierarchyPanel.cpp | 8 +- .../Workspace}/Hierarchy/HierarchyPanel.h | 4 +- .../Hierarchy/HierarchyWorkspaceFeature.cpp | 161 ++++ .../Hierarchy/HierarchyWorkspaceFeature.h | 13 + .../AudioListenerInspectorComponentEditor.h | 2 +- .../AudioSourceInspectorComponentEditor.h | 2 +- .../BoxColliderInspectorComponentEditor.h | 2 +- .../CameraInspectorComponentEditor.h | 2 +- .../CapsuleColliderInspectorComponentEditor.h | 2 +- .../ColliderInspectorComponentEditorUtils.h | 2 +- .../Components/IInspectorComponentEditor.h | 2 +- .../InspectorBindingComponentEditor.cpp | 2 +- .../InspectorBindingComponentEditor.h | 4 +- .../InspectorComponentEditorRegistry.cpp | 30 +- .../InspectorComponentEditorRegistry.h | 0 .../InspectorComponentEditorUtils.h | 0 .../LightInspectorComponentEditor.h | 2 +- .../MeshFilterInspectorComponentEditor.h | 2 +- .../MeshRendererInspectorComponentEditor.h | 2 +- .../RigidbodyInspectorComponentEditor.h | 2 +- ...criptComponentInspectorComponentEditor.cpp | 4 +- .../ScriptComponentInspectorComponentEditor.h | 2 +- .../SphereColliderInspectorComponentEditor.h | 2 +- .../TransformInspectorComponentEditor.cpp | 2 +- .../TransformInspectorComponentEditor.h | 2 +- .../VolumeRendererInspectorComponentEditor.h | 2 +- .../Inspector/InspectorFieldValueApplier.h | 10 +- .../Workspace}/Inspector/InspectorPanel.cpp | 18 +- .../Workspace}/Inspector/InspectorPanel.h | 6 +- .../Inspector/InspectorPresentationModel.cpp | 10 +- .../Inspector/InspectorPresentationModel.h | 2 +- .../Workspace}/Inspector/InspectorSubject.cpp | 6 +- .../Workspace}/Inspector/InspectorSubject.h | 4 +- .../Inspector/InspectorWorkspaceFeature.cpp | 129 +++ .../Inspector/InspectorWorkspaceFeature.h | 13 + .../Workspace}/Project/ProjectPanel.cpp | 8 +- .../Workspace}/Project/ProjectPanel.h | 6 +- .../Project/ProjectWorkspaceFeature.cpp | 245 +++++ .../Project/ProjectWorkspaceFeature.h | 13 + .../Scene/SceneEditCommandRoute.cpp | 4 +- .../Workspace}/Scene/SceneEditCommandRoute.h | 2 +- .../Scene/SceneViewportController.cpp | 20 +- .../Scene/SceneViewportController.h | 6 +- .../Workspace}/Scene/SceneViewportFeature.cpp | 6 +- .../Workspace}/Scene/SceneViewportFeature.h | 8 +- .../Scene/SceneViewportSceneOverlay.cpp | 10 +- .../Scene/SceneViewportSceneOverlay.h | 0 .../Workspace}/Scene/SceneViewportSession.cpp | 4 +- .../Workspace}/Scene/SceneViewportSession.h | 8 +- .../Scene/SceneViewportToolOverlay.cpp | 2 +- .../Scene/SceneViewportToolOverlay.h | 4 +- .../Scene/SceneViewportTransformGizmo.cpp | 10 +- .../Scene/SceneViewportTransformGizmo.h | 0 .../SceneViewportTransformGizmoSupport.cpp | 2 +- .../SceneViewportTransformGizmoSupport.h | 2 +- .../Workspace/Scene/SceneWorkspaceFeature.cpp | 131 +++ .../Workspace/Scene/SceneWorkspaceFeature.h | 13 + .../Workspace/WorkspaceFeatureHelpers.h | 20 + .../EditorUtilityWindowContent.h} | 10 +- .../Workspace/EditorWorkspaceHostedContent.h | 126 +++ .../EditorWorkspaceHostedContentSet.cpp | 207 ++++ .../EditorWorkspaceHostedContentSet.h | 51 + .../Registry/EditorProductRegistry.cpp | 207 ++++ .../Product/Registry/EditorProductRegistry.h | 110 +++ .../Rendering/Assets/BuiltInIcons.cpp | 2 +- .../Product}/Rendering/Assets/BuiltInIcons.h | 2 +- .../Assets/EditorIconServiceFactory.cpp | 4 +- .../Assets/EditorIconServiceFactory.h | 2 +- .../EditorViewportRuntimeServicesFactory.cpp | 4 +- .../EditorViewportRuntimeServicesFactory.h | 2 +- .../Viewport/GameViewportRenderService.cpp | 6 +- .../Viewport/GameViewportRenderService.h | 2 +- .../Viewport/Passes/SceneViewportGridPass.cpp | 4 +- .../Viewport/Passes/SceneViewportGridPass.h | 2 +- .../SceneViewportSelectedHelpersPass.cpp | 2 +- .../Passes/SceneViewportSelectedHelpersPass.h | 2 +- .../SceneViewportSelectionOutlinePass.cpp | 6 +- .../SceneViewportSelectionOutlinePass.h | 2 +- .../Viewport/SceneViewportPassSpecs.h | 0 .../SceneViewportRenderPassBundle.cpp | 2 +- .../Viewport/SceneViewportRenderPassBundle.h | 10 +- .../Viewport/SceneViewportRenderPlan.h | 6 +- .../Viewport/SceneViewportRenderService.cpp | 8 +- .../Viewport/SceneViewportRenderService.h | 6 +- .../Viewport/SceneViewportResourcePaths.h | 2 +- .../Viewport/ViewportContentRenderer.h | 2 +- .../Viewport/ViewportHostService.cpp | 28 +- .../Rendering/Viewport/ViewportHostService.h | 10 +- .../Viewport/ViewportObjectIdPicker.h | 2 +- .../Viewport/ViewportObjectPickerService.h | 3 + .../Viewport/ViewportRenderTargetUtils.cpp | 2 +- .../Viewport/ViewportRenderTargetUtils.h | 2 +- .../Viewport/ViewportRenderTargets.cpp | 2 +- .../Viewport/ViewportRenderTargets.h | 2 +- .../Rendering/Viewport/ViewportTypes.h | 2 +- .../EditorFrameStatusController.cpp | 84 +- .../Diagnostics/EditorFrameStatusController.h | 41 + .../Runtime/Diagnostics/WorkspaceTraceEntry.h | 12 + .../Product/Runtime/EditorProductRuntime.cpp | 244 +++++ .../Product/Runtime/EditorProductRuntime.h | 84 ++ .../Features/EditorFeatureComposition.cpp | 89 ++ .../Features/EditorFeatureComposition.h | 45 + .../Shell}/EditorShellAssetBuilder.cpp | 75 +- .../Runtime/Shell}/EditorShellAssetBuilder.h | 4 +- .../Shell/EditorShellDefinitionService.cpp | 60 ++ .../Shell}/EditorShellDefinitionService.h | 15 +- .../Shell}/EditorShellDrawComposer.cpp | 10 +- .../Runtime/Shell}/EditorShellDrawComposer.h | 4 +- .../Shell}/EditorShellInteractionEngine.cpp | 6 +- .../Shell}/EditorShellInteractionEngine.h | 2 +- .../Runtime/Shell}/EditorShellRuntime.cpp | 120 ++- .../Runtime/Shell}/EditorShellRuntime.h | 41 +- .../Shell}/EditorWorkspaceShellRuntime.h | 16 +- .../src/Product/Runtime/Store/EditorStore.cpp | 245 +++++ .../src/Product/Runtime/Store/EditorStore.h | 54 ++ .../EditorUtilityWindowContentController.cpp | 32 +- .../EditorUtilityWindowContentController.h | 10 +- .../Content/EditorWindowContentController.cpp | 38 + .../Content/EditorWindowContentController.h | 18 +- .../Content/EditorWindowContentFactory.cpp | 37 +- .../Content/EditorWindowContentFactory.h | 11 +- ...EditorWorkspaceWindowContentController.cpp | 80 +- .../EditorWorkspaceWindowContentController.h | 20 +- .../Windowing/EditorWindowDiagnostics.h | 2 +- .../Windowing/EditorWindowInstance.cpp | 8 +- .../Runtime}/Windowing/EditorWindowInstance.h | 0 .../Runtime/Windowing/EditorWindowManager.cpp | 590 +++++++++++ .../Runtime}/Windowing/EditorWindowManager.h | 44 +- .../Runtime}/Windowing/EditorWindowVisuals.h | 0 .../Frame/EditorWindowFrameOrchestrator.cpp | 20 +- .../Frame/EditorWindowFrameOrchestrator.h | 12 +- .../Runtime/EditorWindowRuntimeController.cpp | 24 +- .../Runtime/EditorWindowRuntimeController.h | 19 +- .../EditorWindowScreenshotController.cpp | 2 +- .../EditorWindowScreenshotController.h | 0 ...orWorkspaceWindowInteractionController.cpp | 542 +++++++++++ ...itorWorkspaceWindowInteractionController.h | 81 ++ .../EditorWorkspaceWindowSynchronizer.cpp | 448 +++++++++ .../EditorWorkspaceWindowSynchronizer.h | 106 ++ .../Services/Engine/EngineEditorServices.cpp | 18 +- .../Services/Engine/EngineEditorServices.h | 10 +- .../Engine/EngineGameViewportBridge.cpp | 2 +- .../Engine/EngineGameViewportBridge.h | 2 +- .../Engine/EngineSceneViewportBridge.cpp | 2 +- .../Engine/EngineSceneViewportBridge.h | 2 +- .../Services/Project/EditorProjectRuntime.cpp | 2 +- .../Services/Project/EditorProjectRuntime.h | 6 +- .../Services/Project/ProjectBrowserModel.cpp | 2 +- .../Services/Project/ProjectBrowserModel.h | 0 .../Project/ProjectGraphicsSettings.h | 0 .../Runtime/EditorPlayModeRuntime.cpp | 316 ++++++ .../Services/Runtime/EditorPlayModeRuntime.h | 58 ++ .../Runtime/EditorRuntimeCommandService.cpp | 196 ++++ .../Runtime/EditorRuntimeCommandService.h | 63 ++ .../Runtime/EditorSceneDocumentRuntime.cpp | 284 ++++++ .../Runtime/EditorSceneDocumentRuntime.h | 67 ++ .../Runtime/EditorScriptAssemblyBuilder.cpp | 4 +- .../Runtime/EditorScriptAssemblyBuilder.h | 0 .../Runtime/EditorScriptingRuntimeService.cpp | 8 +- .../Runtime/EditorScriptingRuntimeService.h | 0 .../Services/Scene/EditorSceneRuntime.cpp | 2 +- .../Services/Scene/EditorSceneRuntime.h | 6 +- .../Scene/EngineEditorSceneBackend.cpp | 2 +- .../Services/Scene/EngineEditorSceneBackend.h | 2 +- .../Product}/Services/Scene/SceneToolState.h | 2 +- .../Scene/SceneViewportCameraController.h | 0 .../State/EditorColorPickerToolState.cpp | 2 +- .../State/EditorColorPickerToolState.h | 0 .../State/EditorCommandFocusService.h | 2 +- .../Product}/State/EditorSelectionService.h | 4 +- .../Product}/State/EditorSelectionStamp.h | 0 .../Product}/State/EditorSession.cpp | 12 +- .../Product}/State/EditorSession.h | 0 editor/src/Product/State/EditorState.h | 27 + .../Product}/Support/EnvironmentFlags.h | 0 .../Product}/Support/StringEncoding.h | 0 .../{app => src/Product}/Support/TextFormat.h | 0 .../Windowing/System/EditorWindowSystem.cpp | 165 ++-- .../System/EditorWindowWorkspaceStore.cpp | 46 - .../System/EditorWindowWorkspaceStore.h | 38 - .../Workspace/UIEditorWorkspaceController.cpp | 193 ++-- 280 files changed, 7421 insertions(+), 6816 deletions(-) delete mode 100644 docs/plan/editor-authoritative-state-refactor-plan.md create mode 100644 docs/plan/editor-feature-colocation-framework-refactor-plan.md create mode 100644 docs/plan/editor-windowing-runtime-boundary-refactor-plan.md create mode 100644 editor/app/Bootstrap/EditorCompositionRoot.cpp create mode 100644 editor/app/Bootstrap/EditorCompositionRoot.h rename editor/app/{ => Bootstrap}/main.cpp (100%) delete mode 100644 editor/app/Composition/EditorContext.cpp delete mode 100644 editor/app/Composition/EditorContext.h delete mode 100644 editor/app/Composition/EditorFrameStatusController.h delete mode 100644 editor/app/Composition/EditorShellDefinitionService.cpp delete mode 100644 editor/app/Composition/EditorShellHostedPanelCoordinator.cpp delete mode 100644 editor/app/Composition/EditorShellHostedPanelCoordinator.h delete mode 100644 editor/app/Composition/EditorShellSessionCoordinator.cpp delete mode 100644 editor/app/Composition/EditorShellSessionCoordinator.h delete mode 100644 editor/app/Composition/EditorUtilityWindowRegistry.cpp delete mode 100644 editor/app/Composition/EditorUtilityWindowRegistry.h delete mode 100644 editor/app/Composition/EditorWorkspacePanelRegistry.cpp delete mode 100644 editor/app/Composition/EditorWorkspacePanelRegistry.h delete mode 100644 editor/app/Core/Panels/EditorPanelIds.h delete mode 100644 editor/app/Core/Product/EditorProductManifest.cpp delete mode 100644 editor/app/Core/Product/EditorProductManifest.h delete mode 100644 editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp delete mode 100644 editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.h delete mode 100644 editor/app/Core/Windowing/Contracts/EditorFrameStatusService.h delete mode 100644 editor/app/Core/Windowing/Contracts/EditorFrameValidation.h delete mode 100644 editor/app/Core/Windowing/Contracts/EditorShellDefinitionProvider.h delete mode 100644 editor/app/Core/Windowing/EditorFrameContracts.h delete mode 100644 editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp delete mode 100644 editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.h delete mode 100644 editor/app/Rendering/Viewport/ViewportObjectPickerService.h delete mode 100644 editor/app/Services/Runtime/EditorRuntimeCoordinator.cpp delete mode 100644 editor/app/Services/Runtime/EditorRuntimeCoordinator.h delete mode 100644 editor/app/Windowing/Content/EditorWindowContentController.cpp delete mode 100644 editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp delete mode 100644 editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h delete mode 100644 editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp delete mode 100644 editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.h delete mode 100644 editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp delete mode 100644 editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h delete mode 100644 editor/app/Windowing/EditorWindowManager.cpp create mode 100644 editor/src/Product/Commands/EditorCommand.cpp create mode 100644 editor/src/Product/Commands/EditorCommand.h rename editor/{app/Core => src/Product}/Commands/EditorHostCommandBridge.cpp (99%) rename editor/{app/Core => src/Product}/Commands/EditorHostCommandBridge.h (97%) rename editor/{app => src/Product}/Core/Assets/EditorIconService.h (100%) rename editor/{app => src/Product}/Core/Commands/EditorEditCommandRoute.h (100%) rename editor/{app => src/Product}/Core/Engine/EditorEngineLifecycle.h (100%) rename editor/{app => src/Product}/Core/Engine/EditorSceneBackendFactory.h (84%) rename editor/{app => src/Product}/Core/Engine/EditorShaderProvider.h (100%) rename editor/{app => src/Product}/Core/Engine/GameViewportEngineBridge.h (100%) rename editor/{app => src/Product}/Core/Engine/SceneViewportEngineBridge.h (95%) rename editor/{app => src/Product}/Core/Environment/EditorRuntimePaths.h (100%) rename editor/{app => src/Product}/Core/Scene/EditorSceneBackend.h (100%) rename editor/{app => src/Product}/Core/Scene/SceneViewportRenderRequest.h (90%) rename editor/{app => src/Product}/Core/Viewport/EditorViewportPicking.h (100%) rename editor/{app => src/Product}/Core/Viewport/EditorViewportRuntimeServices.h (88%) rename editor/{app => src/Product}/Core/Viewport/EditorViewportTypes.h (100%) rename editor/{app => src/Product}/Core/Windowing/EditorShellVariant.h (100%) rename editor/{app => src/Product}/Core/Windowing/EditorWindowGeometry.h (100%) rename editor/{app => src/Product}/Core/Windowing/EditorWindowMetrics.h (100%) rename editor/{app => src/Product}/Core/Windowing/EditorWindowTransferRequests.h (92%) rename editor/{app => src/Product}/Core/Windowing/EditorWindowTypes.h (100%) create mode 100644 editor/src/Product/Effects/EditorEffect.cpp create mode 100644 editor/src/Product/Effects/EditorEffect.h rename editor/{app/Features/Inspector => src/Product/Features/Utility/AddComponent}/AddComponentPanel.cpp (96%) rename editor/{app/Features/Inspector => src/Product/Features/Utility/AddComponent}/AddComponentPanel.h (88%) create mode 100644 editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.cpp create mode 100644 editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h rename editor/{app/Features => src/Product/Features/Utility}/ColorPicker/ColorPickerPanel.cpp (98%) rename editor/{app/Features => src/Product/Features/Utility}/ColorPicker/ColorPickerPanel.h (89%) create mode 100644 editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.cpp create mode 100644 editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Console/ConsolePanel.cpp (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Console/ConsolePanel.h (93%) create mode 100644 editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Game/GameViewportController.cpp (79%) rename editor/{app/Features => src/Product/Features/Workspace}/Game/GameViewportController.h (100%) rename editor/{app/Features => src/Product/Features/Workspace}/Game/GameViewportFeature.cpp (92%) rename editor/{app/Features => src/Product/Features/Workspace}/Game/GameViewportFeature.h (90%) create mode 100644 editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Hierarchy/HierarchyModel.cpp (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Hierarchy/HierarchyModel.h (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Hierarchy/HierarchyPanel.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Hierarchy/HierarchyPanel.h (97%) create mode 100644 editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/AudioListenerInspectorComponentEditor.h (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/AudioSourceInspectorComponentEditor.h (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/BoxColliderInspectorComponentEditor.h (95%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/CameraInspectorComponentEditor.h (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/CapsuleColliderInspectorComponentEditor.h (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/ColliderInspectorComponentEditorUtils.h (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/IInspectorComponentEditor.h (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/InspectorBindingComponentEditor.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/InspectorBindingComponentEditor.h (95%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/InspectorComponentEditorRegistry.cpp (59%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/InspectorComponentEditorRegistry.h (100%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/InspectorComponentEditorUtils.h (100%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/LightInspectorComponentEditor.h (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/MeshFilterInspectorComponentEditor.h (95%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/MeshRendererInspectorComponentEditor.h (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/RigidbodyInspectorComponentEditor.h (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/ScriptComponentInspectorComponentEditor.h (94%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/SphereColliderInspectorComponentEditor.h (95%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/TransformInspectorComponentEditor.cpp (96%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/TransformInspectorComponentEditor.h (89%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/Components/VolumeRendererInspectorComponentEditor.h (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorFieldValueApplier.h (78%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorPanel.cpp (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorPanel.h (96%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorPresentationModel.cpp (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorPresentationModel.h (95%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorSubject.cpp (89%) rename editor/{app/Features => src/Product/Features/Workspace}/Inspector/InspectorSubject.h (92%) create mode 100644 editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Project/ProjectPanel.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Project/ProjectPanel.h (98%) create mode 100644 editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.h rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneEditCommandRoute.cpp (97%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneEditCommandRoute.h (90%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportController.cpp (96%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportController.h (91%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportFeature.cpp (89%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportFeature.h (80%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportSceneOverlay.cpp (94%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportSceneOverlay.h (100%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportSession.cpp (96%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportSession.h (84%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportToolOverlay.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportToolOverlay.h (96%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportTransformGizmo.cpp (98%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportTransformGizmo.h (100%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportTransformGizmoSupport.cpp (99%) rename editor/{app/Features => src/Product/Features/Workspace}/Scene/SceneViewportTransformGizmoSupport.h (99%) create mode 100644 editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.cpp create mode 100644 editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.h create mode 100644 editor/src/Product/Features/Workspace/WorkspaceFeatureHelpers.h rename editor/{app/Core/UtilityWindows/EditorUtilityWindowRuntime.h => src/Product/Framework/UtilityWindow/EditorUtilityWindowContent.h} (84%) create mode 100644 editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContent.h create mode 100644 editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.cpp create mode 100644 editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h create mode 100644 editor/src/Product/Registry/EditorProductRegistry.cpp create mode 100644 editor/src/Product/Registry/EditorProductRegistry.h rename editor/{app => src/Product}/Rendering/Assets/BuiltInIcons.cpp (99%) rename editor/{app => src/Product}/Rendering/Assets/BuiltInIcons.h (98%) rename editor/{app => src/Product}/Rendering/Assets/EditorIconServiceFactory.cpp (62%) rename editor/{app => src/Product}/Rendering/Assets/EditorIconServiceFactory.h (77%) rename editor/{app => src/Product}/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp (62%) rename editor/{app => src/Product}/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h (75%) rename editor/{app => src/Product}/Rendering/Viewport/GameViewportRenderService.cpp (93%) rename editor/{app => src/Product}/Rendering/Viewport/GameViewportRenderService.h (90%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportGridPass.cpp (99%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportGridPass.h (95%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp (99%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h (95%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp (99%) rename editor/{app => src/Product}/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h (96%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportPassSpecs.h (100%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportRenderPassBundle.cpp (95%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportRenderPassBundle.h (67%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportRenderPlan.h (97%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportRenderService.cpp (96%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportRenderService.h (90%) rename editor/{app => src/Product}/Rendering/Viewport/SceneViewportResourcePaths.h (97%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportContentRenderer.h (92%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportHostService.cpp (93%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportHostService.h (91%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportObjectIdPicker.h (98%) create mode 100644 editor/src/Product/Rendering/Viewport/ViewportObjectPickerService.h rename editor/{app => src/Product}/Rendering/Viewport/ViewportRenderTargetUtils.cpp (97%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportRenderTargetUtils.h (97%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportRenderTargets.cpp (99%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportRenderTargets.h (97%) rename editor/{app => src/Product}/Rendering/Viewport/ViewportTypes.h (79%) rename editor/{app/Composition => src/Product/Runtime/Diagnostics}/EditorFrameStatusController.cpp (74%) create mode 100644 editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.h create mode 100644 editor/src/Product/Runtime/Diagnostics/WorkspaceTraceEntry.h create mode 100644 editor/src/Product/Runtime/EditorProductRuntime.cpp create mode 100644 editor/src/Product/Runtime/EditorProductRuntime.h create mode 100644 editor/src/Product/Runtime/Features/EditorFeatureComposition.cpp create mode 100644 editor/src/Product/Runtime/Features/EditorFeatureComposition.h rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellAssetBuilder.cpp (88%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellAssetBuilder.h (84%) create mode 100644 editor/src/Product/Runtime/Shell/EditorShellDefinitionService.cpp rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellDefinitionService.h (63%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellDrawComposer.cpp (94%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellDrawComposer.h (87%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellInteractionEngine.cpp (97%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellInteractionEngine.h (95%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellRuntime.cpp (71%) rename editor/{app/Composition => src/Product/Runtime/Shell}/EditorShellRuntime.h (77%) rename editor/{app/Core/Windowing => src/Product/Runtime/Shell}/EditorWorkspaceShellRuntime.h (90%) create mode 100644 editor/src/Product/Runtime/Store/EditorStore.cpp create mode 100644 editor/src/Product/Runtime/Store/EditorStore.h rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorUtilityWindowContentController.cpp (79%) rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorUtilityWindowContentController.h (82%) create mode 100644 editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.cpp rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorWindowContentController.h (88%) rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorWindowContentFactory.cpp (61%) rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorWindowContentFactory.h (77%) rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorWorkspaceWindowContentController.cpp (83%) rename editor/{app => src/Product/Runtime}/Windowing/Content/EditorWorkspaceWindowContentController.h (88%) rename editor/{app => src/Product/Runtime}/Windowing/EditorWindowDiagnostics.h (82%) rename editor/{app => src/Product/Runtime}/Windowing/EditorWindowInstance.cpp (98%) rename editor/{app => src/Product/Runtime}/Windowing/EditorWindowInstance.h (100%) create mode 100644 editor/src/Product/Runtime/Windowing/EditorWindowManager.cpp rename editor/{app => src/Product/Runtime}/Windowing/EditorWindowManager.h (75%) rename editor/{app => src/Product/Runtime}/Windowing/EditorWindowVisuals.h (100%) rename editor/{app => src/Product/Runtime}/Windowing/Frame/EditorWindowFrameOrchestrator.cpp (93%) rename editor/{app => src/Product/Runtime}/Windowing/Frame/EditorWindowFrameOrchestrator.h (88%) rename editor/{app => src/Product/Runtime}/Windowing/Runtime/EditorWindowRuntimeController.cpp (95%) rename editor/{app => src/Product/Runtime}/Windowing/Runtime/EditorWindowRuntimeController.h (89%) rename editor/{app => src/Product/Runtime}/Windowing/Runtime/EditorWindowScreenshotController.cpp (98%) rename editor/{app => src/Product/Runtime}/Windowing/Runtime/EditorWindowScreenshotController.h (100%) create mode 100644 editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.cpp create mode 100644 editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h create mode 100644 editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.cpp create mode 100644 editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h rename editor/{app => src/Product}/Services/Engine/EngineEditorServices.cpp (93%) rename editor/{app => src/Product}/Services/Engine/EngineEditorServices.h (76%) rename editor/{app => src/Product}/Services/Engine/EngineGameViewportBridge.cpp (96%) rename editor/{app => src/Product}/Services/Engine/EngineGameViewportBridge.h (91%) rename editor/{app => src/Product}/Services/Engine/EngineSceneViewportBridge.cpp (98%) rename editor/{app => src/Product}/Services/Engine/EngineSceneViewportBridge.h (95%) rename editor/{app => src/Product}/Services/Project/EditorProjectRuntime.cpp (99%) rename editor/{app => src/Product}/Services/Project/EditorProjectRuntime.h (96%) rename editor/{app => src/Product}/Services/Project/ProjectBrowserModel.cpp (99%) rename editor/{app => src/Product}/Services/Project/ProjectBrowserModel.h (100%) rename editor/{app => src/Product}/Services/Project/ProjectGraphicsSettings.h (100%) create mode 100644 editor/src/Product/Services/Runtime/EditorPlayModeRuntime.cpp create mode 100644 editor/src/Product/Services/Runtime/EditorPlayModeRuntime.h create mode 100644 editor/src/Product/Services/Runtime/EditorRuntimeCommandService.cpp create mode 100644 editor/src/Product/Services/Runtime/EditorRuntimeCommandService.h create mode 100644 editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.cpp create mode 100644 editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.h rename editor/{app => src/Product}/Services/Runtime/EditorScriptAssemblyBuilder.cpp (99%) rename editor/{app => src/Product}/Services/Runtime/EditorScriptAssemblyBuilder.h (100%) rename editor/{app => src/Product}/Services/Runtime/EditorScriptingRuntimeService.cpp (96%) rename editor/{app => src/Product}/Services/Runtime/EditorScriptingRuntimeService.h (100%) rename editor/{app => src/Product}/Services/Scene/EditorSceneRuntime.cpp (99%) rename editor/{app => src/Product}/Services/Scene/EditorSceneRuntime.h (97%) rename editor/{app => src/Product}/Services/Scene/EngineEditorSceneBackend.cpp (99%) rename editor/{app => src/Product}/Services/Scene/EngineEditorSceneBackend.h (89%) rename editor/{app => src/Product}/Services/Scene/SceneToolState.h (97%) rename editor/{app => src/Product}/Services/Scene/SceneViewportCameraController.h (100%) rename editor/{app/Core => src/Product}/State/EditorColorPickerToolState.cpp (96%) rename editor/{app/Core => src/Product}/State/EditorColorPickerToolState.h (100%) rename editor/{app/Core => src/Product}/State/EditorCommandFocusService.h (98%) rename editor/{app/Core => src/Product}/State/EditorSelectionService.h (97%) rename editor/{app/Core => src/Product}/State/EditorSelectionStamp.h (100%) rename editor/{app/Core => src/Product}/State/EditorSession.cpp (84%) rename editor/{app/Core => src/Product}/State/EditorSession.h (100%) create mode 100644 editor/src/Product/State/EditorState.h rename editor/{app => src/Product}/Support/EnvironmentFlags.h (100%) rename editor/{app => src/Product}/Support/StringEncoding.h (100%) rename editor/{app => src/Product}/Support/TextFormat.h (100%) delete mode 100644 editor/src/Windowing/System/EditorWindowWorkspaceStore.cpp delete mode 100644 editor/src/Windowing/System/EditorWindowWorkspaceStore.h diff --git a/docs/plan/editor-authoritative-state-refactor-plan.md b/docs/plan/editor-authoritative-state-refactor-plan.md deleted file mode 100644 index a6501e6c..00000000 --- a/docs/plan/editor-authoritative-state-refactor-plan.md +++ /dev/null @@ -1,870 +0,0 @@ -# XCEditor Authoritative State Architecture Refactor Plan - -## 1. 文档目的 - -本文档给出 XCEditor 的完整成熟重构方案。目标不是继续在现有 `controller/coordinator/manager/system` 叠层结构上做局部修补,而是从根源上把 editor 重构成: - -- 单一 authoritative state 驱动 -- 明确的 `Domain / Runtime / Host / Bootstrap` 分层 -- `app/` 回归可执行装配层与平台适配层 -- `include/` 与 `src/` 继续保留,作为稳定库边界 - -这是最终架构目标,不是过渡性方案。 - ---- - -## 2. 当前架构的核心问题 - -### 2.1 唯一最严重的问题 - -当前 editor 的根本问题是: - -**运行时建立在“controller/snapshot 副本 + 回写”模式上,而不是建立在单一 authoritative state 上。** - -这体现在几个关键点: - -- `UIEditorWorkspaceController` 同时承担状态容器、变更入口、校验器、回写桥接器四种职责。 -- `UIEditorWindowWorkspaceController` 对整个 window set 进行副本式变更,再由外层 commit。 -- `EditorWindowSystem` 一边保存 store,一边临时构建 live controller 和 mutation controller。 -- `EditorWorkspaceWindowContentController` 每帧都向系统请求一份 controller,再交给 shell runtime 消费。 -- `EditorWindowWorkspaceCoordinator` 负责跨窗口拖拽、分离、projection 更新、host 同步、plan commit,已经成为 reconciliation 大中枢。 - -### 2.2 这个问题带来的直接后果 - -1. 同一业务状态在多层重复存在。 -2. 运行时的职责边界不清,导致 `Composition`、`Windowing`、`Coordinator`、`Runtime` 概念重叠。 -3. 新功能更容易继续叠加新的 controller/coordinator,而不是扩展统一的 state 和 command。 -4. 多窗口、拖放、布局持久化、focus/capture、viewport 等功能都需要额外的同步代码维持一致性。 -5. 目录结构看似分层,实则业务状态所有权仍然混乱。 - -### 2.3 本次重构的结论 - -本次重构必须直接消除“副本式 controller 写回模式”。 -不以继续细化 `EditorWindowManager`、`EditorShellRuntime`、`EditorWindowWorkspaceCoordinator` 为方向。 - ---- - -## 3. 重构目标 - -### 3.1 总体目标 - -构建以下稳定架构: - -- `EditorState` 是进程内唯一业务真相源 -- 所有业务变更通过 `EditorCommand` 进入 reducer -- 所有外部副作用通过 `EditorEffect` 发出 -- 所有 UI、shell、window projection 都从 authoritative state 派生 -- `Host` 只负责平台适配,不拥有 editor 业务状态 -- `Runtime` 只负责每帧驱动和瞬时交互状态,不拥有业务真相源 - -### 3.2 非目标 - -以下内容不在本次重构的目标范围内: - -- 重写全部 UI widgets -- 替换 D3D12/Win32 后端 -- 改变已有视觉外观 -- 一次性重写全部 panels 的内部业务 -- 同时推进 unrelated feature work - ---- - -## 4. 目标架构总览 - -### 4.1 四层结构 - -#### Domain - -职责: - -- 定义 editor 业务状态 -- 定义命令、效果、校验、查询、投影 -- 处理 workspace/window topology 的 authoritative mutation - -特点: - -- 不依赖 Win32、D3D12、native window -- 不依赖 frame lifecycle -- 不持有 renderer、texture host、resource service - -#### Runtime - -职责: - -- 驱动每帧输入、shell 更新、panel 更新、viewport 请求 -- 维护瞬时交互状态 -- 调用 store dispatch command -- 消费 effect 并协调 host/runtime service - -特点: - -- 依赖 Domain 和 UI -- 不成为 authoritative business state owner - -#### Host - -职责: - -- Win32 window -- message loop -- DPI / cursor / clipboard / file dialog / monitor / crash trace -- D3D12 device、swapchain、UI renderer、capture - -特点: - -- 是平台 adapter -- 不做 editor 业务决策 -- 不直接修改 editor domain state - -#### Bootstrap - -职责: - -- 应用启动装配 -- 构造 store/runtime/host -- 初始化产品配置和依赖注入 -- 驱动主循环启动与关闭 - -特点: - -- 尽量薄 -- 不长期持有 editor 业务协调逻辑 - ---- - -## 5. 目标状态模型 - -### 5.1 EditorState - -`EditorState` 是唯一 authoritative business state,建议至少包含: - -```cpp -struct EditorState { - EditorSessionState session; - EditorWorkspaceState workspace; - EditorWindowWorkspaceState windowWorkspace; - EditorSelectionState selection; - EditorToolState toolState; - EditorProjectState project; - EditorSceneState scene; - EditorStatusState status; - EditorConsoleState console; - EditorLayoutPersistenceState layoutPersistence; -}; -``` - -说明: - -- `workspace` 表示单窗口 workspace 业务结构定义 -- `windowWorkspace` 表示多窗口 authoritative topology -- `selection`、`toolState`、`status` 不再散落在 context、frame status controller、panel runtime 中 - -### 5.2 EditorRuntimeState - -`EditorRuntimeState` 与 `EditorState` 分离,只存放瞬时交互态: - -```cpp -struct EditorRuntimeState { - GlobalTabDragRuntimeState globalTabDrag; - std::unordered_map windows; - ViewportRuntimeState viewports; - ScreenshotRuntimeState screenshots; -}; -``` - -这类状态的特点: - -- 可以 reset -- 不能作为业务真相源 -- 不能参与布局持久化 -- 不能作为 panel/window topology 的 authoritative owner - -### 5.3 EditorCommand - -所有业务变更统一走命令: - -- `OpenPanel` -- `ClosePanel` -- `ShowPanel` -- `HidePanel` -- `ActivatePanel` -- `SetSplitRatio` -- `MovePanelToStack` -- `DockPanelRelative` -- `DetachPanelToNewWindow` -- `MovePanelAcrossWindows` -- `DockPanelAcrossWindows` -- `OpenSceneAsset` -- `SetSelection` -- `RequestOpenUtilityWindow` -- `SaveLayout` - -### 5.4 EditorEffect - -所有副作用统一由 effect 描述: - -- `CreateWorkspaceWindow` -- `UpdateWorkspaceWindow` -- `CloseWorkspaceWindow` -- `FocusWindow` -- `OpenUtilityWindow` -- `SaveLayoutToDisk` -- `LoadScene` -- `RefreshProjectBrowser` -- `RequestViewportCapture` - -最终形态下: - -- reducer 只改 `EditorState` -- runtime/host 只消费 `EditorEffect` - ---- - -## 6. 目标目录结构 - -## 6.1 editor 根目录 - -```text -editor/ - include/ - src/ - app/ - resources/ - bin/ -``` - -`include/` 和 `src/` 会继续保留,不会删除。 - -## 6.2 include 目标结构 - -```text -editor/include/ - XCEditor/ - Domain/ - EditorState.h - EditorCommand.h - EditorEffect.h - EditorStore.h - Workspace/ - Windowing/ - Selection/ - Scene/ - Project/ - Persistence/ - UI/ - Collections/ - Docking/ - Fields/ - Foundation/ - Menu/ - Panels/ - Shell/ - Viewport/ - Widgets/ - Runtime/ - Shell/ - Panels/ - Windowing/ - Viewport/ - Effects/ -``` - -## 6.3 src 目标结构 - -```text -editor/src/ - Domain/ - State/ - Commands/ - Effects/ - Queries/ - Projections/ - Validation/ - Persistence/ - Workspace/ - Windowing/ - UI/ - Collections/ - Docking/ - Fields/ - Foundation/ - Menu/ - Panels/ - Shell/ - Viewport/ - Widgets/ - Runtime/ - Store/ - Shell/ - Panels/ - Windowing/ - Viewport/ - Effects/ - Diagnostics/ -``` - -## 6.4 app 目标结构 - -```text -editor/app/ - main.cpp - - Bootstrap/ - Application.h - Application.cpp - EditorAppConfig.h - EditorAppConfig.cpp - EditorBootstrapContext.h - EditorBootstrapContext.cpp - EditorCompositionRoot.h - EditorCompositionRoot.cpp - EditorStartup.h - EditorStartup.cpp - - Product/ - EditorProductInfo.h - EditorProductInfo.cpp - EditorBuildInfo.h - - Host/ - Interfaces/ - EditorNativeHost.h - EditorNativeWindow.h - EditorGraphicsHost.h - EditorHostResourceService.h - EditorHostServices.h - EditorHostTypes.h - - Win32/ - Diagnostics/ - Resources/ - System/ - Windowing/ - Chrome/ - - D3D12/ - ... -``` - -最终要求: - -- `app/` 里不再保留 editor 业务状态协调代码 -- `app/` 里不再保留 workspace mutation controller -- `app/` 里不再保留跨窗口业务协调器作为核心架构 - ---- - -## 7. 依赖方向约束 - -必须强制执行以下依赖规则: - -1. `Domain` 不依赖 `Runtime` -2. `Domain` 不依赖 `Host` -3. `UI` 可以依赖 `Domain`,不能依赖 `Host` -4. `Runtime` 可以依赖 `Domain + UI` -5. `Host` 可以依赖 `Runtime` 暴露的稳定接口,但不能成为 business state owner -6. `Bootstrap` 可以依赖所有层,但只负责装配,不承载长期业务逻辑 - -建议最终拆分为以下 CMake target: - -- `XCEditorDomain` -- `XCEditorUI` -- `XCEditorRuntime` -- `XCEditorHost` -- `XCEditorApp` - -依赖关系: - -```text -XCEditorDomain - <- XCEditorUI - <- XCEditorRuntime - <- XCEditorHost - <- XCEditorApp -``` - ---- - -## 8. 当前代码到目标架构的映射 - -### 8.1 直接保留并迁移目录的模块 - -这些模块整体质量较高,应尽量保留: - -- `editor/src/Collections` -- `editor/src/Docking` -- `editor/src/Fields` -- `editor/src/Foundation` -- `editor/src/Menu` -- `editor/src/Panels` -- `editor/src/Shell` -- `editor/src/Viewport` -- `editor/src/Widgets` - -动作: - -- 目录整体迁移到 `editor/src/UI/...` -- 对外头文件迁移到 `editor/include/XCEditor/UI/...` -- 不改变其核心职责 - -### 8.2 需要拆分重构的模块 - -#### Workspace controller 层 - -当前: - -- `editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h` -- `editor/src/Workspace/UIEditorWorkspaceController.cpp` - -目标: - -- `Domain/Workspace/WorkspaceCommands.*` -- `Domain/Workspace/WorkspaceReducers.*` -- `Domain/Workspace/WorkspaceQueries.*` -- `Domain/Workspace/WorkspaceValidation.*` - -原则: - -- 彻底去掉内部持有副本 + bound pointer 回写模式 - -#### Window workspace controller 层 - -当前: - -- `editor/include/XCEditor/Workspace/UIEditorWindowWorkspaceController.h` -- `editor/src/Workspace/UIEditorWindowWorkspaceController.cpp` -- `editor/src/Windowing/System/EditorWindowSystem.cpp` - -目标: - -- `Domain/Windowing/WindowWorkspaceCommands.*` -- `Domain/Windowing/WindowWorkspaceReducers.*` -- `Domain/Windowing/WindowWorkspaceQueries.*` -- `Runtime/Windowing/WindowHostSyncService.*` - -原则: - -- 去掉先 copy `windowSet` 再 commit 的变更模式 -- window set mutation 直接对 `EditorState` 生效 - -#### Composition / Context 层 - -当前: - -- `editor/app/Composition/EditorContext.*` -- `editor/app/Composition/EditorShellRuntime.*` -- `editor/app/Composition/EditorShellSessionCoordinator.*` -- `editor/app/Composition/EditorShellInteractionEngine.*` -- `editor/app/Composition/EditorFrameStatusController.*` - -目标: - -- `Runtime/Store/EditorStore.*` -- `Runtime/Shell/EditorShellRuntime.*` -- `Runtime/Shell/ShellProjectionBuilder.*` -- `Runtime/Shell/ShellInteractionRuntime.*` -- `Runtime/Diagnostics/EditorStatusRuntime.*` - -原则: - -- `EditorContext` 不再充当半个状态容器 -- session/status/selection 合并进 `EditorState` - -#### Windowing / Coordinator 层 - -当前: - -- `editor/app/Windowing/EditorWindowManager.*` -- `editor/app/Windowing/Runtime/EditorWindowRuntimeController.*` -- `editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.*` -- `editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.*` -- `editor/app/Windowing/Content/EditorWorkspaceWindowContentController.*` - -目标: - -- `Runtime/Windowing/WindowFrameDriver.*` -- `Runtime/Windowing/WindowProjectionRuntime.*` -- `Runtime/Windowing/WindowHostSyncService.*` -- `Runtime/Windowing/GlobalTabDragRuntime.*` -- `Runtime/Windowing/WorkspaceWindowContentRuntime.*` - -原则: - -- manager/coordinator/controller 三层叠加必须压缩 -- 保留 host 驱动,但去掉业务 reconciliation 中枢 - -### 8.3 应在最终架构中消失的核心概念 - -以下概念允许作为迁移期兼容层短暂存在,但不得保留在最终架构中: - -- 有状态 `UIEditorWorkspaceController` -- 有状态 `UIEditorWindowWorkspaceController` -- `TryBuildLiveWindowWorkspaceController` -- `BuildWorkspaceMutationController` -- 每帧临时构建 controller 再交给 runtime 更新 -- 业务 mutation 依赖 host synchronization plan commit 才真正生效 - ---- - -## 9. 关键运行时设计 - -### 9.1 EditorStore - -定义: - -- 持有唯一 `EditorState` -- 提供 `Dispatch(EditorCommand)` -- 产出 `EditorEffectQueue` -- 支持 observer / subscription - -接口建议: - -```cpp -class EditorStore { -public: - const EditorState& GetState() const; - DispatchResult Dispatch(const EditorCommand& command); - SubscriptionToken Subscribe(EditorStateObserver observer); -}; -``` - -### 9.2 Projection Builder - -所有视图模型从 authoritative state 派生: - -- `BuildWorkspaceProjection(state, windowId)` -- `BuildShellDefinition(state, windowId)` -- `BuildStatusBarModel(state, windowId)` -- `BuildViewportRequests(state, windowId)` - -原则: - -- projection 是纯只读派生 -- projection 自己不拥有状态 - -### 9.3 Global Tab Drag - -跨窗口拖拽不再通过 window coordinator 临时协调 authoritative mutation。 - -目标流程: - -1. runtime 持有 `GlobalTabDragRuntimeState` -2. pointer move/update 只更新 runtime transient state -3. pointer up 时生成 `DockPanelAcrossWindows` 或 `MovePanelAcrossWindows` command -4. reducer 直接修改 `EditorState.windowWorkspace` -5. `WindowHostSyncService` 比较前后 state,驱动 native window 更新 - -### 9.4 WindowHostSyncService - -职责: - -- 比较 `previousState.windowWorkspace` 与 `currentState.windowWorkspace` -- 产出 host action:create/update/close/focus -- 应用 action 到 native windows - -原则: - -- sync service 是 projection/applier,不是 state owner -- authoritative source 永远是 store - ---- - -## 10. 分阶段实施计划 - -## Phase 0 - 基线冻结与护栏 - -### 目标 - -- 在重构开始前建立足够的回归护栏 - -### 工作项 - -1. 整理现有 editor 相关单测分类 -2. 增补以下关键回归测试: - - 单窗口 workspace mutation - - 多窗口 detach / move / dock - - cross-window drag drop - - layout persistence - - primary window close 与 detached window close -3. 建立重构追踪文档和验收矩阵 - -### 退出条件 - -- 现有关键 editor 单测可稳定运行 -- 已识别不可回归场景 - -## Phase 1 - 建立 Domain 状态中心 - -### 目标 - -- 引入 `EditorState`、`EditorCommand`、`EditorEffect`、`EditorStore` - -### 工作项 - -1. 新建 `Domain` 目标目录 -2. 定义 `EditorState` -3. 把 `selection/status/console/session` 收口到 state -4. 新建 `EditorStore` -5. 建立 command dispatch 基础设施 - -### 退出条件 - -- editor 存在唯一业务状态入口 -- 不再新增新的 state owner - -## Phase 2 - 拆除 Workspace Controller - -### 目标 - -- 把单窗口 workspace mutation 改成 reducer 模式 - -### 工作项 - -1. 从 `UIEditorWorkspaceController` 提取 query/reducer/validation -2. 去掉 `m_boundWorkspace` / `m_boundSession` 这类回写桥 -3. 把 `Dispatch/SetSplitRatio/MoveTabToStack/DockTabRelative` 改为纯 reducer -4. 让 layout persistence 直接面向 `EditorState` - -### 退出条件 - -- 单窗口 workspace 不再依赖有状态 controller - -## Phase 3 - 拆除 Window Workspace Controller - -### 目标 - -- 把多窗口 topology mutation 改成 reducer 模式 - -### 工作项 - -1. 拆分 `UIEditorWindowWorkspaceController` -2. 把 detach/move/dock 迁移为 `EditorCommand` -3. 删除 `BuildWorkspaceMutationController` -4. 删除 `EvaluateDetachPanelToNewWindow` 这种先构建副本 controller 的路径 - -### 退出条件 - -- 多窗口变更直接作用于 authoritative state - -## Phase 4 - 建立 Projection 驱动 Shell - -### 目标 - -- shell/runtime 不再每帧构建 controller,而是每帧读取 projection - -### 工作项 - -1. 新建 `ShellProjectionBuilder` -2. `EditorShellDefinitionService` 改为纯 projection service -3. `EditorWorkspaceWindowContentController` 改为只持有 `windowId` -4. `EditorShellRuntime` 输入改为 `state + runtimeState + windowId` - -### 退出条件 - -- shell 更新流程中不再出现 live workspace controller 构建 - -## Phase 5 - Runtime 瞬时状态收口 - -### 目标 - -- 所有 pointer capture / drop preview / drag session / shell frame cache 收口到 runtime state - -### 工作项 - -1. 建立 `WindowRuntimeState` -2. 建立 `GlobalTabDragRuntimeState` -3. 建立 per-window shell transient state -4. 从 content controller、coordinator 中剥离瞬时交互态 - -### 退出条件 - -- 业务状态与瞬时状态分离清晰 - -## Phase 6 - 用 WindowHostSyncService 替换 WorkspaceCoordinator - -### 目标 - -- 去掉业务 reconciliation 大中枢 - -### 工作项 - -1. 实现 `WindowHostSyncService` -2. host window create/update/close/focus 改为 effect 驱动 -3. `EditorWindowWorkspaceCoordinator` 退役 -4. `EditorWindowManager` 压缩为 host lifecycle/loop driver - -### 退出条件 - -- window topology 同步不再依赖 coordinator 手工协调 - -## Phase 7 - Panels / Services 迁移 - -### 目标 - -- feature panel 与 runtime service 统一接入 store - -### 工作项 - -1. `Features/*` 面向 store/query/projection 重写接入方式 -2. `Services/*` 分拆为 domain-side data service 与 runtime-side effect runner -3. panel 只能发 semantic action,不直接改 authoritative state 结构 - -### 退出条件 - -- panel runtime 不再偷偷持有业务状态副本 - -## Phase 8 - app 收缩与最终清理 - -### 目标 - -- `app/` 只保留 bootstrap 与 host - -### 工作项 - -1. 重排 `app/Bootstrap` -2. 把 `app/Core`、`app/Composition`、`app/Windowing`、`app/Services` 迁出到 `src` -3. 清理遗留 controller/coordinator/manager -4. 清理临时 compatibility shim - -### 退出条件 - -- `app/` 不再承载 editor 业务状态和核心运行时协调逻辑 - ---- - -## 11. 最终 `app/` 迁移规则 - -### 保留在 `app/` - -- `main.cpp` -- `Bootstrap/*` -- `Host/Win32/*` -- `Host/D3D12/*` -- `Host/Interfaces/*` -- 产品信息与资源文件 - -### 迁移出 `app/` - -- `app/Core/*` -> `src/Domain/*` 或 `src/Runtime/*` -- `app/Composition/*` -> `src/Runtime/*` -- `app/Features/*` -> `src/Runtime/Panels/*` -- `app/Services/*` -> `src/Runtime/Services/*` -- `app/Windowing/*` -> `src/Runtime/Windowing/*` - -### 最终不应存在于 `app/` 的类型 - -- `EditorContext` -- `EditorShellRuntime` -- `EditorWindowManager` -- `EditorWindowRuntimeController` -- `EditorWindowWorkspaceCoordinator` -- `EditorWindowLifecycleCoordinator` -- `EditorWorkspacePanelRegistry` - ---- - -## 12. 测试与验证策略 - -### 12.1 单元测试 - -必须覆盖: - -- workspace reducer -- window workspace reducer -- layout persistence -- projection builder -- effect generation -- host sync diff - -### 12.2 集成测试 - -必须覆盖: - -- primary window 启动 -- panel detach 到新窗口 -- cross-window drag drop -- detached window close -- primary window close 导致全局退出 -- viewport 正常请求与渲染 - -### 12.3 手工验证 - -必须验证: - -- 标题栏与 detached title bar tab strip -- 鼠标 capture 行为 -- dock drop preview -- 跨 DPI 窗口移动 -- 截图与 viewport surface presentation - ---- - -## 13. 风险与约束 - -### 13.1 主要风险 - -1. 迁移过程中过渡层过多,导致最终架构没有真正收敛 -2. 多窗口拖放与 native capture 时序被破坏 -3. panel runtime 隐式依赖旧 controller API -4. 当前 `Features/*` 与 `Services/*` 对 `EditorSession` 的直接耦合较深 - -### 13.2 风险控制策略 - -1. 每一阶段结束必须删除对应旧路径,避免“双栈常驻” -2. 所有跨窗口行为优先补测试再迁移 -3. 中期允许兼容适配器,但必须在 Phase 8 全部清理 -4. 不在重构期间叠加 unrelated editor feature - ---- - -## 14. 完成标准 - -满足以下条件才算重构完成: - -1. editor 业务真相源只剩一个 `EditorState` -2. 不再存在有状态 workspace/window workspace controller -3. 不再存在每帧临时构建 controller 的更新路径 -4. 多窗口拓扑 mutation 全部走 command + reducer -5. host window create/update/close/focus 全部走 effect + sync service -6. `app/` 只剩 bootstrap 与 host adapter -7. 关键 editor 单测、集成测试、手工验证全部通过 - ---- - -## 15. 执行建议 - -### 建议执行顺序 - -1. Phase 0 -2. Phase 1 -3. Phase 2 -4. Phase 3 -5. Phase 4 -6. Phase 5 -7. Phase 6 -8. Phase 7 -9. Phase 8 - -### 建议实施原则 - -- 一次只迁一类状态所有权 -- 先建立新 authoritative path,再切换调用方,最后删除旧路径 -- 每阶段都保证 trunk 可编译、关键测试可跑 -- 不接受“最终长期保留双系统” - ---- - -## 16. 最终结论 - -XCEditor 的问题不是 `include/src` 这层目录形式本身,而是: - -**业务状态所有权、运行时瞬时状态、host 同步职责被混在 controller/coordinator 链条里。** - -因此最终方案必须是: - -- 保留 `include/src` -- 收缩 `app` -- 建立 `EditorState` 单一真相源 -- 用 `command + reducer + effect + projection` 替换现有副本式 controller 架构 - -这是唯一能从根上解决当前 editor 架构膨胀、状态同步复杂、跨窗口逻辑难维护问题的成熟方案。 diff --git a/docs/plan/editor-feature-colocation-framework-refactor-plan.md b/docs/plan/editor-feature-colocation-framework-refactor-plan.md new file mode 100644 index 00000000..4aa38d57 --- /dev/null +++ b/docs/plan/editor-feature-colocation-framework-refactor-plan.md @@ -0,0 +1,181 @@ +# XCEditor Feature Colocation And Framework Boundary Refactor Plan + +## Status + +Completed on 2026-04-30. + +This document is now the final closure record for the product-layer refactor, not a +pending migration checklist. + +## Goal + +Eliminate the old editor product split where: + +- feature UI lived under `Product/Content` +- feature registration lived under `Product/Modules` +- reusable hosted-content contracts lived under `Product/Hosting` + +That structure fragmented ownership, hid real runtime boundaries, and kept the +registry layer fatter than its name suggested. + +## Final Architecture + +```text +editor/src/Product/ + Commands/ + Core/ + Effects/ + Features/ + Utility/ + AddComponent/ + AddComponentPanel.* + AddComponentUtilityFeature.* + ColorPicker/ + ColorPickerPanel.* + ColorPickerUtilityFeature.* + Workspace/ + Console/ + ConsolePanel.* + ConsoleWorkspaceFeature.* + Game/ + GameViewportController.* + GameViewportFeature.* + GameWorkspaceFeature.* + Hierarchy/ + HierarchyModel.* + HierarchyPanel.* + HierarchyWorkspaceFeature.* + Inspector/ + Components/* + InspectorPanel.* + InspectorPresentationModel.* + InspectorSubject.* + InspectorWorkspaceFeature.* + Project/ + ProjectPanel.* + ProjectWorkspaceFeature.* + Scene/ + SceneEditCommandRoute.* + SceneViewportController.* + SceneViewportFeature.* + SceneViewportSceneOverlay.* + SceneViewportSession.* + SceneViewportToolOverlay.* + SceneViewportTransformGizmo.* + SceneViewportTransformGizmoSupport.* + SceneWorkspaceFeature.* + Framework/ + UtilityWindow/ + EditorUtilityWindowContent.h + Workspace/ + EditorWorkspaceHostedContent.h + EditorWorkspaceHostedContentSet.h/.cpp + Registry/ + EditorProductRegistry.h/.cpp + Rendering/ + Runtime/ + Diagnostics/ + Features/ + EditorFeatureComposition.h/.cpp + Shell/ + Store/ + Windowing/ + Services/ + State/ + Support/ +``` + +## Final Rules + +- `Features/*` owns feature identity, feature-local registration metadata, and feature + implementation together. +- `Framework/*` owns only stable host-facing contracts and generic hosted-content + containers. +- `Registry/*` owns only descriptor types, lookup, and validation. +- `Runtime/Features/*` owns runtime feature composition and content instantiation. +- no central `EditorWorkspacePanelIds.h` pool exists; workspace feature identity now + lives in the owning feature headers. +- obsolete `Product/Content`, `Product/Modules`, and `Product/Hosting` trees do not + exist anymore. + +## What Was Closed + +### 1. Framework Boundary + +- `Product/Hosting` was replaced by `Product/Framework`. +- hosted-content contracts were split into: + - `EditorWorkspaceHostedContent.h` + - `EditorWorkspaceHostedContentSet.h/.cpp` + - `EditorUtilityWindowContent.h` + +### 2. Feature Colocation + +- all workspace feature registration files now sit beside their owning feature UI. +- all utility feature registration files now sit beside their owning utility content. +- feature IDs were pulled back into feature-owned headers instead of a centralized + workspace panel id table. + +### 3. Thin Registry + +- `EditorProductModuleRegistry` was replaced by `EditorProductRegistry`. +- public registration API terminology now uses `Feature`, not `Module`. +- runtime-specific composition was removed from `Registry` and moved into: + - `Product/Runtime/Features/EditorFeatureComposition.*` + +### 4. Physical Cleanup + +- `Product/Content` was removed. +- `Product/Modules` was removed. +- `Product/Hosting` was removed. +- old include paths and old product registration entry points were removed from the + build. + +## Validation + +Validated during the refactor on 2026-04-30 with repeated build and smoke cycles. + +Latest validation commands: + +- `cmake --build build --config Debug --target XCEditor` +- smoke test with: + - `XCUIEDITOR_SMOKE_TEST=1` + - `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=12` + +Latest observed smoke result: + +- `EXIT_CODE=0` +- `DURATION_SECONDS=14.25` + +## Marker Gate + +Final marker audit on 2026-04-30 found a single pending marker file: + +- `20260430-154622-394.dog` + +That file was deleted only after: + +1. obsolete product trees were physically removed +2. registry responsibilities were reduced to descriptor/lookup/validation only +3. feature identity ownership was moved back into feature-owned headers +4. the latest build succeeded +5. the latest 12-second smoke test exited cleanly + +Current marker status: + +- no `.dog` files remain in the workspace + +## Completion Criteria + +The refactor is closed because all of the following are true: + +1. `Product/Framework` replaced `Product/Hosting`. +2. `Product/Features` replaced `Product/Content` and absorbed feature-local + registration. +3. `Product/Registry` replaced `EditorProductModuleRegistry`. +4. feature behavior and feature registration are physically co-located. +5. feature identity is no longer stored in a global workspace panel id pool. +6. the registry is thin and feature-agnostic. +7. runtime feature assembly lives under `Runtime/Features`. +8. the latest `XCEditor` build passed. +9. the latest 12-second smoke test passed. +10. the `.dog` marker gate was processed after items 1-9 were satisfied. diff --git a/docs/plan/editor-windowing-runtime-boundary-refactor-plan.md b/docs/plan/editor-windowing-runtime-boundary-refactor-plan.md new file mode 100644 index 00000000..515f5c82 --- /dev/null +++ b/docs/plan/editor-windowing-runtime-boundary-refactor-plan.md @@ -0,0 +1,171 @@ +# XCEditor Windowing Runtime Boundary Refactor Plan + +## Goal + +Eliminate the last invalid architectural seam inside the editor windowing stack: +`Product/Core/Windowing/Contracts` and `Product/Core/Windowing/EditorWorkspaceShellRuntime` +were pretending to be reusable core abstractions, but they actually described concrete +editor runtime services and a single concrete shell runtime flow. + +The target state is: + +- `Product/Core/Windowing` contains only stable value types and transfer models. +- `Product/Runtime/Shell` owns shell-definition and workspace-shell runtime services. +- `Product/Runtime/Diagnostics` owns frame-status and workspace trace services. +- windowing runtime code binds concrete runtime services directly instead of routing + them through fake cross-layer contracts. + +## Root Problem + +The previous structure had three coupled problems: + +1. `EditorShellDefinitionProvider`, `EditorFrameStatusService`, and + `EditorFrameValidation` were not true abstraction boundaries. + They each had one editor-owned runtime implementation. +2. `EditorWorkspaceShellRuntime` was stored under `Core`, but its API was tied to + editor runtime services, shell composition, viewport runtime wiring, and per-frame + status synchronization. +3. These pseudo-contracts were threaded through constructor signatures and per-frame + update contexts, so runtime-specific behavior leaked into supposedly reusable core + types. + +This made `Core` look reusable while actually encoding editor runtime policy. + +## Target Architecture + +```text +editor/src/Product/ + Core/ + Windowing/ + EditorShellVariant.h + EditorWindowGeometry.h + EditorWindowMetrics.h + EditorWindowTransferRequests.h + EditorWindowTypes.h + Runtime/ + Diagnostics/ + EditorFrameStatusController.* + WorkspaceTraceEntry.h + Shell/ + EditorShellDefinitionService.* + EditorShellRuntime.* + EditorWorkspaceShellRuntime.h + Windowing/ + Content/ + Frame/ + Runtime/ + Workspace/ +``` + +Rules: + +- `Core/Windowing` may only host stable primitive data, geometry, metrics, enums, and + transfer request types. +- runtime services must live in `Runtime/*`, even when they are used widely. +- per-frame content contexts may carry frame data, but not service locators or fake + contracts for globally owned runtime services. +- windowing runtime objects may depend directly on concrete editor runtime services + when there is only one valid implementation. + +## Execution Stages + +### Stage 1 - Remove Fake Contracts + +- Delete `Product/Core/Windowing/Contracts/*`. +- Delete `Product/Core/Windowing/EditorFrameContracts.h`. +- remove inheritance from runtime services that were only satisfying those fake + contracts. + +Exit condition: + +- no editor runtime service inherits from `Core/Windowing/Contracts` + +Status: + +- completed + +### Stage 2 - Move Runtime Shell Ownership + +- Move `EditorWorkspaceShellRuntime` out of `Product/Core/Windowing` into + `Product/Runtime/Shell`. +- update all include paths to the runtime-owned shell boundary. + +Exit condition: + +- workspace shell runtime is owned only by `Runtime/Shell` + +Status: + +- completed + +### Stage 3 - Rewire Windowing Runtime To Concrete Services + +- bind `EditorShellDefinitionService` and `EditorFrameStatusController` directly in + `EditorShellRuntime` +- bind the same concrete services directly in `EditorWindowRuntimeController` and + `EditorWindowManager` +- remove `shellDefinitionProvider` and `frameStatusService` from + `EditorWindowContentFrameContext` +- stop passing shell-definition and frame-status pseudo-services through per-frame + window update calls + +Exit condition: + +- the frame loop no longer depends on fake contracts or contract-style service passing + +Status: + +- completed + +### Stage 4 - Physical Cleanup And Audit + +- remove the empty `Core/Windowing/Contracts` directory +- search for all old type names and include paths +- rebuild `XCEditor` +- run the required 12-second smoke test + +Exit condition: + +- no code references the removed contract layer +- `XCEditor` builds +- smoke test exits cleanly + +Status: + +- completed + +## Validation + +Validated on 2026-04-30: + +- `cmake --build build --config Debug --target XCEditor` +- smoke test: + `XCUIEDITOR_SMOKE_TEST=1` + `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=12` + +Observed result: + +- build succeeded +- smoke process exited with `EXIT_CODE=0` +- measured smoke duration was `14.22` seconds + +## Final State + +The root architectural issue is closed when all of the following are true: + +1. `Product/Core/Windowing` contains only stable value-layer windowing types. +2. no `Contracts` directory exists under `Product/Core/Windowing`. +3. `EditorWorkspaceShellRuntime` is runtime-owned, not core-owned. +4. frame status and shell definition services are concrete runtime services, not + pseudo-interfaces. +5. windowing frame code no longer forwards fake contract services per frame. +6. build and smoke validation both pass. + +Current status: + +- all six conditions are satisfied + +## Marker Audit + +No `.dog` files are present in the workspace as of the final audit on 2026-04-30, so +there is no remaining marker file to remove for this refactor. diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 9179caea..93c3b955 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -142,7 +142,6 @@ set(XCUI_EDITOR_WINDOWING_SOURCES src/Windowing/Presentation/EditorWindowPresentationPolicy.cpp src/Windowing/System/EditorWindowSynchronizationPlanner.cpp src/Windowing/System/EditorWindowSystem.cpp - src/Windowing/System/EditorWindowWorkspaceStore.cpp ) set(XCUI_EDITOR_WIDGET_SUPPORT_SOURCES @@ -185,18 +184,17 @@ target_link_libraries(XCUIEditor PUBLIC xcui_editor_apply_common_target_settings(XCUIEditor PUBLIC) set(XCUI_EDITOR_APP_WINDOWING_SOURCES - app/Windowing/EditorWindowInstance.cpp - app/Windowing/EditorWindowManager.cpp - app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp - app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp - app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp - app/Windowing/Content/EditorWindowContentController.cpp - app/Windowing/Content/EditorWindowContentFactory.cpp - app/Windowing/Content/EditorUtilityWindowContentController.cpp - app/Windowing/Content/EditorWorkspaceWindowContentController.cpp - app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp - app/Windowing/Runtime/EditorWindowRuntimeController.cpp - app/Windowing/Runtime/EditorWindowScreenshotController.cpp + src/Product/Runtime/Windowing/EditorWindowInstance.cpp + src/Product/Runtime/Windowing/EditorWindowManager.cpp + src/Product/Runtime/Windowing/Content/EditorWindowContentController.cpp + src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.cpp + src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.cpp + src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.cpp + src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.cpp + src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.cpp + src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.cpp + src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.cpp + src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.cpp ) set(XCUI_EDITOR_HOST_PLATFORM_SOURCES @@ -218,99 +216,120 @@ set(XCUI_EDITOR_HOST_RENDERING_SOURCES ) if(XCENGINE_BUILD_XCUI_EDITOR_CORE) - set(XCUI_EDITOR_APP_CORE_CONTRACT_SOURCES - app/Core/Product/EditorProductManifest.cpp - app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp - app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp + set(XCUI_EDITOR_PRODUCT_FOUNDATION_SOURCES + src/Product/Commands/EditorCommand.cpp + src/Product/Effects/EditorEffect.cpp + src/Product/Runtime/Store/EditorStore.cpp + ) + + set(XCUI_EDITOR_APP_FRAMEWORK_SOURCES + src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.cpp ) set(XCUI_EDITOR_APP_STATE_SOURCES - app/Core/State/EditorColorPickerToolState.cpp - app/Core/State/EditorSession.cpp + src/Product/State/EditorColorPickerToolState.cpp + src/Product/State/EditorSession.cpp ) set(XCUI_EDITOR_APP_COMMAND_SOURCES - app/Core/Commands/EditorHostCommandBridge.cpp + src/Product/Commands/EditorHostCommandBridge.cpp ) - set(XCUI_EDITOR_APP_COMPOSITION_SOURCES - app/Composition/EditorUtilityWindowRegistry.cpp - app/Composition/EditorWorkspacePanelRegistry.cpp - app/Composition/EditorShellAssetBuilder.cpp - app/Composition/EditorContext.cpp - app/Composition/EditorFrameStatusController.cpp - app/Composition/EditorShellDefinitionService.cpp - app/Composition/EditorShellDrawComposer.cpp - app/Composition/EditorShellHostedPanelCoordinator.cpp - app/Composition/EditorShellInteractionEngine.cpp - app/Composition/EditorShellRuntime.cpp - app/Composition/EditorShellSessionCoordinator.cpp + set(XCUI_EDITOR_APP_REGISTRY_SOURCES + src/Product/Registry/EditorProductRegistry.cpp + ) + + set(XCUI_EDITOR_APP_FEATURE_REGISTRATION_SOURCES + src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.cpp + src/Product/Features/Workspace/Game/GameWorkspaceFeature.cpp + src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.cpp + src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.cpp + src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.cpp + src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.cpp + src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.cpp + src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.cpp + ) + + set(XCUI_EDITOR_APP_RUNTIME_SOURCES + src/Product/Runtime/Features/EditorFeatureComposition.cpp + src/Product/Runtime/Shell/EditorShellAssetBuilder.cpp + src/Product/Runtime/EditorProductRuntime.cpp + src/Product/Runtime/Diagnostics/EditorFrameStatusController.cpp + src/Product/Runtime/Shell/EditorShellDefinitionService.cpp + src/Product/Runtime/Shell/EditorShellDrawComposer.cpp + src/Product/Runtime/Shell/EditorShellInteractionEngine.cpp + src/Product/Runtime/Shell/EditorShellRuntime.cpp ) set(XCUI_EDITOR_APP_FEATURE_SOURCES - app/Features/Console/ConsolePanel.cpp - app/Features/ColorPicker/ColorPickerPanel.cpp - app/Features/Game/GameViewportController.cpp - app/Features/Game/GameViewportFeature.cpp - app/Features/Hierarchy/HierarchyModel.cpp - app/Features/Hierarchy/HierarchyPanel.cpp - app/Features/Inspector/AddComponentPanel.cpp - app/Features/Inspector/InspectorPanel.cpp - app/Features/Inspector/InspectorPresentationModel.cpp - app/Features/Inspector/InspectorSubject.cpp - app/Features/Inspector/Components/InspectorBindingComponentEditor.cpp - app/Features/Inspector/Components/InspectorComponentEditorRegistry.cpp - app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp - app/Features/Inspector/Components/TransformInspectorComponentEditor.cpp - app/Features/Project/ProjectPanel.cpp - app/Features/Scene/SceneViewportTransformGizmo.cpp - app/Features/Scene/SceneViewportTransformGizmoSupport.cpp - app/Features/Scene/SceneViewportSceneOverlay.cpp - app/Features/Scene/SceneEditCommandRoute.cpp - app/Features/Scene/SceneViewportFeature.cpp - app/Features/Scene/SceneViewportSession.cpp - app/Features/Scene/SceneViewportToolOverlay.cpp - app/Features/Scene/SceneViewportController.cpp + src/Product/Features/Workspace/Console/ConsolePanel.cpp + src/Product/Features/Utility/ColorPicker/ColorPickerPanel.cpp + src/Product/Features/Workspace/Game/GameViewportController.cpp + src/Product/Features/Workspace/Game/GameViewportFeature.cpp + src/Product/Features/Workspace/Hierarchy/HierarchyModel.cpp + src/Product/Features/Workspace/Hierarchy/HierarchyPanel.cpp + src/Product/Features/Utility/AddComponent/AddComponentPanel.cpp + src/Product/Features/Workspace/Inspector/InspectorPanel.cpp + src/Product/Features/Workspace/Inspector/InspectorPresentationModel.cpp + src/Product/Features/Workspace/Inspector/InspectorSubject.cpp + src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.cpp + src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.cpp + src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp + src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.cpp + src/Product/Features/Workspace/Project/ProjectPanel.cpp + src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.cpp + src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.cpp + src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.cpp + src/Product/Features/Workspace/Scene/SceneEditCommandRoute.cpp + src/Product/Features/Workspace/Scene/SceneViewportFeature.cpp + src/Product/Features/Workspace/Scene/SceneViewportSession.cpp + src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.cpp + src/Product/Features/Workspace/Scene/SceneViewportController.cpp ) set(XCUI_EDITOR_APP_RENDERING_SOURCES - app/Rendering/Assets/EditorIconServiceFactory.cpp - app/Rendering/Assets/BuiltInIcons.cpp - app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp - app/Rendering/Viewport/GameViewportRenderService.cpp - app/Rendering/Viewport/Passes/SceneViewportGridPass.cpp - app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp - app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp - app/Rendering/Viewport/SceneViewportRenderPassBundle.cpp - app/Rendering/Viewport/SceneViewportRenderService.cpp - app/Rendering/Viewport/ViewportHostService.cpp - app/Rendering/Viewport/ViewportRenderTargets.cpp - app/Rendering/Viewport/ViewportRenderTargetUtils.cpp + src/Product/Rendering/Assets/EditorIconServiceFactory.cpp + src/Product/Rendering/Assets/BuiltInIcons.cpp + src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp + src/Product/Rendering/Viewport/GameViewportRenderService.cpp + src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.cpp + src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp + src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp + src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.cpp + src/Product/Rendering/Viewport/SceneViewportRenderService.cpp + src/Product/Rendering/Viewport/ViewportHostService.cpp + src/Product/Rendering/Viewport/ViewportRenderTargets.cpp + src/Product/Rendering/Viewport/ViewportRenderTargetUtils.cpp ) set(XCUI_EDITOR_APP_SUPPORT_SOURCES - app/Services/Engine/EngineEditorServices.cpp - app/Services/Engine/EngineGameViewportBridge.cpp - app/Services/Engine/EngineSceneViewportBridge.cpp - app/Services/Scene/EngineEditorSceneBackend.cpp - app/Services/Scene/EditorSceneRuntime.cpp - app/Services/Runtime/EditorRuntimeCoordinator.cpp - app/Services/Runtime/EditorScriptingRuntimeService.cpp - app/Services/Project/EditorProjectRuntime.cpp - app/Services/Project/ProjectBrowserModel.cpp + src/Product/Services/Engine/EngineEditorServices.cpp + src/Product/Services/Engine/EngineGameViewportBridge.cpp + src/Product/Services/Engine/EngineSceneViewportBridge.cpp + src/Product/Services/Scene/EngineEditorSceneBackend.cpp + src/Product/Services/Scene/EditorSceneRuntime.cpp + src/Product/Services/Runtime/EditorRuntimeCommandService.cpp + src/Product/Services/Runtime/EditorPlayModeRuntime.cpp + src/Product/Services/Runtime/EditorSceneDocumentRuntime.cpp + src/Product/Services/Runtime/EditorScriptingRuntimeService.cpp + src/Product/Services/Project/EditorProjectRuntime.cpp + src/Product/Services/Project/ProjectBrowserModel.cpp ) if(XCENGINE_ENABLE_MONO_SCRIPTING) list(APPEND XCUI_EDITOR_APP_SUPPORT_SOURCES - app/Services/Runtime/EditorScriptAssemblyBuilder.cpp + src/Product/Services/Runtime/EditorScriptAssemblyBuilder.cpp ) endif() set(XCUI_EDITOR_APP_CORE_SOURCES - ${XCUI_EDITOR_APP_CORE_CONTRACT_SOURCES} + ${XCUI_EDITOR_PRODUCT_FOUNDATION_SOURCES} + ${XCUI_EDITOR_APP_FRAMEWORK_SOURCES} ${XCUI_EDITOR_APP_STATE_SOURCES} ${XCUI_EDITOR_APP_COMMAND_SOURCES} - ${XCUI_EDITOR_APP_COMPOSITION_SOURCES} + ${XCUI_EDITOR_APP_REGISTRY_SOURCES} + ${XCUI_EDITOR_APP_FEATURE_REGISTRATION_SOURCES} + ${XCUI_EDITOR_APP_RUNTIME_SOURCES} ${XCUI_EDITOR_APP_FEATURE_SOURCES} ${XCUI_EDITOR_APP_SUPPORT_SOURCES} ) @@ -326,17 +345,11 @@ if(XCENGINE_BUILD_XCUI_EDITOR_CORE) target_include_directories(XCEditorCore PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/app/Core + ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Interfaces ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/third_party/stb - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/app/Composition - ${CMAKE_CURRENT_SOURCE_DIR}/app/Features - ${CMAKE_CURRENT_SOURCE_DIR}/app/Services - ${CMAKE_CURRENT_SOURCE_DIR}/app/Support - ${CMAKE_CURRENT_SOURCE_DIR}/app/Windowing ) xcui_editor_apply_common_target_settings(XCEditorCore PUBLIC) @@ -368,9 +381,8 @@ if(XCENGINE_BUILD_XCUI_EDITOR_CORE) target_include_directories(XCEditorRendering PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/app/Core + ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Interfaces - ${CMAKE_CURRENT_SOURCE_DIR}/app/Rendering ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/third_party/stb @@ -390,7 +402,8 @@ endif() if(XCENGINE_BUILD_XCUI_EDITOR_APP) set(XCUI_EDITOR_APP_BOOTSTRAP_SOURCES app/Bootstrap/EditorApp.rc - app/main.cpp + app/Bootstrap/main.cpp + app/Bootstrap/EditorCompositionRoot.cpp app/Bootstrap/Application.cpp ) @@ -417,18 +430,17 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) ${XCUI_EDITOR_APP_HOST_SOURCES} ) - target_include_directories(XCEditorHost PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/app/Bootstrap - ${CMAKE_CURRENT_SOURCE_DIR}/app/Core + target_include_directories(XCEditorHost + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Interfaces - ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/D3D12 - ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Win32 - ${CMAKE_CURRENT_SOURCE_DIR}/app/Rendering - ${CMAKE_CURRENT_SOURCE_DIR}/app/Support - ${CMAKE_CURRENT_SOURCE_DIR}/app/Windowing ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/third_party/stb + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/app/Bootstrap + ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/D3D12 + ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Win32 ) xcui_editor_apply_common_target_settings(XCEditorHost PUBLIC) @@ -450,16 +462,10 @@ if(XCENGINE_BUILD_XCUI_EDITOR_APP) target_include_directories(XCEditor PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/app/Bootstrap - ${CMAKE_CURRENT_SOURCE_DIR}/app/Composition - ${CMAKE_CURRENT_SOURCE_DIR}/app/Core - ${CMAKE_CURRENT_SOURCE_DIR}/app/Features ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Interfaces ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/Win32 ${CMAKE_CURRENT_SOURCE_DIR}/app/Host/D3D12 - ${CMAKE_CURRENT_SOURCE_DIR}/app/Rendering - ${CMAKE_CURRENT_SOURCE_DIR}/app/Services - ${CMAKE_CURRENT_SOURCE_DIR}/app/Support - ${CMAKE_CURRENT_SOURCE_DIR}/app/Windowing + ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/engine/include ${CMAKE_SOURCE_DIR}/engine/third_party/stb diff --git a/editor/app/Bootstrap/Application.cpp b/editor/app/Bootstrap/Application.cpp index f633dc8c..2e904eab 100644 --- a/editor/app/Bootstrap/Application.cpp +++ b/editor/app/Bootstrap/Application.cpp @@ -1,29 +1,14 @@ #include "Application.h" -#include "EditorResources.h" -#include "SystemInteractionService.h" -#include "EditorContext.h" -#include "EditorShellRuntime.h" -#include "Engine/EngineEditorServices.h" -#include "EditorUtilityWindowRegistry.h" -#include "EditorWorkspacePanelRegistry.h" -#include "EditorWindowManager.h" -#include "Assets/EditorIconServiceFactory.h" -#include "Diagnostics/Win32CrashTrace.h" -#include "Viewport/EditorViewportRuntimeServicesFactory.h" -#include "System/Win32SystemInteractionHost.h" -#include "Resources/Win32EditorResourceService.h" -#include "Windowing/EditorWindow.h" -#include "Windowing/EditorWindowHostConfig.h" -#include "Windowing/EditorWindowHostRuntime.h" -#include "Windowing/EditorWindowMessageDispatcher.h" -#include "D3D12EditorWindowRenderRuntime.h" -#include "EnvironmentFlags.h" -#include +#include "EditorCompositionRoot.h" +#include "EditorResources.h" +#include "Diagnostics/Win32CrashTrace.h" +#include "Resources/Win32EditorResourceService.h" +#include "Product/Support/EnvironmentFlags.h" + #include #include -#include -#include + #include #include #include @@ -124,7 +109,8 @@ void EnableDpiAwareness() { if (shcore != nullptr) { using SetProcessDpiAwarenessFn = HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS); const auto setProcessDpiAwareness = - reinterpret_cast(GetProcAddress(shcore, "SetProcessDpiAwareness")); + reinterpret_cast( + GetProcAddress(shcore, "SetProcessDpiAwareness")); if (setProcessDpiAwareness != nullptr) { const HRESULT hr = setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); FreeLibrary(shcore); @@ -146,20 +132,32 @@ void EnableDpiAwareness() { } } -UIEditorWindowWorkspaceState BuildPrimaryWindowState( - std::string_view windowId, - const UIEditorWorkspaceController& workspaceController) { - UIEditorWindowWorkspaceState windowState = {}; - windowState.windowId = std::string(windowId); - windowState.workspace = workspaceController.GetWorkspace(); - windowState.session = workspaceController.GetSession(); - return windowState; +void TryEnableNonClientDpiScaling(HWND hwnd) { + if (hwnd == nullptr) { + return; + } + + const HMODULE user32 = GetModuleHandleW(L"user32.dll"); + if (user32 == nullptr) { + return; + } + + using EnableNonClientDpiScalingFn = BOOL(WINAPI*)(HWND); + const auto enableNonClientDpiScaling = + reinterpret_cast( + GetProcAddress(user32, "EnableNonClientDpiScaling")); + if (enableNonClientDpiScaling != nullptr) { + enableNonClientDpiScaling(hwnd); + } +} + +Application* GetApplicationFromWindowUserData(HWND hwnd) { + return reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); } } // namespace -Application::Application() - : m_editorContext(std::make_unique()) {} +Application::Application() = default; Application::~Application() = default; @@ -174,12 +172,12 @@ namespace XCEngine::UI::Editor { bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { m_hInstance = hInstance; - m_resourceService = std::make_unique(m_hInstance); - m_runtimePaths = ResolveRuntimePaths(m_resourceService->GetExecutableDirectory()); - m_engineComposition = App::CreateEngineEditorComposition(); EnableDpiAwareness(); - const std::filesystem::path logRoot = m_resourceService->GetExecutableDirectory() / "logs"; + auto resourceService = std::make_unique(m_hInstance); + const App::EditorRuntimePaths runtimePaths = + ResolveRuntimePaths(resourceService->GetExecutableDirectory()); + const std::filesystem::path logRoot = resourceService->GetExecutableDirectory() / "logs"; InitializeUIEditorRuntimeTrace(logRoot); auto runtimeTraceLogSink = std::make_unique(); m_runtimeTraceLogSink = runtimeTraceLogSink.get(); @@ -187,99 +185,28 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { SetUnhandledExceptionFilter(&Application::HandleUnhandledException); AppendUIEditorRuntimeTrace("app", "initialize begin"); - if (m_engineComposition == nullptr) { - AppendUIEditorRuntimeTrace("app", "engine services initialization failed"); - return false; - } - if (!m_editorContext->Initialize( - m_runtimePaths, - m_engineComposition->GetSceneBackendFactory())) { - AppendUIEditorRuntimeTrace( - "app", - "editor context initialization failed: " + - m_editorContext->GetValidationMessage()); - return false; - } if (!RegisterWindowClass()) { return false; } - m_systemInteractionHost = std::make_unique(); - m_windowSystem = std::make_unique( - m_editorContext->GetShellAsset().panelRegistry); + m_compositionRoot = std::make_unique(); + App::EditorCompositionRootInitializeParams compositionParams = {}; + compositionParams.hInstance = m_hInstance; + compositionParams.showCommand = nCmdShow; + compositionParams.windowClassName = kWindowClassName; + compositionParams.windowStyle = kBorderlessWindowStyle; + compositionParams.primaryWindowTitle = kWindowTitle; + compositionParams.windowUserData = this; + compositionParams.runtimePaths = runtimePaths; + compositionParams.resourceService = std::move(resourceService); + if (!m_compositionRoot->Initialize(std::move(compositionParams))) { + AppendUIEditorRuntimeTrace( + "app", + "composition root initialization failed: " + + m_compositionRoot->GetValidationMessage()); + return false; + } - App::EditorWindowHostConfig hostConfig = {}; - hostConfig.hInstance = m_hInstance; - hostConfig.windowClassName = kWindowClassName; - hostConfig.windowStyle = kBorderlessWindowStyle; - hostConfig.primaryWindowTitle = kWindowTitle; - hostConfig.windowUserData = this; - m_windowHostRuntime = std::make_unique( - hostConfig, - m_runtimePaths); - m_renderRuntimeFactory = - std::make_unique(); - App::EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory = [this]() { - return App::CreateEditorWorkspaceShellRuntime( - App::CreateEditorWorkspacePanelRuntimeSet( - m_editorContext->GetConsoleEntries(), - m_editorContext->GetProjectRuntime(), - m_editorContext->GetSceneRuntime(), - m_editorContext->GetColorPickerToolState(), - m_systemInteractionHost.get(), - [this](App::EditorUtilityWindowKind kind) { - m_editorContext->RequestOpenUtilityWindow(kind); - }, - [this](const std::filesystem::path& scenePath) { - return m_editorContext->RequestOpenSceneAsset(scenePath); - }), - App::CreateEditorIconService(), - App::CreateEditorViewportRuntimeServices(), - ::XCEngine::UI::Editor::BuildEditorShellShortcutManager( - m_editorContext->GetShellAsset()), - m_editorContext->GetRuntimeCoordinator(), - [this]() { - if (m_windowManager == nullptr) { - return; - } - - m_windowManager->RequestPrimaryWindowClose(); - }); - }; - m_windowManager = std::make_unique( - m_editorContext->GetFrameValidation(), - m_editorContext->GetShellDefinitionProvider(), - m_editorContext->GetFrameStatusService(), - [this]() { - return m_editorContext != nullptr - ? m_editorContext->ConsumeOpenUtilityWindowRequest() - : std::optional{}; - }, - m_engineComposition->GetSceneViewportBridge(), - m_engineComposition->GetGameViewportBridge(), - m_engineComposition->GetShaderProvider(), - *m_windowSystem, - *m_renderRuntimeFactory, - *m_resourceService, - *m_windowHostRuntime, - std::move(workspaceShellRuntimeFactory), - [this](App::EditorUtilityWindowKind kind) { - return App::CreateEditorUtilityWindowPanel( - kind, - m_editorContext->GetColorPickerToolState(), - m_editorContext->GetSceneRuntime()); - }); - - m_editorContext->GetFrameStatusService().SetReadyStatus(); - - App::EditorWindowCreateParams createParams = {}; - createParams.windowId = "main"; - createParams.title = kWindowTitle; - createParams.category = App::EditorWindowCategory::Workspace; - createParams.showCommand = nCmdShow; - createParams.primary = true; - createParams.autoCaptureOnStartup = - App::IsEnvironmentFlagEnabled("XCUI_AUTO_CAPTURE_ON_STARTUP"); m_smokeTestEnabled = App::IsEnvironmentFlagEnabled("XCUIEDITOR_SMOKE_TEST"); m_smokeTestCloseRequested = false; m_smokeTestRenderedFrameCount = 0; @@ -298,26 +225,6 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { } m_smokeTestDuration = std::chrono::seconds(smokeTestDurationSeconds); m_smokeTestStartTime = {}; - UIEditorWorkspaceController primaryWorkspaceController = - m_editorContext->BuildWorkspaceController(); - const UIEditorWindowWorkspaceState primaryWindowState = - BuildPrimaryWindowState(createParams.windowId, primaryWorkspaceController); - std::string windowSystemError = {}; - if (!m_windowSystem->BootstrapPrimaryWindow( - createParams.windowId, - primaryWindowState, - windowSystemError)) { - AppendUIEditorRuntimeTrace( - "app", - "window system bootstrap failed: " + windowSystemError); - return false; - } - if (m_windowManager->CreateWorkspaceWindow( - primaryWindowState, - createParams) == nullptr) { - AppendUIEditorRuntimeTrace("app", "primary window creation failed"); - return false; - } AppendUIEditorRuntimeTrace("app", "initialize end"); if (m_smokeTestEnabled) { @@ -333,25 +240,14 @@ bool Application::Initialize(HINSTANCE hInstance, int nCmdShow) { void Application::Shutdown() { AppendUIEditorRuntimeTrace("app", "shutdown begin"); - if (m_windowManager != nullptr) { - m_windowManager->Shutdown(); - m_windowManager.reset(); - } - m_renderRuntimeFactory.reset(); - m_windowHostRuntime.reset(); - m_windowSystem.reset(); - m_resourceService.reset(); - - m_systemInteractionHost.reset(); - - if (m_engineComposition != nullptr) { - m_engineComposition->GetLifecycle().Shutdown(); - m_engineComposition.reset(); + if (m_compositionRoot != nullptr) { + m_compositionRoot->Shutdown(); + m_compositionRoot.reset(); } if (m_windowClassAtom != 0 && m_hInstance != nullptr) { UnregisterClassW(kWindowClassName, m_hInstance); - m_windowClassAtom = 0; + m_windowClassAtom = 0; } m_smokeTestStartTime = {}; @@ -393,10 +289,6 @@ LONG WINAPI Application::HandleUnhandledException(EXCEPTION_POINTERS* exceptionI return EXCEPTION_EXECUTE_HANDLER; } -} // namespace XCEngine::UI::Editor - -namespace XCEngine::UI::Editor { - int Application::Run(HINSTANCE hInstance, int nCmdShow) { if (!Initialize(hInstance, nCmdShow)) { Shutdown(); @@ -419,41 +311,32 @@ int Application::Run(HINSTANCE hInstance, int nCmdShow) { ++processedMessageCount; } - if (m_windowManager != nullptr) { - if (m_engineComposition != nullptr) { - m_engineComposition->GetLifecycle().UpdateAsyncLoads(); - } - m_windowManager->DestroyClosedWindows(); - if (!m_windowManager->HasWindows()) { - break; - } + if (m_compositionRoot == nullptr) { + break; + } - if (m_editorContext != nullptr) { - m_editorContext->TickEditorRuntime(); - } - m_windowManager->RenderAllWindows(); - if (m_smokeTestEnabled && !m_smokeTestCloseRequested) { - ++m_smokeTestRenderedFrameCount; - const bool reachedFrameLimit = - m_smokeTestFrameLimit > 0 && - m_smokeTestRenderedFrameCount >= m_smokeTestFrameLimit; - const bool reachedDuration = - m_smokeTestDuration.count() > 0 && - m_smokeTestStartTime != std::chrono::steady_clock::time_point{} && - (std::chrono::steady_clock::now() - m_smokeTestStartTime) >= - m_smokeTestDuration; - if (reachedFrameLimit || reachedDuration) { - AppendUIEditorRuntimeTrace( - "smoke", - "auto-exit requested after duration/frame limit"); - m_smokeTestCloseRequested = true; - if (!m_windowManager->RequestPrimaryWindowClose()) { - PostQuitMessage(0); - } + if (!m_compositionRoot->TickFrame()) { + break; + } + if (m_smokeTestEnabled && !m_smokeTestCloseRequested) { + ++m_smokeTestRenderedFrameCount; + const bool reachedFrameLimit = + m_smokeTestFrameLimit > 0 && + m_smokeTestRenderedFrameCount >= m_smokeTestFrameLimit; + const bool reachedDuration = + m_smokeTestDuration.count() > 0 && + m_smokeTestStartTime != std::chrono::steady_clock::time_point{} && + (std::chrono::steady_clock::now() - m_smokeTestStartTime) >= + m_smokeTestDuration; + if (reachedFrameLimit || reachedDuration) { + AppendUIEditorRuntimeTrace( + "smoke", + "auto-exit requested after duration/frame limit"); + m_smokeTestCloseRequested = true; + if (!m_compositionRoot->RequestPrimaryWindowClose()) { + PostQuitMessage(0); } } - } else { - break; } } @@ -461,37 +344,6 @@ int Application::Run(HINSTANCE hInstance, int nCmdShow) { return 0; } -} // namespace XCEngine::UI::Editor - -namespace XCEngine::UI::Editor { - -namespace { - -void TryEnableNonClientDpiScaling(HWND hwnd) { - if (hwnd == nullptr) { - return; - } - - const HMODULE user32 = GetModuleHandleW(L"user32.dll"); - if (user32 == nullptr) { - return; - } - - using EnableNonClientDpiScalingFn = BOOL(WINAPI*)(HWND); - const auto enableNonClientDpiScaling = - reinterpret_cast( - GetProcAddress(user32, "EnableNonClientDpiScaling")); - if (enableNonClientDpiScaling != nullptr) { - enableNonClientDpiScaling(hwnd); - } -} - -Application* GetApplicationFromWindowUserData(HWND hwnd) { - return reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); -} - -} // namespace - bool Application::RegisterWindowClass() { WNDCLASSEXW windowClass = {}; windowClass.cbSize = sizeof(windowClass); @@ -538,8 +390,8 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP ? reinterpret_cast(createStruct->lpCreateParams) : nullptr; SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(application)); - if (application != nullptr && application->m_windowHostRuntime != nullptr) { - application->m_windowHostRuntime->HandlePendingNativeWindowCreated(hwnd); + if (application != nullptr && application->m_compositionRoot != nullptr) { + application->m_compositionRoot->HandlePendingNativeWindowCreated(hwnd); } return TRUE; } @@ -547,24 +399,17 @@ LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LP Application* application = GetApplicationFromWindowUserData(hwnd); LRESULT dispatcherResult = 0; if (application != nullptr && - application->m_windowHostRuntime != nullptr && - application->m_windowManager != nullptr) { - if (App::EditorWindow* const window = application->m_windowHostRuntime->FindWindow(hwnd); - window != nullptr && - App::EditorWindowMessageDispatcher::TryDispatch( + application->m_compositionRoot != nullptr && + application->m_compositionRoot->TryDispatchWindowMessage( hwnd, - *application->m_windowManager, - *window, message, wParam, lParam, dispatcherResult)) { - return dispatcherResult; - } + return dispatcherResult; } return DefWindowProcW(hwnd, message, wParam, lParam); } } // namespace XCEngine::UI::Editor - diff --git a/editor/app/Bootstrap/Application.h b/editor/app/Bootstrap/Application.h index 522e0e0b..7ef5051b 100644 --- a/editor/app/Bootstrap/Application.h +++ b/editor/app/Bootstrap/Application.h @@ -6,7 +6,7 @@ #include -#include "Environment/EditorRuntimePaths.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" #include #include @@ -16,24 +16,8 @@ namespace XCEngine::Debug { class ILogSink; } -namespace XCEngine::UI::Editor { -class EditorWindowSystem; - -namespace App { -class EditorContext; -class EngineEditorComposition; -class EditorWindowManager; -class EditorWindowHostRuntime; -} - -namespace Host { -class D3D12EditorWindowRenderRuntimeFactory; -class EditorHostResourceService; -} - -namespace System { -class SystemInteractionService; -} +namespace XCEngine::UI::Editor::App { +class EditorCompositionRoot; } namespace XCEngine::UI::Editor { @@ -62,17 +46,9 @@ private: HINSTANCE m_hInstance = nullptr; ATOM m_windowClassAtom = 0; - App::EditorRuntimePaths m_runtimePaths = {}; std::chrono::steady_clock::time_point m_smokeTestStartTime = {}; std::chrono::milliseconds m_smokeTestDuration = std::chrono::milliseconds::zero(); - std::unique_ptr m_editorContext = {}; - std::unique_ptr m_engineComposition = {}; - std::unique_ptr m_windowSystem = {}; - std::unique_ptr m_windowHostRuntime = {}; - std::unique_ptr m_renderRuntimeFactory = {}; - std::unique_ptr m_resourceService = {}; - std::unique_ptr m_windowManager = {}; - std::unique_ptr m_systemInteractionHost = {}; + std::unique_ptr m_compositionRoot = {}; XCEngine::Debug::ILogSink* m_runtimeTraceLogSink = nullptr; int m_smokeTestFrameLimit = 0; int m_smokeTestRenderedFrameCount = 0; @@ -83,4 +59,3 @@ private: int RunXCEditor(HINSTANCE hInstance, int nCmdShow); } // namespace XCEngine::UI::Editor - diff --git a/editor/app/Bootstrap/EditorCompositionRoot.cpp b/editor/app/Bootstrap/EditorCompositionRoot.cpp new file mode 100644 index 00000000..651a97eb --- /dev/null +++ b/editor/app/Bootstrap/EditorCompositionRoot.cpp @@ -0,0 +1,231 @@ +#include "EditorCompositionRoot.h" + +#include "EditorHostResourceService.h" +#include "SystemInteractionService.h" +#include "Product/Runtime/EditorProductRuntime.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" +#include "Product/Rendering/Assets/EditorIconServiceFactory.h" +#include "Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h" +#include "Product/Runtime/Shell/EditorShellRuntime.h" +#include "Product/Runtime/Windowing/EditorWindowManager.h" +#include "Product/Services/Engine/EngineEditorServices.h" +#include "Product/Support/EnvironmentFlags.h" +#include "System/Win32SystemInteractionHost.h" +#include "Windowing/EditorWindow.h" +#include "Windowing/EditorWindowHostConfig.h" +#include "Windowing/EditorWindowHostRuntime.h" +#include "Windowing/EditorWindowMessageDispatcher.h" +#include "D3D12EditorWindowRenderRuntime.h" + +#include +#include +#include + +#include +#include + +namespace XCEngine::UI::Editor::App { + +EditorCompositionRoot::EditorCompositionRoot() = default; + +EditorCompositionRoot::~EditorCompositionRoot() { + Shutdown(); +} + +bool EditorCompositionRoot::Initialize(EditorCompositionRootInitializeParams params) { + Shutdown(); + m_validationMessage.clear(); + + m_resourceService = std::move(params.resourceService); + if (m_resourceService == nullptr) { + m_validationMessage = "resource service was not provided"; + return false; + } + + m_engineComposition = CreateEngineEditorComposition(); + if (m_engineComposition == nullptr) { + m_validationMessage = "engine services initialization failed"; + return false; + } + + m_productRuntime = std::make_unique(); + if (!m_productRuntime->Initialize( + params.runtimePaths, + m_engineComposition->GetSceneBackendFactory())) { + m_validationMessage = "editor product runtime initialization failed: " + + m_productRuntime->GetValidationMessage(); + return false; + } + + m_systemInteractionHost = std::make_unique(); + m_windowSystem = std::make_unique( + m_productRuntime->GetPanelRegistry()); + + EditorWindowHostConfig hostConfig = {}; + hostConfig.hInstance = params.hInstance; + hostConfig.windowClassName = params.windowClassName; + hostConfig.windowStyle = params.windowStyle; + hostConfig.primaryWindowTitle = params.primaryWindowTitle; + hostConfig.windowUserData = params.windowUserData; + m_windowHostRuntime = std::make_unique( + std::move(hostConfig), + params.runtimePaths); + m_renderRuntimeFactory = + std::make_unique(); + EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory = [this]() { + return CreateEditorWorkspaceShellRuntime( + CreateEditorWorkspaceFeatureContentSet( + m_productRuntime->GetConsoleEntries(), + m_productRuntime->GetProjectRuntime(), + m_productRuntime->GetSceneRuntime(), + m_productRuntime->GetColorPickerToolState(), + m_systemInteractionHost.get(), + [this](EditorUtilityWindowKind kind) { + m_productRuntime->RequestOpenUtilityWindow(kind); + }, + [this](const std::filesystem::path& scenePath) { + return m_productRuntime->RequestOpenSceneAsset(scenePath); + }), + CreateEditorIconService(), + CreateEditorViewportRuntimeServices(), + ::XCEngine::UI::Editor::BuildEditorShellShortcutManager( + m_productRuntime->GetShellAsset()), + m_productRuntime->GetShellDefinitionService(), + m_productRuntime->GetFrameStatusController(), + m_productRuntime->GetRuntimeCommandService(), + [this]() { + if (m_windowManager != nullptr) { + m_windowManager->RequestPrimaryWindowClose(); + } + }); + }; + m_windowManager = std::make_unique( + *m_productRuntime, + [this]() { + return m_productRuntime != nullptr + ? m_productRuntime->ConsumeOpenUtilityWindowRequest() + : std::optional{}; + }, + m_engineComposition->GetSceneViewportBridge(), + m_engineComposition->GetGameViewportBridge(), + m_engineComposition->GetShaderProvider(), + *m_windowSystem, + *m_renderRuntimeFactory, + *m_resourceService, + *m_windowHostRuntime, + std::move(workspaceShellRuntimeFactory), + [this](EditorUtilityWindowKind kind) { + return CreateEditorUtilityFeatureContent( + kind, + m_productRuntime->GetColorPickerToolState(), + m_productRuntime->GetSceneRuntime()); + }); + + m_productRuntime->GetFrameStatusController().SetReadyStatus(); + + EditorWindowCreateParams createParams = {}; + createParams.windowId = "main"; + createParams.title = params.primaryWindowTitle; + createParams.category = EditorWindowCategory::Workspace; + createParams.showCommand = params.showCommand; + createParams.primary = true; + createParams.autoCaptureOnStartup = + IsEnvironmentFlagEnabled("XCUI_AUTO_CAPTURE_ON_STARTUP"); + + const UIEditorWindowWorkspaceState* primaryWindowState = + m_productRuntime->FindWindowWorkspaceState(createParams.windowId); + if (primaryWindowState == nullptr) { + m_validationMessage = "primary window state is missing from authoritative store"; + return false; + } + if (m_windowManager->CreateWorkspaceWindow( + *primaryWindowState, + createParams) == nullptr) { + m_validationMessage = "primary window creation failed"; + return false; + } + + return true; +} + +void EditorCompositionRoot::Shutdown() { + if (m_windowManager != nullptr) { + m_windowManager->Shutdown(); + m_windowManager.reset(); + } + + m_renderRuntimeFactory.reset(); + m_windowHostRuntime.reset(); + m_windowSystem.reset(); + m_systemInteractionHost.reset(); + m_resourceService.reset(); + + if (m_engineComposition != nullptr) { + m_engineComposition->GetLifecycle().Shutdown(); + m_engineComposition.reset(); + } + + m_productRuntime.reset(); +} + +const std::string& EditorCompositionRoot::GetValidationMessage() const { + return m_validationMessage; +} + +void EditorCompositionRoot::HandlePendingNativeWindowCreated(HWND hwnd) { + if (m_windowHostRuntime != nullptr) { + m_windowHostRuntime->HandlePendingNativeWindowCreated(hwnd); + } +} + +bool EditorCompositionRoot::TryDispatchWindowMessage( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam, + LRESULT& outResult) { + if (m_windowHostRuntime == nullptr || m_windowManager == nullptr) { + return false; + } + + if (EditorWindow* const window = m_windowHostRuntime->FindWindow(hwnd); + window != nullptr) { + return EditorWindowMessageDispatcher::TryDispatch( + hwnd, + *m_windowManager, + *window, + message, + wParam, + lParam, + outResult); + } + + return false; +} + +bool EditorCompositionRoot::TickFrame() { + if (m_windowManager == nullptr) { + return false; + } + + if (m_engineComposition != nullptr) { + m_engineComposition->GetLifecycle().UpdateAsyncLoads(); + } + m_windowManager->DestroyClosedWindows(); + if (!m_windowManager->HasWindows()) { + return false; + } + + if (m_productRuntime != nullptr) { + m_productRuntime->TickEditorRuntime(); + } + m_windowManager->RenderAllWindows(); + return m_windowManager->HasWindows(); +} + +bool EditorCompositionRoot::RequestPrimaryWindowClose() { + return m_windowManager != nullptr && + m_windowManager->RequestPrimaryWindowClose(); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Bootstrap/EditorCompositionRoot.h b/editor/app/Bootstrap/EditorCompositionRoot.h new file mode 100644 index 00000000..7855bf6b --- /dev/null +++ b/editor/app/Bootstrap/EditorCompositionRoot.h @@ -0,0 +1,82 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include + +#include "Product/Core/Environment/EditorRuntimePaths.h" + +#include +#include + +namespace XCEngine::UI::Editor { + +namespace Host { +class D3D12EditorWindowRenderRuntimeFactory; +class EditorHostResourceService; +} + +namespace System { +class SystemInteractionService; +} + +class EditorWindowSystem; + +namespace App { + +class EngineEditorComposition; +class EditorProductRuntime; +class EditorWindowManager; +class EditorWindowHostRuntime; + +struct EditorCompositionRootInitializeParams { + HINSTANCE hInstance = nullptr; + int showCommand = SW_SHOWNORMAL; + const wchar_t* windowClassName = nullptr; + DWORD windowStyle = 0; + const wchar_t* primaryWindowTitle = nullptr; + void* windowUserData = nullptr; + EditorRuntimePaths runtimePaths = {}; + std::unique_ptr resourceService = {}; +}; + +class EditorCompositionRoot final { +public: + EditorCompositionRoot(); + ~EditorCompositionRoot(); + + EditorCompositionRoot(const EditorCompositionRoot&) = delete; + EditorCompositionRoot& operator=(const EditorCompositionRoot&) = delete; + EditorCompositionRoot(EditorCompositionRoot&&) = delete; + EditorCompositionRoot& operator=(EditorCompositionRoot&&) = delete; + + bool Initialize(EditorCompositionRootInitializeParams params); + void Shutdown(); + + const std::string& GetValidationMessage() const; + void HandlePendingNativeWindowCreated(HWND hwnd); + bool TryDispatchWindowMessage( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam, + LRESULT& outResult); + bool TickFrame(); + bool RequestPrimaryWindowClose(); + +private: + std::unique_ptr m_productRuntime = {}; + std::unique_ptr m_engineComposition = {}; + std::unique_ptr m_windowSystem = {}; + std::unique_ptr m_windowHostRuntime = {}; + std::unique_ptr m_renderRuntimeFactory = {}; + std::unique_ptr m_resourceService = {}; + std::unique_ptr m_systemInteractionHost = {}; + std::unique_ptr m_windowManager = {}; + std::string m_validationMessage = {}; +}; + +} // namespace App +} // namespace XCEngine::UI::Editor diff --git a/editor/app/main.cpp b/editor/app/Bootstrap/main.cpp similarity index 100% rename from editor/app/main.cpp rename to editor/app/Bootstrap/main.cpp diff --git a/editor/app/Composition/EditorContext.cpp b/editor/app/Composition/EditorContext.cpp deleted file mode 100644 index b3b370dc..00000000 --- a/editor/app/Composition/EditorContext.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "EditorContext.h" -#include "Engine/EditorSceneBackendFactory.h" -#include "Scene/EditorSceneRuntime.h" - -#include - -#include - -namespace XCEngine::UI::Editor::App { - -using ::XCEngine::UI::Editor::AppendUIEditorRuntimeTrace; - -bool EditorContext::Initialize( - const EditorRuntimePaths& runtimePaths, - EditorSceneBackendFactory& sceneBackendFactory) { - m_valid = false; - m_validationMessage.clear(); - AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize begin"); - if (!m_shellDefinitionService.Initialize(runtimePaths)) { - m_validationMessage = m_shellDefinitionService.GetValidationMessage(); - AppendUIEditorRuntimeTrace("startup", m_validationMessage); - return false; - } - - m_session = {}; - m_consoleEntries.clear(); - m_session.workspaceRoot = runtimePaths.workspaceRoot; - m_session.projectRoot = runtimePaths.projectRoot; - m_selectionService = {}; - m_projectRuntime.Reset(); - AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize begin"); - m_projectRuntime.Initialize(runtimePaths.projectRoot); - AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize end"); - m_projectRuntime.BindSelectionService(&m_selectionService); - m_sceneRuntime.SetBackend(sceneBackendFactory.CreateSceneBackend()); - AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize begin"); - const EditorStartupSceneResult startupScene = - m_sceneRuntime.Initialize(m_session.projectRoot); - if (!startupScene.ready) { - m_validationMessage = "Editor scene runtime failed to initialize."; - AppendUIEditorRuntimeTrace("startup", m_validationMessage); - return false; - } - AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize end"); - m_sceneRuntime.BindSelectionService(&m_selectionService); - m_runtimeCoordinator.Initialize( - m_session, - m_sceneRuntime, - m_projectRuntime, - runtimePaths, - startupScene); - ResetEditorColorPickerToolState(m_colorPickerToolState); - m_pendingUtilityWindowRequest.reset(); - SyncSessionFromSelectionService(); - m_frameStatusController.Bind( - m_session, - m_consoleEntries, - [this]() { - SyncSessionFromSelectionService(); - }); - m_frameStatusController.Reset(); - m_frameStatusController.SetReadyStatus(); - m_valid = true; - AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize end"); - return true; -} - -void EditorContext::TickEditorRuntime() { - m_runtimeCoordinator.TickFrame(); -} - -bool EditorContext::IsValid() const { - return m_valid; -} - -const std::string& EditorContext::GetValidationMessage() const { - return m_validationMessage; -} - -const EditorShellAsset& EditorContext::GetShellAsset() const { - return m_shellDefinitionService.GetShellAsset(); -} - -const EditorSession& EditorContext::GetSession() const { - return m_session; -} - -const std::vector& EditorContext::GetConsoleEntries() const { - return m_consoleEntries; -} - -EditorProjectRuntime& EditorContext::GetProjectRuntime() { - return m_projectRuntime; -} - -const EditorProjectRuntime& EditorContext::GetProjectRuntime() const { - return m_projectRuntime; -} - -EditorSceneRuntime& EditorContext::GetSceneRuntime() { - return m_sceneRuntime; -} - -const EditorSceneRuntime& EditorContext::GetSceneRuntime() const { - return m_sceneRuntime; -} - -EditorRuntimeCoordinator& EditorContext::GetRuntimeCoordinator() { - return m_runtimeCoordinator; -} - -const EditorRuntimeCoordinator& EditorContext::GetRuntimeCoordinator() const { - return m_runtimeCoordinator; -} - -EditorColorPickerToolState& EditorContext::GetColorPickerToolState() { - return m_colorPickerToolState; -} - -const EditorColorPickerToolState& EditorContext::GetColorPickerToolState() const { - return m_colorPickerToolState; -} - -void EditorContext::RequestOpenUtilityWindow(EditorUtilityWindowKind kind) { - if (kind == EditorUtilityWindowKind::None) { - return; - } - - m_pendingUtilityWindowRequest = kind; -} - -std::optional EditorContext::ConsumeOpenUtilityWindowRequest() { - const std::optional requestedKind = - m_pendingUtilityWindowRequest; - m_pendingUtilityWindowRequest.reset(); - return requestedKind; -} - -void EditorContext::SyncSessionFromSelectionService() { - m_session.selection = m_selectionService.GetSelection(); -} - -UIEditorWorkspaceController EditorContext::BuildWorkspaceController() const { - return m_shellDefinitionService.BuildWorkspaceController(); -} - -bool EditorContext::RequestOpenSceneAsset(const std::filesystem::path& scenePath) { - const bool opened = m_runtimeCoordinator.RequestOpenSceneAsset(scenePath); - m_frameStatusController.SetStatus( - "Scene", - opened - ? m_runtimeCoordinator.GetLastMessage() - : m_runtimeCoordinator.GetLastMessage().empty() - ? std::string("Failed to open scene asset.") - : m_runtimeCoordinator.GetLastMessage()); - SyncSessionFromSelectionService(); - return opened; -} - -const EditorFrameValidation& EditorContext::GetFrameValidation() const { - return m_shellDefinitionService; -} - -const EditorShellDefinitionProvider& EditorContext::GetShellDefinitionProvider() const { - return m_shellDefinitionService; -} - -EditorFrameStatusService& EditorContext::GetFrameStatusService() { - return m_frameStatusController; -} - -const EditorFrameStatusService& EditorContext::GetFrameStatusService() const { - return m_frameStatusController; -} - -} // namespace XCEngine::UI::Editor::App - diff --git a/editor/app/Composition/EditorContext.h b/editor/app/Composition/EditorContext.h deleted file mode 100644 index dbe251a5..00000000 --- a/editor/app/Composition/EditorContext.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "Environment/EditorRuntimePaths.h" -#include "Scene/EditorSceneRuntime.h" -#include "Project/EditorProjectRuntime.h" -#include "Runtime/EditorRuntimeCoordinator.h" -#include "UtilityWindows/EditorUtilityWindowRuntime.h" -#include "EditorFrameStatusController.h" -#include "EditorShellDefinitionService.h" - -#include "State/EditorColorPickerToolState.h" -#include "State/EditorSelectionService.h" -#include "State/EditorSession.h" -#include -#include - -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -class EditorSceneBackendFactory; - -class EditorContext { -public: - bool Initialize( - const EditorRuntimePaths& runtimePaths, - EditorSceneBackendFactory& sceneBackendFactory); - - bool IsValid() const; - const std::string& GetValidationMessage() const; - - const EditorSession& GetSession() const; - const std::vector& GetConsoleEntries() const; - EditorProjectRuntime& GetProjectRuntime(); - const EditorProjectRuntime& GetProjectRuntime() const; - EditorSceneRuntime& GetSceneRuntime(); - const EditorSceneRuntime& GetSceneRuntime() const; - EditorColorPickerToolState& GetColorPickerToolState(); - const EditorColorPickerToolState& GetColorPickerToolState() const; - const EditorShellAsset& GetShellAsset() const; - EditorRuntimeCoordinator& GetRuntimeCoordinator(); - const EditorRuntimeCoordinator& GetRuntimeCoordinator() const; - UIEditorWorkspaceController BuildWorkspaceController() const; - const EditorFrameValidation& GetFrameValidation() const; - const EditorShellDefinitionProvider& GetShellDefinitionProvider() const; - EditorFrameStatusService& GetFrameStatusService(); - const EditorFrameStatusService& GetFrameStatusService() const; - - void RequestOpenUtilityWindow(EditorUtilityWindowKind kind); - std::optional ConsumeOpenUtilityWindowRequest(); - bool RequestOpenSceneAsset(const std::filesystem::path& scenePath); - void TickEditorRuntime(); - -private: - void SyncSessionFromSelectionService(); - - EditorShellDefinitionService m_shellDefinitionService = {}; - EditorFrameStatusController m_frameStatusController = {}; - EditorSession m_session = {}; - std::vector m_consoleEntries = {}; - EditorSelectionService m_selectionService = {}; - EditorProjectRuntime m_projectRuntime = {}; - EditorSceneRuntime m_sceneRuntime = {}; - EditorRuntimeCoordinator m_runtimeCoordinator = {}; - EditorColorPickerToolState m_colorPickerToolState = {}; - std::optional m_pendingUtilityWindowRequest = {}; - bool m_valid = false; - std::string m_validationMessage = {}; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorFrameStatusController.h b/editor/app/Composition/EditorFrameStatusController.h deleted file mode 100644 index a1778706..00000000 --- a/editor/app/Composition/EditorFrameStatusController.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "Windowing/EditorFrameContracts.h" -#include "State/EditorSession.h" - -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -class EditorFrameStatusController final : public EditorFrameStatusService { -public: - using SessionSyncCallback = std::function; - - void Bind( - EditorSession& session, - std::vector& consoleEntries, - SessionSyncCallback syncSessionProjection); - void Reset(); - - void SetStatus(std::string status, std::string message) override; - void SetReadyStatus() override; - std::string ComposeStatusText() const override; - void UpdateStatusFromShellResult( - const UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionResult& result) override; - std::string DescribeWorkspaceState( - const UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionState& interactionState) const override; - std::vector SyncWorkspacePanelFrameEvents( - const std::vector& panelEvents) override; - -private: - void AppendConsoleEntry(std::string channel, std::string message); - void SyncSessionConsoleProjection(); - - EditorSession* m_session = nullptr; - std::vector* m_consoleEntries = nullptr; - SessionSyncCallback m_syncSessionProjection = {}; - std::string m_lastStatus = {}; - std::string m_lastMessage = {}; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellDefinitionService.cpp b/editor/app/Composition/EditorShellDefinitionService.cpp deleted file mode 100644 index 5ba6e29a..00000000 --- a/editor/app/Composition/EditorShellDefinitionService.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "EditorShellDefinitionService.h" - -#include "EditorShellAssetBuilder.h" -#include "EditorWorkspacePanelRegistry.h" -#include "Panels/EditorPanelIds.h" - -namespace XCEngine::UI::Editor::App { - -namespace { - -using ::XCEngine::UI::Editor::UIEditorWorkspacePanelPresentationModel; - -UIEditorWorkspacePanelPresentationModel* FindMutablePresentation( - std::vector& presentations, - std::string_view panelId) { - for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) { - if (presentation.panelId == panelId) { - return &presentation; - } - } - - return nullptr; -} - -} // namespace - -bool EditorShellDefinitionService::Initialize(const EditorRuntimePaths& runtimePaths) { - m_valid = false; - m_validationMessage.clear(); - - const EditorWorkspacePanelCompositionValidationResult panelCompositionValidation = - ValidateEditorWorkspacePanelComposition(); - if (!panelCompositionValidation.IsValid()) { - m_validationMessage = panelCompositionValidation.message; - return false; - } - - m_shellAsset = BuildEditorApplicationShellAsset(runtimePaths); - m_shellValidation = ValidateEditorShellAsset(m_shellAsset); - if (!m_shellValidation.IsValid()) { - m_validationMessage = m_shellValidation.message; - return false; - } - - m_valid = true; - return true; -} - -bool EditorShellDefinitionService::IsValid() const { - return m_valid; -} - -const std::string& EditorShellDefinitionService::GetValidationMessage() const { - return m_validationMessage; -} - -const EditorShellAsset& EditorShellDefinitionService::GetShellAsset() const { - return m_shellAsset; -} - -UIEditorWorkspaceController EditorShellDefinitionService::BuildWorkspaceController() const { - return UIEditorWorkspaceController( - m_shellAsset.panelRegistry, - m_shellAsset.workspace, - m_shellAsset.workspaceSession); -} - -UIEditorShellInteractionDefinition EditorShellDefinitionService::BuildShellDefinition( - const UIEditorWorkspaceController& workspaceController, - std::string_view statusText, - std::string_view captureText, - EditorShellVariant variant) const { - UIEditorShellInteractionDefinition definition = - BuildEditorApplicationShellInteractionDefinition( - m_shellAsset, - workspaceController, - statusText, - captureText, - variant); - - if (UIEditorWorkspacePanelPresentationModel* scenePresentation = - FindMutablePresentation(definition.workspacePresentations, kScenePanelId); - scenePresentation != nullptr) { - scenePresentation->viewportShellModel.spec.chrome.showTopBar = true; - scenePresentation->viewportShellModel.spec.chrome.topBarHeight = 24.0f; - scenePresentation->viewportShellModel.spec.chrome.title = {}; - scenePresentation->viewportShellModel.spec.chrome.subtitle = {}; - scenePresentation->viewportShellModel.spec.toolItems.clear(); - scenePresentation->viewportShellModel.spec.visualState.hoveredToolIndex = - Widgets::UIEditorViewportSlotInvalidIndex; - scenePresentation->viewportShellModel.spec.visualState.activeToolIndex = - Widgets::UIEditorViewportSlotInvalidIndex; - } - - return definition; -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp b/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp deleted file mode 100644 index 421f48bf..00000000 --- a/editor/app/Composition/EditorShellHostedPanelCoordinator.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "EditorShellHostedPanelCoordinator.h" - -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" - -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -void EditorShellHostedPanelCoordinator::Update( - const EditorShellHostedPanelCoordinatorContext& context) const { - const bool shellOwnsHostedContentPointerStream = - ShouldYieldUIEditorHostedContentPointerStream( - context.shellFrame, - context.shellInteractiveCaptureActive); - const std::vector<::XCEngine::UI::UIInputEvent> hostedContentEvents = - FilterUIEditorHostedContentInputEvents( - context.inputEvents, - shellOwnsHostedContentPointerStream); - - const EditorWorkspacePanelUpdateContext updateContext{ - .shellFrame = context.shellFrame, - .shellInteractionState = context.shellInteractionState, - .hostedContentEvents = hostedContentEvents, - }; - - context.workspacePanels.UpdatePhase( - updateContext, - EditorWorkspacePanelUpdatePhase::Main); - context.workspacePanels.UpdatePhase( - updateContext, - EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellHostedPanelCoordinator.h b/editor/app/Composition/EditorShellHostedPanelCoordinator.h deleted file mode 100644 index 8de0ddb5..00000000 --- a/editor/app/Composition/EditorShellHostedPanelCoordinator.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -namespace XCEngine::UI { - -struct UIInputEvent; - -} // namespace XCEngine::UI - -namespace XCEngine::UI::Editor { - -struct UIEditorShellInteractionFrame; -struct UIEditorShellInteractionState; - -} // namespace XCEngine::UI::Editor - -namespace XCEngine::UI::Editor::App { - -class EditorWorkspacePanelRuntimeSet; - -struct EditorShellHostedPanelCoordinatorContext { - UIEditorShellInteractionFrame& shellFrame; - UIEditorShellInteractionState& shellInteractionState; - const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents; - bool shellInteractiveCaptureActive = false; - EditorWorkspacePanelRuntimeSet& workspacePanels; -}; - -class EditorShellHostedPanelCoordinator final { -public: - void Update(const EditorShellHostedPanelCoordinatorContext& context) const; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellSessionCoordinator.cpp b/editor/app/Composition/EditorShellSessionCoordinator.cpp deleted file mode 100644 index 2e309f40..00000000 --- a/editor/app/Composition/EditorShellSessionCoordinator.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "EditorShellSessionCoordinator.h" - -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" - -namespace XCEngine::UI::Editor::App { - -UIEditorShellInteractionDefinition EditorShellSessionCoordinator::PrepareShellDefinition( - const EditorShellSessionCoordinatorContext& context) const { - context.workspacePanels.PrepareForShellDefinition(context.workspaceController); - UIEditorShellInteractionDefinition definition = - context.shellDefinitionProvider.BuildShellDefinition( - context.workspaceController, - context.frameStatusService.ComposeStatusText(), - context.captureText, - context.shellVariant); - definition.panelOverlayRegions = - context.workspacePanels.CollectPanelOverlayRegions(); - return definition; -} - -void EditorShellSessionCoordinator::FinalizeFrame( - EditorFrameStatusService& frameStatusService, - UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionResult& shellResult) const { - frameStatusService.UpdateStatusFromShellResult(workspaceController, shellResult); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellSessionCoordinator.h b/editor/app/Composition/EditorShellSessionCoordinator.h deleted file mode 100644 index 782b211a..00000000 --- a/editor/app/Composition/EditorShellSessionCoordinator.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "Windowing/EditorFrameContracts.h" - -#include -#include - -#include - -namespace XCEngine::UI::Editor::App { - -class EditorWorkspacePanelRuntimeSet; - -struct EditorShellSessionCoordinatorContext { - const EditorShellDefinitionProvider& shellDefinitionProvider; - EditorFrameStatusService& frameStatusService; - UIEditorWorkspaceController& workspaceController; - std::string_view captureText; - EditorShellVariant shellVariant = EditorShellVariant::Primary; - EditorWorkspacePanelRuntimeSet& workspacePanels; -}; - -class EditorShellSessionCoordinator final { -public: - UIEditorShellInteractionDefinition PrepareShellDefinition( - const EditorShellSessionCoordinatorContext& context) const; - void FinalizeFrame( - EditorFrameStatusService& frameStatusService, - UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionResult& shellResult) const; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorUtilityWindowRegistry.cpp b/editor/app/Composition/EditorUtilityWindowRegistry.cpp deleted file mode 100644 index 6c37b581..00000000 --- a/editor/app/Composition/EditorUtilityWindowRegistry.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "EditorUtilityWindowRegistry.h" - -#include "ColorPicker/ColorPickerPanel.h" -#include "Inspector/AddComponentPanel.h" -#include "Scene/EditorSceneRuntime.h" -#include "State/EditorColorPickerToolState.h" - -namespace XCEngine::UI::Editor::App { - -std::unique_ptr CreateEditorUtilityWindowPanel( - EditorUtilityWindowKind kind, - EditorColorPickerToolState& colorPickerToolState, - EditorSceneRuntime& sceneRuntime) { - switch (kind) { - case EditorUtilityWindowKind::ColorPicker: - return std::make_unique(colorPickerToolState); - case EditorUtilityWindowKind::AddComponent: - return std::make_unique(sceneRuntime); - case EditorUtilityWindowKind::None: - default: - return nullptr; - } -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorUtilityWindowRegistry.h b/editor/app/Composition/EditorUtilityWindowRegistry.h deleted file mode 100644 index add943bc..00000000 --- a/editor/app/Composition/EditorUtilityWindowRegistry.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "UtilityWindows/EditorUtilityWindowRuntime.h" - -namespace XCEngine::UI::Editor::App { - -struct EditorColorPickerToolState; -class EditorSceneRuntime; - -std::unique_ptr CreateEditorUtilityWindowPanel( - EditorUtilityWindowKind kind, - EditorColorPickerToolState& colorPickerToolState, - EditorSceneRuntime& sceneRuntime); - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorWorkspacePanelRegistry.cpp b/editor/app/Composition/EditorWorkspacePanelRegistry.cpp deleted file mode 100644 index a603d915..00000000 --- a/editor/app/Composition/EditorWorkspacePanelRegistry.cpp +++ /dev/null @@ -1,763 +0,0 @@ -#include "EditorWorkspacePanelRegistry.h" - -#include "Panels/EditorPanelIds.h" -#include "Product/EditorProductManifest.h" -#include "Console/ConsolePanel.h" -#include "Game/GameViewportFeature.h" -#include "Hierarchy/HierarchyPanel.h" -#include "Inspector/InspectorPanel.h" -#include "Project/ProjectPanel.h" -#include "Scene/SceneEditCommandRoute.h" -#include "Scene/SceneViewportFeature.h" - -#include - -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -struct EditorProductPanelRuntimeContext { - const std::vector& consoleEntries; - EditorCommandFocusService& commandFocusService; - EditorProjectRuntime& projectRuntime; - EditorSceneRuntime& sceneRuntime; - EditorColorPickerToolState& colorPickerToolState; - ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost = - nullptr; - const RequestOpenUtilityWindowCallback& requestOpenUtilityWindow; - const RequestOpenSceneAssetCallback& requestOpenSceneAsset; -}; - -using EditorWorkspacePanelFactory = - std::unique_ptr(*)( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context); - -constexpr int kSceneUpdatePriority = 0; -constexpr int kGameUpdatePriority = 5; -constexpr int kHierarchyUpdatePriority = 10; -constexpr int kProjectUpdatePriority = 20; -constexpr int kInspectorUpdatePriority = 30; -constexpr int kConsoleUpdatePriority = 40; - -const UIEditorHostedPanelDispatchEntry& ResolveHostedPanelDispatchEntry( - const UIEditorHostedPanelDispatchFrame& dispatchFrame, - std::string_view panelId) { - static const UIEditorHostedPanelDispatchEntry kEmptyEntry = {}; - if (const UIEditorHostedPanelDispatchEntry* entry = - FindUIEditorHostedPanelDispatchEntry(dispatchFrame, panelId); - entry != nullptr) { - return *entry; - } - - return kEmptyEntry; -} - -std::string_view DescribeProjectItemKind(ProjectBrowserModel::ItemKind kind) { - switch (kind) { - case ProjectBrowserModel::ItemKind::Folder: - return "Folder"; - case ProjectBrowserModel::ItemKind::Scene: - return "Scene"; - case ProjectBrowserModel::ItemKind::Model: - return "Model"; - case ProjectBrowserModel::ItemKind::Material: - return "Material"; - case ProjectBrowserModel::ItemKind::Texture: - return "Texture"; - case ProjectBrowserModel::ItemKind::Script: - return "Script"; - case ProjectBrowserModel::ItemKind::File: - default: - return "File"; - } -} - -std::string DescribeProjectPanelEvent(const ProjectPanel::Event& event) { - std::ostringstream stream = {}; - switch (event.kind) { - case ProjectPanel::EventKind::AssetSelected: - stream << "AssetSelected"; - break; - case ProjectPanel::EventKind::AssetSelectionCleared: - stream << "AssetSelectionCleared"; - break; - case ProjectPanel::EventKind::FolderNavigated: - stream << "FolderNavigated"; - break; - case ProjectPanel::EventKind::AssetOpened: - stream << "AssetOpened"; - break; - case ProjectPanel::EventKind::RenameRequested: - stream << "RenameRequested"; - break; - case ProjectPanel::EventKind::ContextMenuRequested: - stream << "ContextMenuRequested"; - break; - case ProjectPanel::EventKind::None: - default: - stream << "None"; - break; - } - - stream << " source="; - switch (event.source) { - case ProjectPanel::EventSource::Tree: - stream << "Tree"; - break; - case ProjectPanel::EventSource::Breadcrumb: - stream << "Breadcrumb"; - break; - case ProjectPanel::EventSource::GridPrimary: - stream << "GridPrimary"; - break; - case ProjectPanel::EventSource::GridDoubleClick: - stream << "GridDoubleClick"; - break; - case ProjectPanel::EventSource::GridSecondary: - stream << "GridSecondary"; - break; - case ProjectPanel::EventSource::GridDrag: - stream << "GridDrag"; - break; - case ProjectPanel::EventSource::Command: - stream << "Command"; - break; - case ProjectPanel::EventSource::Background: - stream << "Background"; - break; - case ProjectPanel::EventSource::None: - default: - stream << "None"; - break; - } - - if (!event.itemId.empty()) { - stream << " item=" << event.itemId; - } - if (!event.displayName.empty()) { - stream << " label=" << event.displayName; - } - if (!event.itemId.empty()) { - stream << " kind=" << DescribeProjectItemKind(event.itemKind); - } - return stream.str(); -} - -std::string DescribeHierarchyPanelEvent(const HierarchyPanel::Event& event) { - std::ostringstream stream = {}; - switch (event.kind) { - case HierarchyPanel::EventKind::SelectionChanged: - stream << "SelectionChanged"; - break; - case HierarchyPanel::EventKind::Reparented: - stream << "Reparented"; - break; - case HierarchyPanel::EventKind::MovedToRoot: - stream << "MovedToRoot"; - break; - case HierarchyPanel::EventKind::RenameRequested: - stream << "RenameRequested"; - break; - case HierarchyPanel::EventKind::None: - default: - stream << "None"; - break; - } - - if (!event.itemId.empty()) { - stream << " item=" << event.itemId; - } - if (!event.targetItemId.empty()) { - stream << " target=" << event.targetItemId; - } - if (!event.label.empty()) { - stream << " label=" << event.label; - } - return stream.str(); -} - -class ConsoleWorkspacePanel final : public EditorWorkspacePanel { -public: - ConsoleWorkspacePanel( - EditorActionRoute actionRoute, - const std::vector& entries) - : m_actionRoute(actionRoute) - , m_entries(entries) {} - - std::string_view GetPanelId() const override { - return kConsolePanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.Console"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - EditorWorkspacePanelUpdatePhase GetUpdatePhase() const override { - return EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync; - } - - int GetUpdatePriority() const override { - return kConsoleUpdatePriority; - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_panel.Update( - m_entries, - ResolveHostedPanelDispatchEntry( - context.shellFrame.hostedPanelDispatchFrame, - GetPanelId())); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.Append(drawList); - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - const std::vector& m_entries; - ConsolePanel m_panel = {}; -}; - -class HierarchyWorkspacePanel final : public EditorWorkspacePanel { -public: - HierarchyWorkspacePanel( - EditorActionRoute actionRoute, - EditorSceneRuntime& sceneRuntime, - EditorCommandFocusService& commandFocusService) - : m_actionRoute(actionRoute) - , m_sceneRuntime(sceneRuntime) - , m_commandFocusService(commandFocusService) {} - - std::string_view GetPanelId() const override { - return kHierarchyPanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.Hierarchy"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - int GetUpdatePriority() const override { - return kHierarchyUpdatePriority; - } - - void Initialize(const EditorWorkspacePanelInitializationContext& context) override { - m_panel.SetIconService(&context.iconService); - m_panel.SetTextMeasurer(&context.textMeasurer); - m_panel.Initialize(); - } - - void ResetInteractionState() override { - m_panel.ResetInteractionState(); - } - - void PrepareForShellDefinition(UIEditorWorkspaceController&) override { - m_panel.SetSceneRuntime(&m_sceneRuntime); - m_panel.SetCommandFocusService(&m_commandFocusService); - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_panel.Update( - ResolveHostedPanelDispatchEntry( - context.shellFrame.hostedPanelDispatchFrame, - GetPanelId()), - context.hostedContentEvents); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.Append(drawList); - } - - bool HasActivePointerCapture() const override { - return m_panel.HasActivePointerCapture(); - } - - EditorEditCommandRoute* GetEditCommandRoute() override { - return &m_panel; - } - - std::vector CollectFrameEvents() const override { - std::vector events = {}; - for (const HierarchyPanel::Event& event : m_panel.GetFrameEvents()) { - events.push_back(EditorWorkspacePanelFrameEvent{ - .status = std::string(kHierarchyPanelTitle), - .traceChannel = std::string(kHierarchyPanelId), - .message = DescribeHierarchyPanelEvent(event), - }); - } - return events; - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - EditorSceneRuntime& m_sceneRuntime; - EditorCommandFocusService& m_commandFocusService; - HierarchyPanel m_panel = {}; -}; - -class InspectorWorkspacePanel final : public EditorWorkspacePanel { -public: - InspectorWorkspacePanel( - EditorActionRoute actionRoute, - EditorCommandFocusService& commandFocusService, - EditorProjectRuntime& projectRuntime, - EditorSceneRuntime& sceneRuntime, - EditorColorPickerToolState& colorPickerToolState, - RequestOpenUtilityWindowCallback requestOpenUtilityWindow) - : m_actionRoute(actionRoute) - , m_commandFocusService(commandFocusService) - , m_projectRuntime(projectRuntime) - , m_sceneRuntime(sceneRuntime) - , m_colorPickerToolState(colorPickerToolState) - , m_requestOpenUtilityWindow(std::move(requestOpenUtilityWindow)) {} - - std::string_view GetPanelId() const override { - return kInspectorPanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.Inspector"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - int GetUpdatePriority() const override { - return kInspectorUpdatePriority; - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_panel.SetCommandFocusService(&m_commandFocusService); - InspectorPanelContext panelContext{ - .projectRuntime = m_projectRuntime, - .sceneRuntime = m_sceneRuntime, - .colorPickerToolState = m_colorPickerToolState, - .textMeasurer = m_textMeasurer, - .requestOpenUtilityWindow = m_requestOpenUtilityWindow, - }; - m_panel.Update( - panelContext, - ResolveHostedPanelDispatchEntry( - context.shellFrame.hostedPanelDispatchFrame, - GetPanelId()), - context.hostedContentEvents); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.Append(drawList); - } - - void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.AppendOverlay(drawList); - } - - std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const override { - return m_panel.CollectInteractiveOverlayBounds(); - } - - void Initialize(const EditorWorkspacePanelInitializationContext& context) override { - m_textMeasurer = &context.textMeasurer; - } - - EditorEditCommandRoute* GetEditCommandRoute() override { - return &m_panel; - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - EditorCommandFocusService& m_commandFocusService; - EditorProjectRuntime& m_projectRuntime; - EditorSceneRuntime& m_sceneRuntime; - EditorColorPickerToolState& m_colorPickerToolState; - RequestOpenUtilityWindowCallback m_requestOpenUtilityWindow = {}; - const UIEditorTextMeasurer* m_textMeasurer = nullptr; - InspectorPanel m_panel = {}; -}; - -class ProjectWorkspacePanel final : public EditorWorkspacePanel { -public: - ProjectWorkspacePanel( - EditorActionRoute actionRoute, - EditorProjectRuntime& projectRuntime, - EditorCommandFocusService& commandFocusService, - ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, - RequestOpenSceneAssetCallback requestOpenSceneAsset) - : m_actionRoute(actionRoute) - , m_projectRuntime(projectRuntime) - , m_commandFocusService(commandFocusService) - , m_systemInteractionHost(systemInteractionHost) - , m_requestOpenSceneAsset(std::move(requestOpenSceneAsset)) {} - - std::string_view GetPanelId() const override { - return kProjectPanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.Project"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - int GetUpdatePriority() const override { - return kProjectUpdatePriority; - } - - void Initialize(const EditorWorkspacePanelInitializationContext& context) override { - m_panel.SetIconService(&context.iconService); - m_panel.SetTextMeasurer(&context.textMeasurer); - } - - void ResetInteractionState() override { - m_panel.ResetInteractionState(); - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_panel.SetProjectRuntime(&m_projectRuntime); - m_panel.SetCommandFocusService(&m_commandFocusService); - m_panel.SetSystemInteractionHost(m_systemInteractionHost); - m_panel.SetSceneAssetOpenRequestHandler(m_requestOpenSceneAsset); - m_panel.Update( - ResolveHostedPanelDispatchEntry( - context.shellFrame.hostedPanelDispatchFrame, - GetPanelId()), - context.hostedContentEvents); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.Append(drawList); - } - - void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const override { - m_panel.AppendOverlay(drawList); - } - - std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const override { - return m_panel.CollectInteractiveOverlayBounds(); - } - - bool HasActivePointerCapture() const override { - return m_panel.HasActivePointerCapture(); - } - - EditorWorkspacePanelCursorKind GetCursorKind() const override { - switch (m_panel.GetCursorKind()) { - case ProjectPanel::CursorKind::ResizeEW: - return EditorWorkspacePanelCursorKind::ResizeEW; - case ProjectPanel::CursorKind::Arrow: - default: - return EditorWorkspacePanelCursorKind::Arrow; - } - } - - EditorEditCommandRoute* GetEditCommandRoute() override { - return &m_panel; - } - - std::vector CollectFrameEvents() const override { - std::vector events = {}; - for (const ProjectPanel::Event& event : m_panel.GetFrameEvents()) { - events.push_back(EditorWorkspacePanelFrameEvent{ - .status = std::string(kProjectPanelTitle), - .traceChannel = std::string(kProjectPanelId), - .message = DescribeProjectPanelEvent(event), - }); - } - return events; - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - EditorProjectRuntime& m_projectRuntime; - EditorCommandFocusService& m_commandFocusService; - ::XCEngine::UI::Editor::System::SystemInteractionService* m_systemInteractionHost = - nullptr; - RequestOpenSceneAssetCallback m_requestOpenSceneAsset = {}; - ProjectPanel m_panel = {}; -}; - -class SceneWorkspacePanel final : public EditorWorkspacePanel { -public: - SceneWorkspacePanel( - EditorActionRoute actionRoute, - EditorSceneRuntime& sceneRuntime, - EditorCommandFocusService& commandFocusService) - : m_actionRoute(actionRoute) - , m_sceneRuntime(sceneRuntime) - , m_commandFocusService(commandFocusService) {} - - std::string_view GetPanelId() const override { - return kScenePanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.SceneOverlay"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - int GetUpdatePriority() const override { - return kSceneUpdatePriority; - } - - void Initialize(const EditorWorkspacePanelInitializationContext& context) override { - if (context.sceneViewportRuntime != nullptr) { - m_feature.Initialize( - &context.iconService, - *context.sceneViewportRuntime); - } - } - - void Shutdown(const EditorWorkspacePanelShutdownContext& context) override { - (void)context; - m_feature.Shutdown(); - m_commandRoute.BindSceneRuntime(nullptr); - } - - void ResetInteractionState() override { - m_feature.ResetInteractionState(); - } - - void PrepareForShellDefinition(UIEditorWorkspaceController&) override { - m_commandRoute.BindSceneRuntime(&m_sceneRuntime); - m_feature.SetCommandFocusService(&m_commandFocusService); - m_feature.SyncRenderRequest(m_sceneRuntime); - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_feature.Update( - m_sceneRuntime, - context.shellInteractionState.workspaceInteractionState.composeState, - context.shellFrame.workspaceInteractionFrame.composeFrame); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_feature.Append(drawList); - } - - EditorEditCommandRoute* GetEditCommandRoute() override { - return &m_commandRoute; - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - EditorSceneRuntime& m_sceneRuntime; - EditorCommandFocusService& m_commandFocusService; - SceneViewportFeature m_feature = {}; - SceneEditCommandRoute m_commandRoute = {}; -}; - -class GameWorkspacePanel final : public EditorWorkspacePanel { -public: - GameWorkspacePanel( - EditorActionRoute actionRoute, - EditorCommandFocusService& commandFocusService) - : m_actionRoute(actionRoute) - , m_commandFocusService(commandFocusService) {} - - std::string_view GetPanelId() const override { - return kGamePanelId; - } - - std::string_view GetDrawListId() const override { - return "XCEditorPanel.GameOverlay"; - } - - EditorActionRoute GetActionRoute() const override { - return m_actionRoute; - } - - int GetUpdatePriority() const override { - return kGameUpdatePriority; - } - - void Shutdown(const EditorWorkspacePanelShutdownContext& context) override { - (void)context; - m_feature.Shutdown(); - } - - void ResetInteractionState() override { - m_feature.ResetInteractionState(); - } - - void PrepareForShellDefinition(UIEditorWorkspaceController&) override { - m_feature.SetCommandFocusService(&m_commandFocusService); - } - - void Update(const EditorWorkspacePanelUpdateContext& context) override { - m_feature.Update( - context.shellInteractionState.workspaceInteractionState.composeState, - context.shellFrame.workspaceInteractionFrame.composeFrame); - } - - void Append(::XCEngine::UI::UIDrawList& drawList) const override { - m_feature.Append(drawList); - } - -private: - EditorActionRoute m_actionRoute = EditorActionRoute::None; - EditorCommandFocusService& m_commandFocusService; - GameViewportFeature m_feature = {}; -}; - -std::unique_ptr CreateConsoleWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.consoleEntries); -} - -std::unique_ptr CreateHierarchyWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.sceneRuntime, - context.commandFocusService); -} - -std::unique_ptr CreateGameWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.commandFocusService); -} - -std::unique_ptr CreateInspectorWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.commandFocusService, - context.projectRuntime, - context.sceneRuntime, - context.colorPickerToolState, - context.requestOpenUtilityWindow); -} - -std::unique_ptr CreateProjectWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.projectRuntime, - context.commandFocusService, - context.systemInteractionHost, - context.requestOpenSceneAsset); -} - -std::unique_ptr CreateSceneWorkspacePanel( - const EditorProductPanelDescriptor& panel, - const EditorProductPanelRuntimeContext& context) { - return std::make_unique( - panel.actionRoute, - context.sceneRuntime, - context.commandFocusService); -} - -EditorWorkspacePanelFactory ResolveEditorWorkspacePanelFactory(std::string_view panelId) { - if (panelId == kConsolePanelId) { - return &CreateConsoleWorkspacePanel; - } - if (panelId == kHierarchyPanelId) { - return &CreateHierarchyWorkspacePanel; - } - if (panelId == kGamePanelId) { - return &CreateGameWorkspacePanel; - } - if (panelId == kInspectorPanelId) { - return &CreateInspectorWorkspacePanel; - } - if (panelId == kProjectPanelId) { - return &CreateProjectWorkspacePanel; - } - if (panelId == kScenePanelId) { - return &CreateSceneWorkspacePanel; - } - return nullptr; -} - -EditorWorkspacePanelCompositionValidationResult BuildCompositionError( - EditorWorkspacePanelCompositionValidationCode code, - std::string message) { - EditorWorkspacePanelCompositionValidationResult result = {}; - result.code = code; - result.message = std::move(message); - return result; -} - -} // namespace - -EditorWorkspacePanelCompositionValidationResult ValidateEditorWorkspacePanelComposition() { - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - if (ResolveEditorWorkspacePanelFactory(panel.panelId) != nullptr) { - continue; - } - - std::ostringstream message = {}; - message << "Editor product panel '" << panel.panelId - << "' has no composed runtime factory."; - return BuildCompositionError( - EditorWorkspacePanelCompositionValidationCode::MissingRuntimeFactory, - message.str()); - } - - return {}; -} - -EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet( - const std::vector& consoleEntries, - EditorProjectRuntime& projectRuntime, - EditorSceneRuntime& sceneRuntime, - EditorColorPickerToolState& colorPickerToolState, - ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, - RequestOpenUtilityWindowCallback requestOpenUtilityWindow, - RequestOpenSceneAssetCallback requestOpenSceneAsset) { - EditorWorkspacePanelRuntimeSet panels = {}; - EditorCommandFocusService& commandFocusService = - panels.GetCommandFocusService(); - const EditorProductPanelRuntimeContext runtimeContext = { - .consoleEntries = consoleEntries, - .commandFocusService = commandFocusService, - .projectRuntime = projectRuntime, - .sceneRuntime = sceneRuntime, - .colorPickerToolState = colorPickerToolState, - .systemInteractionHost = systemInteractionHost, - .requestOpenUtilityWindow = requestOpenUtilityWindow, - .requestOpenSceneAsset = requestOpenSceneAsset, - }; - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - const EditorWorkspacePanelFactory factory = - ResolveEditorWorkspacePanelFactory(panel.panelId); - if (factory == nullptr) { - continue; - } - - if (std::unique_ptr runtime = - factory(panel, runtimeContext); - runtime != nullptr) { - panels.AddPanel(std::move(runtime)); - } - } - return panels; -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorWorkspacePanelRegistry.h b/editor/app/Composition/EditorWorkspacePanelRegistry.h deleted file mode 100644 index a176a7a5..00000000 --- a/editor/app/Composition/EditorWorkspacePanelRegistry.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" - -#include -#include - -namespace XCEngine::UI::Editor::App { - -enum class EditorWorkspacePanelCompositionValidationCode : std::uint8_t { - None = 0, - MissingRuntimeFactory, -}; - -struct EditorWorkspacePanelCompositionValidationResult { - EditorWorkspacePanelCompositionValidationCode code = - EditorWorkspacePanelCompositionValidationCode::None; - std::string message = {}; - - [[nodiscard]] bool IsValid() const { - return code == EditorWorkspacePanelCompositionValidationCode::None; - } -}; - -EditorWorkspacePanelCompositionValidationResult ValidateEditorWorkspacePanelComposition(); - -EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet( - const std::vector& consoleEntries, - EditorProjectRuntime& projectRuntime, - EditorSceneRuntime& sceneRuntime, - EditorColorPickerToolState& colorPickerToolState, - ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, - RequestOpenUtilityWindowCallback requestOpenUtilityWindow, - RequestOpenSceneAssetCallback requestOpenSceneAsset); - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Panels/EditorPanelIds.h b/editor/app/Core/Panels/EditorPanelIds.h deleted file mode 100644 index a10f4bb6..00000000 --- a/editor/app/Core/Panels/EditorPanelIds.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace XCEngine::UI::Editor::App { - -inline constexpr std::string_view kHierarchyPanelId = "hierarchy"; -inline constexpr std::string_view kScenePanelId = "scene"; -inline constexpr std::string_view kGamePanelId = "game"; -inline constexpr std::string_view kInspectorPanelId = "inspector"; -inline constexpr std::string_view kConsolePanelId = "console"; -inline constexpr std::string_view kProjectPanelId = "project"; - -inline constexpr std::string_view kHierarchyPanelTitle = "Hierarchy"; -inline constexpr std::string_view kScenePanelTitle = "Scene"; -inline constexpr std::string_view kGamePanelTitle = "Game"; -inline constexpr std::string_view kInspectorPanelTitle = "Inspector"; -inline constexpr std::string_view kConsolePanelTitle = "Console"; -inline constexpr std::string_view kProjectPanelTitle = "Project"; - -[[nodiscard]] bool IsEditorViewportPanelId(std::string_view panelId); - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Product/EditorProductManifest.cpp b/editor/app/Core/Product/EditorProductManifest.cpp deleted file mode 100644 index e3f92400..00000000 --- a/editor/app/Core/Product/EditorProductManifest.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "Product/EditorProductManifest.h" - -#include "Panels/EditorPanelIds.h" - -#include -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -constexpr std::array kEditorProductPanels = { - EditorProductPanelDescriptor{ - kHierarchyPanelId, - kHierarchyPanelTitle, - UIEditorPanelPresentationKind::HostedContent, - true, - false, - false, - false, - false, - EditorActionRoute::Hierarchy, - EditorProductViewportKind::None, - {} }, - EditorProductPanelDescriptor{ - kScenePanelId, - kScenePanelTitle, - UIEditorPanelPresentationKind::ViewportShell, - false, - false, - false, - false, - false, - EditorActionRoute::Scene, - EditorProductViewportKind::Scene, - {} }, - EditorProductPanelDescriptor{ - kGamePanelId, - kGamePanelTitle, - UIEditorPanelPresentationKind::ViewportShell, - false, - false, - false, - false, - false, - EditorActionRoute::Game, - EditorProductViewportKind::Game, - {} }, - EditorProductPanelDescriptor{ - kInspectorPanelId, - kInspectorPanelTitle, - UIEditorPanelPresentationKind::HostedContent, - true, - false, - false, - false, - false, - EditorActionRoute::Inspector, - EditorProductViewportKind::None, - {} }, - EditorProductPanelDescriptor{ - kConsolePanelId, - kConsolePanelTitle, - UIEditorPanelPresentationKind::HostedContent, - true, - false, - false, - false, - false, - EditorActionRoute::Console, - EditorProductViewportKind::None, - {} }, - EditorProductPanelDescriptor{ - kProjectPanelId, - kProjectPanelTitle, - UIEditorPanelPresentationKind::HostedContent, - false, - false, - false, - false, - false, - EditorActionRoute::Project, - EditorProductViewportKind::None, - {} }, -}; - -EditorProductManifestValidationResult BuildManifestError( - EditorProductManifestValidationCode code, - std::string_view message) { - EditorProductManifestValidationResult result = {}; - result.code = code; - result.message = std::string(message); - return result; -} - -} // namespace - -std::span GetEditorProductPanels() { - return kEditorProductPanels; -} - -const EditorProductPanelDescriptor* FindEditorProductPanel( - std::string_view panelId) { - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - if (panel.panelId == panelId) { - return &panel; - } - } - - return nullptr; -} - -bool IsEditorProductViewportPanel(std::string_view panelId) { - const EditorProductPanelDescriptor* panel = FindEditorProductPanel(panelId); - return panel != nullptr && - panel->presentationKind == UIEditorPanelPresentationKind::ViewportShell; -} - -EditorProductManifestValidationResult ValidateEditorProductManifest() { - const std::span panels = - GetEditorProductPanels(); - for (std::size_t index = 0u; index < panels.size(); ++index) { - const EditorProductPanelDescriptor& panel = panels[index]; - if (panel.panelId.empty()) { - return BuildManifestError( - EditorProductManifestValidationCode::EmptyPanelId, - "Editor product manifest contains a panel with an empty id."); - } - if (panel.defaultTitle.empty()) { - return BuildManifestError( - EditorProductManifestValidationCode::EmptyPanelTitle, - "Editor product manifest contains a panel with an empty title."); - } - - for (std::size_t other = index + 1u; other < panels.size(); ++other) { - if (panels[other].panelId == panel.panelId) { - std::ostringstream message = {}; - message << "Editor product manifest duplicates panel id '" - << panel.panelId << "'."; - return BuildManifestError( - EditorProductManifestValidationCode::DuplicatePanelId, - message.str()); - } - } - - if (panel.presentationKind == UIEditorPanelPresentationKind::ViewportShell && - panel.viewportKind == EditorProductViewportKind::None) { - std::ostringstream message = {}; - message << "Viewport panel '" << panel.panelId - << "' declares no viewport kind."; - return BuildManifestError( - EditorProductManifestValidationCode::ViewportPanelMissingViewportKind, - message.str()); - } - - if (panel.presentationKind != UIEditorPanelPresentationKind::ViewportShell && - panel.viewportKind != EditorProductViewportKind::None) { - std::ostringstream message = {}; - message << "Non-viewport panel '" << panel.panelId - << "' declares a viewport kind."; - return BuildManifestError( - EditorProductManifestValidationCode::NonViewportPanelHasViewportKind, - message.str()); - } - - if (panel.viewportKind == EditorProductViewportKind::Placeholder && - panel.viewportPlaceholderStatus.empty()) { - std::ostringstream message = {}; - message << "Placeholder viewport panel '" << panel.panelId - << "' has no status text."; - return BuildManifestError( - EditorProductManifestValidationCode::PlaceholderViewportMissingStatus, - message.str()); - } - } - - return {}; -} - -bool IsEditorViewportPanelId(std::string_view panelId) { - return IsEditorProductViewportPanel(panelId); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Product/EditorProductManifest.h b/editor/app/Core/Product/EditorProductManifest.h deleted file mode 100644 index 51dba556..00000000 --- a/editor/app/Core/Product/EditorProductManifest.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "State/EditorSession.h" - -#include - -#include -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -enum class EditorProductViewportKind : std::uint8_t { - None = 0, - Scene, - Game, - Placeholder, -}; - -struct EditorProductPanelDescriptor { - std::string_view panelId = {}; - std::string_view defaultTitle = {}; - UIEditorPanelPresentationKind presentationKind = - UIEditorPanelPresentationKind::Placeholder; - bool placeholder = true; - bool canHide = true; - bool canClose = true; - bool showViewportTopBar = false; - bool showViewportBottomBar = false; - EditorActionRoute actionRoute = EditorActionRoute::None; - EditorProductViewportKind viewportKind = EditorProductViewportKind::None; - std::string_view viewportPlaceholderStatus = {}; -}; - -enum class EditorProductManifestValidationCode : std::uint8_t { - None = 0, - EmptyPanelId, - EmptyPanelTitle, - DuplicatePanelId, - ViewportPanelMissingViewportKind, - NonViewportPanelHasViewportKind, - PlaceholderViewportMissingStatus -}; - -struct EditorProductManifestValidationResult { - EditorProductManifestValidationCode code = - EditorProductManifestValidationCode::None; - std::string message = {}; - - [[nodiscard]] bool IsValid() const { - return code == EditorProductManifestValidationCode::None; - } -}; - -std::span GetEditorProductPanels(); - -const EditorProductPanelDescriptor* FindEditorProductPanel( - std::string_view panelId); - -bool IsEditorProductViewportPanel(std::string_view panelId); - -EditorProductManifestValidationResult ValidateEditorProductManifest(); - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp b/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp deleted file mode 100644 index 79b76636..00000000 --- a/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "UtilityWindows/EditorUtilityWindowRegistry.h" - -namespace XCEngine::UI::Editor::App { - -namespace { - -using ::XCEngine::UI::UISize; - -constexpr EditorWindowChromePolicy kUtilityWindowChromePolicy = { - .allowDetachedTitleBarTabStrip = false, - .showFrameStats = false, - .showTopmostButton = true, - .topmostByDefault = true, -}; - -constexpr EditorWindowNativeHostPolicy kUtilityWindowNativeHostPolicy = { - .shellRole = EditorWindowNativeShellRole::ToolWindow, -}; - -constexpr EditorUtilityWindowDescriptor kColorPickerUtilityWindowDescriptor = { - .kind = EditorUtilityWindowKind::ColorPicker, - .windowId = "utility.color-picker", - .title = L"Color Picker", - .chromePolicy = kUtilityWindowChromePolicy, - .nativeHostPolicy = kUtilityWindowNativeHostPolicy, - .preferredOuterSize = UISize(400.0f, 600.0f), - .minimumOuterSize = UISize(360.0f, 560.0f), -}; - -constexpr EditorUtilityWindowDescriptor kAddComponentUtilityWindowDescriptor = { - .kind = EditorUtilityWindowKind::AddComponent, - .windowId = "utility.add-component", - .title = L"Add Component", - .chromePolicy = kUtilityWindowChromePolicy, - .nativeHostPolicy = kUtilityWindowNativeHostPolicy, - .preferredOuterSize = UISize(352.0f, 500.0f), - .minimumOuterSize = UISize(320.0f, 460.0f), -}; - -} // namespace - -const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor( - EditorUtilityWindowKind kind) { - switch (kind) { - case EditorUtilityWindowKind::ColorPicker: - return &kColorPickerUtilityWindowDescriptor; - case EditorUtilityWindowKind::AddComponent: - return &kAddComponentUtilityWindowDescriptor; - case EditorUtilityWindowKind::None: - default: - return nullptr; - } -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.h b/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.h deleted file mode 100644 index 43778bda..00000000 --- a/editor/app/Core/UtilityWindows/EditorUtilityWindowRegistry.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "UtilityWindows/EditorUtilityWindowRuntime.h" - -namespace XCEngine::UI::Editor::App { - -const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor( - EditorUtilityWindowKind kind); - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Windowing/Contracts/EditorFrameStatusService.h b/editor/app/Core/Windowing/Contracts/EditorFrameStatusService.h deleted file mode 100644 index 46b354a8..00000000 --- a/editor/app/Core/Windowing/Contracts/EditorFrameStatusService.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" - -#include -#include - -#include -#include - -namespace XCEngine::UI::Editor::App { - -struct WorkspaceTraceEntry { - std::string channel = {}; - std::string message = {}; -}; - -class EditorFrameStatusService { -public: - virtual ~EditorFrameStatusService() = default; - - virtual void SetStatus(std::string status, std::string message) = 0; - virtual void SetReadyStatus() = 0; - virtual std::string ComposeStatusText() const = 0; - virtual void UpdateStatusFromShellResult( - const UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionResult& result) = 0; - virtual std::string DescribeWorkspaceState( - const UIEditorWorkspaceController& workspaceController, - const UIEditorShellInteractionState& interactionState) const = 0; - virtual std::vector SyncWorkspacePanelFrameEvents( - const std::vector& panelEvents) = 0; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Windowing/Contracts/EditorFrameValidation.h b/editor/app/Core/Windowing/Contracts/EditorFrameValidation.h deleted file mode 100644 index f9ec4576..00000000 --- a/editor/app/Core/Windowing/Contracts/EditorFrameValidation.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace XCEngine::UI::Editor::App { - -class EditorFrameValidation { -public: - virtual ~EditorFrameValidation() = default; - - virtual bool IsValid() const = 0; - virtual const std::string& GetValidationMessage() const = 0; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Windowing/Contracts/EditorShellDefinitionProvider.h b/editor/app/Core/Windowing/Contracts/EditorShellDefinitionProvider.h deleted file mode 100644 index d8732911..00000000 --- a/editor/app/Core/Windowing/Contracts/EditorShellDefinitionProvider.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Windowing/EditorShellVariant.h" - -#include -#include - -#include - -namespace XCEngine::UI::Editor::App { - -class EditorShellDefinitionProvider { -public: - virtual ~EditorShellDefinitionProvider() = default; - - virtual UIEditorShellInteractionDefinition BuildShellDefinition( - const UIEditorWorkspaceController& workspaceController, - std::string_view statusText, - std::string_view captureText, - EditorShellVariant variant) const = 0; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/Windowing/EditorFrameContracts.h b/editor/app/Core/Windowing/EditorFrameContracts.h deleted file mode 100644 index 5f4c396d..00000000 --- a/editor/app/Core/Windowing/EditorFrameContracts.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "Windowing/Contracts/EditorFrameStatusService.h" -#include "Windowing/Contracts/EditorFrameValidation.h" -#include "Windowing/Contracts/EditorShellDefinitionProvider.h" diff --git a/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp b/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp deleted file mode 100644 index 1a4730a2..00000000 --- a/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" - -#include -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -template -void AppendDrawPacket( - ::XCEngine::UI::UIDrawData& drawData, - std::string debugName, - AppendFn&& appendFn) { - ::XCEngine::UI::UIDrawList drawList(std::move(debugName)); - appendFn(drawList); - if (!drawList.Empty()) { - drawData.AddDrawList(std::move(drawList)); - } -} - -} // namespace - -void EditorWorkspacePanelRuntimeSet::AddPanel( - std::unique_ptr panel) { - if (panel != nullptr) { - m_panels.push_back(std::move(panel)); - } -} - -EditorCommandFocusService& EditorWorkspacePanelRuntimeSet::GetCommandFocusService() { - return m_commandFocusService; -} - -const EditorCommandFocusService& -EditorWorkspacePanelRuntimeSet::GetCommandFocusService() const { - return m_commandFocusService; -} - -void EditorWorkspacePanelRuntimeSet::Initialize( - const EditorWorkspacePanelInitializationContext& context) { - for (const std::unique_ptr& panel : m_panels) { - panel->Initialize(context); - } -} - -void EditorWorkspacePanelRuntimeSet::Shutdown( - const EditorWorkspacePanelShutdownContext& context) { - for (const std::unique_ptr& panel : m_panels) { - panel->Shutdown(context); - } -} - -void EditorWorkspacePanelRuntimeSet::ResetInteractionState() { - m_commandFocusService.ClearFocus(); - for (const std::unique_ptr& panel : m_panels) { - panel->ResetInteractionState(); - } -} - -void EditorWorkspacePanelRuntimeSet::PrepareForShellDefinition( - UIEditorWorkspaceController& workspaceController) { - for (const std::unique_ptr& panel : m_panels) { - panel->PrepareForShellDefinition(workspaceController); - } -} - -void EditorWorkspacePanelRuntimeSet::UpdatePhase( - const EditorWorkspacePanelUpdateContext& context, - EditorWorkspacePanelUpdatePhase phase) { - const std::vector phasePanels = - BuildUpdateOrder(phase); - for (EditorWorkspacePanel* panel : phasePanels) { - panel->Update(context); - } -} - -void EditorWorkspacePanelRuntimeSet::AppendDrawPackets( - ::XCEngine::UI::UIDrawData& drawData) const { - for (const std::unique_ptr& panel : m_panels) { - AppendDrawPacket( - drawData, - std::string(panel->GetDrawListId()), - [&](::XCEngine::UI::UIDrawList& drawList) { - panel->Append(drawList); - }); - } -} - -void EditorWorkspacePanelRuntimeSet::AppendOverlayDrawPackets( - ::XCEngine::UI::UIDrawData& drawData) const { - for (const std::unique_ptr& panel : m_panels) { - AppendDrawPacket( - drawData, - std::string(panel->GetDrawListId()) + ".Overlay", - [&](::XCEngine::UI::UIDrawList& drawList) { - panel->AppendOverlay(drawList); - }); - } -} - -std::vector -EditorWorkspacePanelRuntimeSet::CollectPanelOverlayRegions() const { - std::vector regions = {}; - for (const std::unique_ptr& panel : m_panels) { - const std::vector<::XCEngine::UI::UIRect> panelBounds = - panel->CollectInteractiveOverlayBounds(); - regions.reserve(regions.size() + panelBounds.size()); - for (const ::XCEngine::UI::UIRect& bounds : panelBounds) { - UIEditorWorkspacePanelOverlayRegion region = {}; - region.panelId = std::string(panel->GetPanelId()); - region.bounds = bounds; - regions.push_back(std::move(region)); - } - } - return regions; -} - -bool EditorWorkspacePanelRuntimeSet::HasActivePointerCapture() const { - for (const std::unique_ptr& panel : m_panels) { - if (panel->HasActivePointerCapture()) { - return true; - } - } - - return false; -} - -EditorWorkspacePanelCursorKind -EditorWorkspacePanelRuntimeSet::GetHostedContentCursorKind() const { - for (const std::unique_ptr& panel : m_panels) { - const EditorWorkspacePanelCursorKind cursorKind = panel->GetCursorKind(); - if (cursorKind != EditorWorkspacePanelCursorKind::Arrow) { - return cursorKind; - } - } - - return EditorWorkspacePanelCursorKind::Arrow; -} - -EditorEditCommandRoute* EditorWorkspacePanelRuntimeSet::FindCommandRoute( - EditorActionRoute route) { - for (const std::unique_ptr& panel : m_panels) { - if (panel->GetActionRoute() == route) { - return panel->GetEditCommandRoute(); - } - } - - return nullptr; -} - -std::vector -EditorWorkspacePanelRuntimeSet::CollectCommandRoutes() const { - std::vector bindings = {}; - bindings.reserve(m_panels.size()); - for (const std::unique_ptr& panel : m_panels) { - const EditorActionRoute route = panel->GetActionRoute(); - if (route == EditorActionRoute::None) { - continue; - } - - bindings.push_back(EditorWorkspacePanelCommandRouteBinding{ - .route = route, - .editCommandRoute = panel->GetEditCommandRoute(), - }); - } - return bindings; -} - -std::vector -EditorWorkspacePanelRuntimeSet::CollectFrameEvents() const { - std::vector events = {}; - for (const std::unique_ptr& panel : m_panels) { - std::vector panelEvents = - panel->CollectFrameEvents(); - events.insert( - events.end(), - std::make_move_iterator(panelEvents.begin()), - std::make_move_iterator(panelEvents.end())); - } - return events; -} - -std::vector EditorWorkspacePanelRuntimeSet::BuildUpdateOrder( - EditorWorkspacePanelUpdatePhase phase) const { - std::vector panels = {}; - panels.reserve(m_panels.size()); - for (const std::unique_ptr& panel : m_panels) { - if (panel->GetUpdatePhase() == phase) { - panels.push_back(panel.get()); - } - } - - std::sort( - panels.begin(), - panels.end(), - [](const EditorWorkspacePanel* left, const EditorWorkspacePanel* right) { - return left->GetUpdatePriority() < right->GetUpdatePriority(); - }); - return panels; -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.h b/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.h deleted file mode 100644 index 5731807a..00000000 --- a/editor/app/Core/WorkspacePanels/EditorWorkspacePanelRuntime.h +++ /dev/null @@ -1,165 +0,0 @@ -#pragma once - -#include "Assets/EditorIconService.h" -#include "Commands/EditorEditCommandRoute.h" -#include "Environment/EditorRuntimePaths.h" -#include "State/EditorColorPickerToolState.h" -#include "State/EditorCommandFocusService.h" -#include "State/EditorSession.h" -#include "Viewport/EditorViewportRuntimeServices.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace XCEngine::UI { - -struct UIInputEvent; - -} // namespace XCEngine::UI - -namespace XCEngine::UI::Editor::System { - -class SystemInteractionService; - -} // namespace XCEngine::UI::Editor::System - -namespace XCEngine::UI::Editor::App { - -class EditorCommandFocusService; -class EditorEditCommandRoute; -class EditorProjectRuntime; -class EditorSceneRuntime; -enum class EditorUtilityWindowKind : std::uint8_t; - -using RequestOpenSceneAssetCallback = - std::function; -using RequestOpenUtilityWindowCallback = - std::function; - -enum class EditorWorkspacePanelCursorKind : std::uint8_t { - Arrow = 0, - ResizeEW -}; - -enum class EditorWorkspacePanelUpdatePhase : std::uint8_t { - Main = 0, - AfterCommandFocusSync -}; - -struct EditorWorkspacePanelFrameEvent { - std::string status = {}; - std::string traceChannel = {}; - std::string message = {}; -}; - -struct EditorWorkspacePanelCommandRouteBinding { - EditorActionRoute route = EditorActionRoute::None; - EditorEditCommandRoute* editCommandRoute = nullptr; -}; - -struct EditorWorkspacePanelInitializationContext { - const EditorRuntimePaths& runtimePaths; - UIEditorTextMeasurer& textMeasurer; - EditorIconService& iconService; - EditorSceneViewportRuntime* sceneViewportRuntime = nullptr; -}; - -struct EditorWorkspacePanelShutdownContext {}; - -struct EditorWorkspacePanelUpdateContext { - UIEditorShellInteractionFrame& shellFrame; - UIEditorShellInteractionState& shellInteractionState; - const std::vector<::XCEngine::UI::UIInputEvent>& hostedContentEvents; -}; - -class EditorWorkspacePanel { -public: - virtual ~EditorWorkspacePanel() = default; - - virtual std::string_view GetPanelId() const = 0; - virtual std::string_view GetDrawListId() const = 0; - virtual EditorActionRoute GetActionRoute() const = 0; - - virtual void Initialize(const EditorWorkspacePanelInitializationContext&) {} - virtual void Shutdown(const EditorWorkspacePanelShutdownContext&) {} - virtual void ResetInteractionState() {} - virtual void PrepareForShellDefinition(UIEditorWorkspaceController&) {} - virtual EditorWorkspacePanelUpdatePhase GetUpdatePhase() const { - return EditorWorkspacePanelUpdatePhase::Main; - } - virtual int GetUpdatePriority() const { - return 0; - } - virtual void Update(const EditorWorkspacePanelUpdateContext& context) = 0; - virtual void Append(::XCEngine::UI::UIDrawList& drawList) const = 0; - virtual void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const {} - virtual std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const { - return {}; - } - virtual bool HasActivePointerCapture() const { - return false; - } - virtual EditorWorkspacePanelCursorKind GetCursorKind() const { - return EditorWorkspacePanelCursorKind::Arrow; - } - virtual EditorEditCommandRoute* GetEditCommandRoute() { - return nullptr; - } - virtual std::vector CollectFrameEvents() const { - return {}; - } -}; - -class EditorWorkspacePanelRuntimeSet final { -public: - EditorWorkspacePanelRuntimeSet() = default; - EditorWorkspacePanelRuntimeSet(const EditorWorkspacePanelRuntimeSet&) = delete; - EditorWorkspacePanelRuntimeSet& operator=(const EditorWorkspacePanelRuntimeSet&) = delete; - EditorWorkspacePanelRuntimeSet(EditorWorkspacePanelRuntimeSet&&) noexcept = default; - EditorWorkspacePanelRuntimeSet& operator=(EditorWorkspacePanelRuntimeSet&&) noexcept = default; - - void AddPanel(std::unique_ptr panel); - EditorCommandFocusService& GetCommandFocusService(); - const EditorCommandFocusService& GetCommandFocusService() const; - void Initialize(const EditorWorkspacePanelInitializationContext& context); - void Shutdown(const EditorWorkspacePanelShutdownContext& context); - void ResetInteractionState(); - void PrepareForShellDefinition(UIEditorWorkspaceController& workspaceController); - void UpdatePhase( - const EditorWorkspacePanelUpdateContext& context, - EditorWorkspacePanelUpdatePhase phase); - void AppendDrawPackets(::XCEngine::UI::UIDrawData& drawData) const; - void AppendOverlayDrawPackets(::XCEngine::UI::UIDrawData& drawData) const; - std::vector CollectPanelOverlayRegions() const; - - bool HasActivePointerCapture() const; - EditorWorkspacePanelCursorKind GetHostedContentCursorKind() const; - EditorEditCommandRoute* FindCommandRoute(EditorActionRoute route); - std::vector CollectCommandRoutes() const; - std::vector CollectFrameEvents() const; - -private: - std::vector BuildUpdateOrder( - EditorWorkspacePanelUpdatePhase phase) const; - - EditorCommandFocusService m_commandFocusService = {}; - std::vector> m_panels = {}; -}; - -using EditorWorkspacePanelRuntimeSetFactory = - std::function; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Host/Interfaces/EditorWindowHostCoordinator.h b/editor/app/Host/Interfaces/EditorWindowHostCoordinator.h index c94ee79f..f283a60e 100644 --- a/editor/app/Host/Interfaces/EditorWindowHostCoordinator.h +++ b/editor/app/Host/Interfaces/EditorWindowHostCoordinator.h @@ -1,7 +1,7 @@ #pragma once #include "EditorWindowHostInterfaces.h" -#include "Windowing/EditorWindowTransferRequests.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Host/Interfaces/EditorWindowHostInterfaces.h b/editor/app/Host/Interfaces/EditorWindowHostInterfaces.h index 7198c39f..fc09719d 100644 --- a/editor/app/Host/Interfaces/EditorWindowHostInterfaces.h +++ b/editor/app/Host/Interfaces/EditorWindowHostInterfaces.h @@ -3,10 +3,10 @@ #include "EditorWindowHostTypes.h" #include "EditorWindowPointerCapture.h" #include "EditorWindowRenderRuntime.h" -#include "Environment/EditorRuntimePaths.h" -#include "Windowing/EditorWindowGeometry.h" -#include "Windowing/EditorWindowTransferRequests.h" -#include "Windowing/EditorWindowTypes.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Windowing/EditorWindowGeometry.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" +#include "Product/Core/Windowing/EditorWindowTypes.h" #include #include diff --git a/editor/app/Host/Interfaces/EditorWindowHostTypes.h b/editor/app/Host/Interfaces/EditorWindowHostTypes.h index 95a0e119..1c326101 100644 --- a/editor/app/Host/Interfaces/EditorWindowHostTypes.h +++ b/editor/app/Host/Interfaces/EditorWindowHostTypes.h @@ -1,6 +1,6 @@ #pragma once -#include "Windowing/EditorWindowTypes.h" +#include "Product/Core/Windowing/EditorWindowTypes.h" #include #include diff --git a/editor/app/Host/Win32/Chrome/EditorWindowChromeController.cpp b/editor/app/Host/Win32/Chrome/EditorWindowChromeController.cpp index 55dd1915..63d8d6d1 100644 --- a/editor/app/Host/Win32/Chrome/EditorWindowChromeController.cpp +++ b/editor/app/Host/Win32/Chrome/EditorWindowChromeController.cpp @@ -2,10 +2,10 @@ #include "Windowing/EditorWindow.h" #include "Windowing/EditorWindowSupport.h" -#include "Windowing/EditorWindowMetrics.h" +#include "Product/Core/Windowing/EditorWindowMetrics.h" #include "EditorWindowContentBindings.h" #include "EditorWindowHostCoordinator.h" -#include "EditorWindowVisuals.h" +#include "Product/Runtime/Windowing/EditorWindowVisuals.h" #include #include diff --git a/editor/app/Host/Win32/Windowing/EditorWindow.cpp b/editor/app/Host/Win32/Windowing/EditorWindow.cpp index cab8d17f..41c0606d 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindow.cpp +++ b/editor/app/Host/Win32/Windowing/EditorWindow.cpp @@ -4,9 +4,9 @@ #include "Windowing/EditorWindowSession.h" #include "Windowing/EditorWindowSupport.h" #include "Runtime/EditorWindowInputController.h" -#include "Windowing/EditorWindowMetrics.h" +#include "Product/Core/Windowing/EditorWindowMetrics.h" #include "EditorWindowContentBindings.h" -#include "EditorWindowDiagnostics.h" +#include "Product/Runtime/Windowing/EditorWindowDiagnostics.h" #include #include #include diff --git a/editor/app/Host/Win32/Windowing/EditorWindow.h b/editor/app/Host/Win32/Windowing/EditorWindow.h index 5ebd908a..6fd110c8 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindow.h +++ b/editor/app/Host/Win32/Windowing/EditorWindow.h @@ -54,9 +54,7 @@ class EditorWindowChromeController; class EditorWindowDockHostBinding; class EditorWindowHostRuntime; class EditorWindowInputController; -class EditorWindowLifecycleCoordinator; class EditorWindowMessageDispatcher; -class EditorWindowWorkspaceCoordinator; class EditorWindowSession; class EditorWindow final : public EditorWindowNativePeer { @@ -113,8 +111,6 @@ private: friend class EditorWindowChromeController; friend class EditorWindowHostRuntime; friend class EditorWindowMessageDispatcher; - friend class EditorWindowLifecycleCoordinator; - friend class EditorWindowWorkspaceCoordinator; bool TryResolveDockTabDragHotspot( std::string_view nodeId, diff --git a/editor/app/Host/Win32/Windowing/EditorWindowMessageDispatcher.h b/editor/app/Host/Win32/Windowing/EditorWindowMessageDispatcher.h index 46f3704a..4307e082 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindowMessageDispatcher.h +++ b/editor/app/Host/Win32/Windowing/EditorWindowMessageDispatcher.h @@ -4,7 +4,7 @@ #define NOMINMAX #endif -#include "Windowing/EditorWindowTransferRequests.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" #include diff --git a/editor/app/Host/Win32/Windowing/EditorWindowSession.h b/editor/app/Host/Win32/Windowing/EditorWindowSession.h index 965e762b..21474462 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindowSession.h +++ b/editor/app/Host/Win32/Windowing/EditorWindowSession.h @@ -1,7 +1,7 @@ #pragma once #include "Windowing/EditorWindowState.h" -#include "Windowing/EditorWindowTransferRequests.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" #include #include diff --git a/editor/app/Host/Win32/Windowing/EditorWindowState.h b/editor/app/Host/Win32/Windowing/EditorWindowState.h index 30767593..e06abe3e 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindowState.h +++ b/editor/app/Host/Win32/Windowing/EditorWindowState.h @@ -4,7 +4,7 @@ #define NOMINMAX #endif -#include "Windowing/EditorWindowTypes.h" +#include "Product/Core/Windowing/EditorWindowTypes.h" #include diff --git a/editor/app/Host/Win32/Windowing/EditorWindowSupport.h b/editor/app/Host/Win32/Windowing/EditorWindowSupport.h index f966b53b..fec373e9 100644 --- a/editor/app/Host/Win32/Windowing/EditorWindowSupport.h +++ b/editor/app/Host/Win32/Windowing/EditorWindowSupport.h @@ -1,7 +1,7 @@ #pragma once #include "System/Win32StringEncoding.h" -#include "TextFormat.h" +#include "Product/Support/TextFormat.h" #include diff --git a/editor/app/Rendering/Viewport/ViewportObjectPickerService.h b/editor/app/Rendering/Viewport/ViewportObjectPickerService.h deleted file mode 100644 index 13af4c08..00000000 --- a/editor/app/Rendering/Viewport/ViewportObjectPickerService.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "Viewport/EditorViewportPicking.h" diff --git a/editor/app/Services/Runtime/EditorRuntimeCoordinator.cpp b/editor/app/Services/Runtime/EditorRuntimeCoordinator.cpp deleted file mode 100644 index ac9b08c7..00000000 --- a/editor/app/Services/Runtime/EditorRuntimeCoordinator.cpp +++ /dev/null @@ -1,592 +0,0 @@ -#include "Runtime/EditorRuntimeCoordinator.h" - -#include "Project/EditorProjectRuntime.h" -#include "Scene/EditorSceneRuntime.h" -#include "State/EditorSession.h" - -#include -#include - -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -UIEditorHostCommandEvaluationResult BuildDisabledResult( - std::string_view message) { - UIEditorHostCommandEvaluationResult result = {}; - result.executable = false; - result.message = std::string(message); - return result; -} - -UIEditorHostCommandEvaluationResult BuildExecutableResult( - std::string_view message) { - UIEditorHostCommandEvaluationResult result = {}; - result.executable = true; - result.message = std::string(message); - return result; -} - -UIEditorHostCommandDispatchResult BuildRejectedDispatch( - std::string_view message) { - UIEditorHostCommandDispatchResult result = {}; - result.commandExecuted = false; - result.message = std::string(message); - return result; -} - -UIEditorHostCommandDispatchResult BuildExecutedDispatch( - std::string_view message) { - UIEditorHostCommandDispatchResult result = {}; - result.commandExecuted = true; - result.message = std::string(message); - return result; -} - -std::string ResolveSceneDisplayName(const std::filesystem::path& scenePath) { - return scenePath.empty() - ? std::string("Untitled") - : scenePath.stem().string(); -} - -std::string ResolveDocumentSceneName( - std::string_view sceneName, - const std::filesystem::path& scenePath) { - return !sceneName.empty() - ? std::string(sceneName) - : ResolveSceneDisplayName(scenePath); -} - -} // namespace - -EditorRuntimeCoordinator::~EditorRuntimeCoordinator() { - Shutdown(); -} - -void EditorRuntimeCoordinator::Initialize( - EditorSession& session, - EditorSceneRuntime& sceneRuntime, - EditorProjectRuntime& projectRuntime, - const EditorRuntimePaths& runtimePaths, - const EditorStartupSceneResult& startupScene) { - Shutdown(); - m_session = &session; - m_sceneRuntime = &sceneRuntime; - m_projectRuntime = &projectRuntime; - m_runtimePaths = runtimePaths; - m_scriptingRuntimeService.Initialize(runtimePaths.projectRoot); - ApplyStartupSceneDocument(startupScene); - SetRuntimeMode(EditorRuntimeMode::Edit); - CaptureCleanSceneRevision(); -} - -void EditorRuntimeCoordinator::Shutdown() { - m_runtimeLoop.Stop(); - m_playSession.reset(); - if (m_session != nullptr) { - m_sceneDocument = {}; - SyncSessionDocumentProjection(); - SetRuntimeMode(EditorRuntimeMode::Edit); - } - m_session = nullptr; - m_sceneRuntime = nullptr; - m_projectRuntime = nullptr; - m_runtimePaths = EditorRuntimePaths{}; - m_scriptingRuntimeService.Shutdown(); - m_sceneDocument = {}; - m_lastFrameTickTime = {}; - m_lastCleanSceneContentRevision = 0u; - m_lastObservedSceneContentRevision = 0u; - m_lastMessage.clear(); -} - -void EditorRuntimeCoordinator::TickFrame() { - const auto now = std::chrono::steady_clock::now(); - if (m_lastFrameTickTime == std::chrono::steady_clock::time_point{}) { - m_lastFrameTickTime = now; - SyncSceneDocumentDirtyFromRevision(); - return; - } - - const float deltaSeconds = std::chrono::duration( - now - m_lastFrameTickTime).count(); - m_lastFrameTickTime = now; - Tick(deltaSeconds); -} - -void EditorRuntimeCoordinator::Tick(float deltaSeconds) { - if (!IsReady()) { - return; - } - - SyncSceneDocumentDirtyFromRevision(); - if (!m_runtimeLoop.IsRunning()) { - if (m_session->runtimeMode != EditorRuntimeMode::Edit) { - SetRuntimeMode(EditorRuntimeMode::Edit); - } - return; - } - - m_runtimeLoop.Tick(deltaSeconds); - if (m_runtimeLoop.IsPaused()) { - SetRuntimeMode(EditorRuntimeMode::Paused); - } else { - SetRuntimeMode(EditorRuntimeMode::Play); - } -} - -bool EditorRuntimeCoordinator::RequestOpenSceneAsset( - const std::filesystem::path& scenePath) { - if (!IsReady()) { - SetLastMessage("Runtime coordinator is unavailable."); - return false; - } - - if (scenePath.empty()) { - SetLastMessage("Scene asset path is empty."); - return false; - } - - StopPlayMode(); - if (!m_sceneRuntime->OpenSceneAsset(scenePath)) { - SetLastMessage("Failed to open scene asset: " + scenePath.string()); - return false; - } - - m_sceneDocument.ready = true; - m_sceneDocument.loadedFromDisk = true; - m_sceneDocument.currentPath = scenePath; - m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath); - m_sceneDocument.dirty = false; - CaptureCleanSceneRevision(); - SyncSessionDocumentProjection(); - SetLastMessage("Opened scene: " + scenePath.string()); - return true; -} - -const std::string& EditorRuntimeCoordinator::GetLastMessage() const { - return m_lastMessage; -} - -UIEditorHostCommandEvaluationResult -EditorRuntimeCoordinator::EvaluateFileCommand( - std::string_view commandId) const { - if (!IsReady()) { - return BuildDisabledResult("Runtime coordinator is unavailable."); - } - - if (commandId == "file.new_scene") { - return BuildExecutableResult("Create a new unsaved scene document."); - } - - if (commandId == "file.open_scene") { - return BuildDisabledResult( - "Open Scene requires a project scene asset selection in the current shell."); - } - - if (commandId == "file.save_scene") { - if (IsPlayModeActive()) { - return BuildDisabledResult("Stop play mode before saving the scene."); - } - if (!HasActiveScene()) { - return BuildDisabledResult("No active scene to save."); - } - if (m_sceneDocument.currentPath.empty()) { - return BuildDisabledResult( - "Save Scene As is required before saving this scene."); - } - return BuildExecutableResult("Save the active scene document."); - } - - if (commandId == "file.save_scene_as") { - return BuildDisabledResult( - "Save Scene As requires a file dialog host; the current shell does not provide one."); - } - - return BuildDisabledResult( - "File command is not owned by the runtime document coordinator."); -} - -UIEditorHostCommandDispatchResult -EditorRuntimeCoordinator::DispatchFileCommand( - std::string_view commandId) { - const UIEditorHostCommandEvaluationResult evaluation = - EvaluateFileCommand(commandId); - if (!evaluation.executable) { - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); - } - - if (commandId == "file.new_scene") { - StopPlayMode(); - if (!m_sceneRuntime->NewScene("Untitled")) { - SetLastMessage("Failed to create a new scene."); - return BuildRejectedDispatch(m_lastMessage); - } - - m_sceneDocument.ready = true; - m_sceneDocument.loadedFromDisk = false; - m_sceneDocument.currentPath.clear(); - m_sceneDocument.currentName = ResolveActiveSceneDisplayName(); - m_sceneDocument.dirty = true; - CaptureCleanSceneRevision(); - SyncSessionDocumentProjection(); - SetLastMessage("New scene created."); - return BuildExecutedDispatch(m_lastMessage); - } - - if (commandId == "file.save_scene") { - const std::filesystem::path scenePath = m_sceneDocument.currentPath; - if (!m_sceneRuntime->SaveScene(scenePath)) { - SetLastMessage("Failed to save scene: " + scenePath.string()); - return BuildRejectedDispatch(m_lastMessage); - } - - m_sceneDocument.ready = true; - m_sceneDocument.loadedFromDisk = !scenePath.empty(); - m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath); - m_sceneDocument.dirty = false; - CaptureCleanSceneRevision(); - SyncSessionDocumentProjection(); - SetLastMessage("Scene saved: " + scenePath.string()); - return BuildExecutedDispatch(m_lastMessage); - } - - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); -} - -UIEditorHostCommandEvaluationResult -EditorRuntimeCoordinator::EvaluateRunCommand( - std::string_view commandId) const { - if (!IsReady()) { - return BuildDisabledResult("Runtime coordinator is unavailable."); - } - - if (commandId == "run.play") { - if (!HasActiveScene()) { - return BuildDisabledResult("No active scene is available for play mode."); - } - return IsPlayModeActive() - ? BuildExecutableResult("Stop play mode.") - : BuildExecutableResult("Start play mode."); - } - - if (commandId == "run.pause") { - if (!IsPlayModeActive()) { - return BuildDisabledResult("Play mode is not running."); - } - return m_session->runtimeMode == EditorRuntimeMode::Paused - ? BuildExecutableResult("Resume play mode.") - : BuildExecutableResult("Pause play mode."); - } - - if (commandId == "run.step") { - if (!HasActiveScene()) { - return BuildDisabledResult("No active scene is available for play mode."); - } - return BuildExecutableResult("Step play mode by one frame."); - } - - if (commandId == "run.stop") { - return IsPlayModeActive() - ? BuildExecutableResult("Stop play mode.") - : BuildDisabledResult("Play mode is not running."); - } - - return BuildDisabledResult( - "Run command is not owned by the runtime coordinator."); -} - -UIEditorHostCommandDispatchResult -EditorRuntimeCoordinator::DispatchRunCommand( - std::string_view commandId) { - const UIEditorHostCommandEvaluationResult evaluation = - EvaluateRunCommand(commandId); - if (!evaluation.executable) { - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); - } - - if (commandId == "run.play") { - const bool executed = IsPlayModeActive() ? StopPlayMode() : StartPlayMode(); - SetLastMessage(executed - ? (m_session->runtimeMode == EditorRuntimeMode::Edit - ? std::string("Play mode stopped.") - : std::string("Play mode started.")) - : std::string("Failed to toggle play mode.")); - return executed - ? BuildExecutedDispatch(m_lastMessage) - : BuildRejectedDispatch(m_lastMessage); - } - - if (commandId == "run.pause") { - const bool executed = m_session->runtimeMode == EditorRuntimeMode::Paused - ? ResumePlayMode() - : PausePlayMode(); - SetLastMessage(executed - ? (m_session->runtimeMode == EditorRuntimeMode::Paused - ? std::string("Play mode paused.") - : std::string("Play mode resumed.")) - : std::string("Failed to toggle play mode pause.")); - return executed - ? BuildExecutedDispatch(m_lastMessage) - : BuildRejectedDispatch(m_lastMessage); - } - - if (commandId == "run.step") { - if (!StepPlayMode()) { - SetLastMessage("Failed to step play mode."); - return BuildRejectedDispatch(m_lastMessage); - } - - SetLastMessage("Play mode stepped one frame."); - return BuildExecutedDispatch(m_lastMessage); - } - - if (commandId == "run.stop") { - if (!StopPlayMode()) { - SetLastMessage("Failed to stop play mode."); - return BuildRejectedDispatch(m_lastMessage); - } - - SetLastMessage("Play mode stopped."); - return BuildExecutedDispatch(m_lastMessage); - } - - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); -} - -UIEditorHostCommandEvaluationResult -EditorRuntimeCoordinator::EvaluateScriptCommand( - std::string_view commandId) const { - if (!IsReady()) { - return BuildDisabledResult("Runtime coordinator is unavailable."); - } - - if (commandId == "scripts.rebuild") { - if (IsPlayModeActive()) { - return BuildDisabledResult( - "Stop play mode before rebuilding script assemblies."); - } - - if (!m_scriptingRuntimeService.CanRebuildProjectAssemblies()) { - const std::string& message = m_scriptingRuntimeService.GetLastMessage(); - return BuildDisabledResult( - message.empty() - ? std::string_view( - "Script rebuild is unavailable because the scripting runtime is not initialized.") - : std::string_view(message)); - } - - return BuildExecutableResult( - "Rebuild project script assemblies and reload the editor scripting runtime."); - } - - return BuildDisabledResult( - "Script command is not owned by the runtime coordinator."); -} - -UIEditorHostCommandDispatchResult -EditorRuntimeCoordinator::DispatchScriptCommand( - std::string_view commandId) { - const UIEditorHostCommandEvaluationResult evaluation = - EvaluateScriptCommand(commandId); - if (!evaluation.executable) { - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); - } - - if (commandId == "scripts.rebuild") { - const bool rebuilt = m_scriptingRuntimeService.RebuildProjectAssemblies(); - if (m_sceneRuntime != nullptr) { - m_sceneRuntime->NotifyExternalInspectorStateChanged(); - } - - const std::string& message = m_scriptingRuntimeService.GetLastMessage(); - SetLastMessage( - !message.empty() - ? message - : rebuilt - ? std::string("Script assemblies rebuilt.") - : std::string("Failed to rebuild script assemblies.")); - return rebuilt - ? BuildExecutedDispatch(m_lastMessage) - : BuildRejectedDispatch(m_lastMessage); - } - - SetLastMessage(evaluation.message); - return BuildRejectedDispatch(evaluation.message); -} - -bool EditorRuntimeCoordinator::IsReady() const { - return m_session != nullptr && m_sceneRuntime != nullptr; -} - -bool EditorRuntimeCoordinator::HasActiveScene() const { - return IsReady() && m_sceneRuntime->GetActiveScene() != nullptr; -} - -bool EditorRuntimeCoordinator::IsPlayModeActive() const { - return IsReady() && m_session->runtimeMode != EditorRuntimeMode::Edit; -} - -bool EditorRuntimeCoordinator::EnsurePlaySession() { - if (!HasActiveScene()) { - return false; - } - - if (m_playSession != nullptr && - m_playSession->GetRuntimeScene() != nullptr) { - return true; - } - - m_playSession = m_sceneRuntime->BeginPlaySession(); - if (m_playSession == nullptr || - m_playSession->GetRuntimeScene() == nullptr) { - m_playSession.reset(); - return false; - } - - return true; -} - -bool EditorRuntimeCoordinator::StartPlayMode() { - if (!EnsurePlaySession()) { - return false; - } - - ::XCEngine::Scripting::ScriptEngine::Get().SetRuntimeFixedDeltaTime( - m_runtimeLoop.GetSettings().fixedDeltaTime); - m_runtimeLoop.Start(m_playSession->GetRuntimeScene()); - SetRuntimeMode(EditorRuntimeMode::Play); - m_lastFrameTickTime = std::chrono::steady_clock::now(); - return m_runtimeLoop.IsRunning(); -} - -bool EditorRuntimeCoordinator::StopPlayMode() { - if (!IsReady()) { - return false; - } - - m_runtimeLoop.Stop(); - m_playSession.reset(); - m_sceneRuntime->RefreshScene(); - SetRuntimeMode(EditorRuntimeMode::Edit); - return true; -} - -bool EditorRuntimeCoordinator::PausePlayMode() { - if (!m_runtimeLoop.IsRunning()) { - return false; - } - - m_runtimeLoop.Pause(); - SetRuntimeMode(EditorRuntimeMode::Paused); - return true; -} - -bool EditorRuntimeCoordinator::ResumePlayMode() { - if (!m_runtimeLoop.IsRunning()) { - return false; - } - - m_runtimeLoop.Resume(); - SetRuntimeMode(EditorRuntimeMode::Play); - m_lastFrameTickTime = std::chrono::steady_clock::now(); - return true; -} - -bool EditorRuntimeCoordinator::StepPlayMode() { - if (!HasActiveScene()) { - return false; - } - - if (!m_runtimeLoop.IsRunning()) { - if (!EnsurePlaySession()) { - return false; - } - - ::XCEngine::Scripting::ScriptEngine::Get().SetRuntimeFixedDeltaTime( - m_runtimeLoop.GetSettings().fixedDeltaTime); - m_runtimeLoop.Start(m_playSession->GetRuntimeScene()); - } - - m_runtimeLoop.StepFrame(); - m_runtimeLoop.Tick(m_runtimeLoop.GetSettings().fixedDeltaTime); - SetRuntimeMode(EditorRuntimeMode::Paused); - m_lastFrameTickTime = std::chrono::steady_clock::now(); - return true; -} - -void EditorRuntimeCoordinator::ApplyStartupSceneDocument( - const EditorStartupSceneResult& startupScene) { - m_sceneDocument.ready = startupScene.ready; - m_sceneDocument.loadedFromDisk = startupScene.loadedFromDisk; - m_sceneDocument.currentPath = startupScene.scenePath; - m_sceneDocument.currentName = - ResolveDocumentSceneName(startupScene.sceneName, startupScene.scenePath); - m_sceneDocument.dirty = false; - SyncSessionDocumentProjection(); -} - -std::string EditorRuntimeCoordinator::ResolveActiveSceneDisplayName( - const std::filesystem::path& fallbackPath) const { - const std::filesystem::path& scenePath = - fallbackPath.empty() ? m_sceneDocument.currentPath : fallbackPath; - const std::string sceneName = - IsReady() ? m_sceneRuntime->GetActiveSceneName() : std::string(); - return ResolveDocumentSceneName(sceneName, scenePath); -} - -void EditorRuntimeCoordinator::SyncSessionDocumentProjection() { - if (m_session == nullptr) { - return; - } - - m_session->currentScenePath = m_sceneDocument.currentPath; - m_session->currentSceneName = m_sceneDocument.currentName; - m_session->sceneDocumentDirty = m_sceneDocument.dirty; -} - -void EditorRuntimeCoordinator::SetRuntimeMode(EditorRuntimeMode mode) { - if (m_session != nullptr) { - m_session->runtimeMode = mode; - } -} - -void EditorRuntimeCoordinator::SyncSceneDocumentDirtyFromRevision() { - if (!IsReady()) { - return; - } - - const std::uint64_t revision = m_sceneRuntime->GetSceneContentRevision(); - if (revision != m_lastObservedSceneContentRevision) { - m_lastObservedSceneContentRevision = revision; - } - if (revision != m_lastCleanSceneContentRevision) { - m_sceneDocument.dirty = true; - SyncSessionDocumentProjection(); - } -} - -void EditorRuntimeCoordinator::CaptureCleanSceneRevision() { - if (!IsReady()) { - m_lastCleanSceneContentRevision = 0u; - m_lastObservedSceneContentRevision = 0u; - return; - } - - const std::uint64_t revision = m_sceneRuntime->GetSceneContentRevision(); - m_lastCleanSceneContentRevision = revision; - m_lastObservedSceneContentRevision = revision; -} - -void EditorRuntimeCoordinator::SetLastMessage(std::string message) { - m_lastMessage = std::move(message); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Services/Runtime/EditorRuntimeCoordinator.h b/editor/app/Services/Runtime/EditorRuntimeCoordinator.h deleted file mode 100644 index b62474a6..00000000 --- a/editor/app/Services/Runtime/EditorRuntimeCoordinator.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "Commands/EditorHostCommandBridge.h" -#include "Environment/EditorRuntimePaths.h" -#include "Runtime/EditorScriptingRuntimeService.h" - -#include - -#include -#include -#include -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -class EditorProjectRuntime; -class EditorScenePlaySession; -class EditorSceneRuntime; -struct EditorStartupSceneResult; -struct EditorSession; - -class EditorRuntimeCoordinator final - : public EditorHostCommandBridge::RuntimeCommandOwner { -public: - EditorRuntimeCoordinator() = default; - ~EditorRuntimeCoordinator(); - EditorRuntimeCoordinator(const EditorRuntimeCoordinator&) = delete; - EditorRuntimeCoordinator& operator=(const EditorRuntimeCoordinator&) = delete; - - void Initialize( - EditorSession& session, - EditorSceneRuntime& sceneRuntime, - EditorProjectRuntime& projectRuntime, - const EditorRuntimePaths& runtimePaths, - const EditorStartupSceneResult& startupScene); - void Shutdown(); - - void TickFrame(); - void Tick(float deltaSeconds); - - bool RequestOpenSceneAsset(const std::filesystem::path& scenePath); - const std::string& GetLastMessage() const; - - UIEditorHostCommandEvaluationResult EvaluateFileCommand( - std::string_view commandId) const override; - UIEditorHostCommandDispatchResult DispatchFileCommand( - std::string_view commandId) override; - UIEditorHostCommandEvaluationResult EvaluateRunCommand( - std::string_view commandId) const override; - UIEditorHostCommandDispatchResult DispatchRunCommand( - std::string_view commandId) override; - UIEditorHostCommandEvaluationResult EvaluateScriptCommand( - std::string_view commandId) const override; - UIEditorHostCommandDispatchResult DispatchScriptCommand( - std::string_view commandId) override; - -private: - struct SceneDocumentState { - bool ready = false; - bool loadedFromDisk = false; - std::filesystem::path currentPath = {}; - std::string currentName = {}; - bool dirty = false; - }; - - bool IsReady() const; - bool HasActiveScene() const; - bool IsPlayModeActive() const; - bool EnsurePlaySession(); - bool StartPlayMode(); - bool StopPlayMode(); - bool PausePlayMode(); - bool ResumePlayMode(); - bool StepPlayMode(); - void ApplyStartupSceneDocument(const EditorStartupSceneResult& startupScene); - std::string ResolveActiveSceneDisplayName( - const std::filesystem::path& fallbackPath = {}) const; - void SyncSessionDocumentProjection(); - void SetRuntimeMode(EditorRuntimeMode mode); - void SyncSceneDocumentDirtyFromRevision(); - void CaptureCleanSceneRevision(); - void SetLastMessage(std::string message); - - EditorSession* m_session = nullptr; - EditorSceneRuntime* m_sceneRuntime = nullptr; - EditorProjectRuntime* m_projectRuntime = nullptr; - EditorRuntimePaths m_runtimePaths = {}; - EditorScriptingRuntimeService m_scriptingRuntimeService = {}; - SceneDocumentState m_sceneDocument = {}; - ::XCEngine::Components::RuntimeLoop m_runtimeLoop{ - ::XCEngine::Components::RuntimeLoop::Settings{} }; - std::unique_ptr m_playSession = {}; - std::chrono::steady_clock::time_point m_lastFrameTickTime = {}; - std::uint64_t m_lastCleanSceneContentRevision = 0u; - std::uint64_t m_lastObservedSceneContentRevision = 0u; - std::string m_lastMessage = {}; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWindowContentController.cpp b/editor/app/Windowing/Content/EditorWindowContentController.cpp deleted file mode 100644 index 940ce3fc..00000000 --- a/editor/app/Windowing/Content/EditorWindowContentController.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "Content/EditorWindowContentController.h" - -#include "EditorWindowVisuals.h" - -#include - -namespace XCEngine::UI::Editor::App { - -bool EditorWindowContentController::IsFrameValid( - const EditorFrameValidation& frameValidation) const { - return frameValidation.IsValid(); -} - -void EditorWindowContentController::AppendInvalidFrame( - const EditorFrameValidation& frameValidation, - ::XCEngine::UI::UIDrawList& drawList) const { - drawList.AddText( - ::XCEngine::UI::UIPoint(28.0f, 28.0f), - "Editor shell asset invalid.", - kShellTextColor, - 16.0f); - drawList.AddText( - ::XCEngine::UI::UIPoint(28.0f, 54.0f), - frameValidation.GetValidationMessage().empty() - ? std::string("Unknown validation error.") - : frameValidation.GetValidationMessage(), - kShellMutedTextColor, - 12.0f); -} - -void EditorWindowContentController::NotifyStartupCaptureRequested( - EditorFrameStatusService& frameStatusService) { - frameStatusService.SetStatus("Capture", "Startup capture requested."); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp deleted file mode 100644 index 1f802e98..00000000 --- a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "Coordinator/EditorUtilityWindowCoordinator.h" - -#include "UtilityWindows/EditorUtilityWindowRegistry.h" -#include "Content/EditorWindowContentController.h" -#include "Content/EditorWindowContentFactory.h" -#include "Coordinator/EditorWindowLifecycleCoordinator.h" -#include "EditorWindowManager.h" -#include "Runtime/EditorWindowRuntimeController.h" - -#include "EditorWindowRenderRuntime.h" - -#include -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -bool IsLiveWindow(const EditorHostWindow* window) { - return window != nullptr && - window->HasLiveHostWindow() && - window->GetLifecycleState() == EditorWindowLifecycleState::Running; -} - -int ResolveOuterDimension(float value, int fallback) { - return value > 0.0f - ? static_cast(std::lround(value)) - : fallback; -} - -} - -EditorUtilityWindowCoordinator::EditorUtilityWindowCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowManager& windowManager, - Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, - EditorWindowContentFactory& contentFactory) - : m_hostRuntime(hostRuntime), - m_windowManager(windowManager), - m_renderRuntimeFactory(renderRuntimeFactory), - m_contentFactory(contentFactory) {} - -EditorUtilityWindowCoordinator::~EditorUtilityWindowCoordinator() = default; - -void EditorUtilityWindowCoordinator::BindLifecycleCoordinator( - EditorWindowLifecycleCoordinator& lifecycleCoordinator) { - m_lifecycleCoordinator = &lifecycleCoordinator; -} - -void EditorUtilityWindowCoordinator::HandleWindowFrameTransferRequests( - EditorHostWindow& sourceWindow, - const EditorWindowFrameTransferRequests& transferRequests) { - if (transferRequests.utility.openUtilityWindow.has_value() && - transferRequests.utility.openUtilityWindow->IsValid()) { - TryProcessOpenUtilityWindowRequest( - sourceWindow, - *transferRequests.utility.openUtilityWindow); - } -} - -bool EditorUtilityWindowCoordinator::TryProcessOpenUtilityWindowRequest( - EditorHostWindow& sourceWindow, - const EditorWindowOpenUtilityWindowRequest& request) { - if (!IsLiveWindow(&sourceWindow)) { - LogRuntimeTrace( - "utility", - "open utility window request rejected: source window is not running"); - return false; - } - - const EditorUtilityWindowDescriptor* descriptor = - ResolveEditorUtilityWindowDescriptor(request.kind); - if (descriptor == nullptr) { - LogRuntimeTrace("utility", "open utility window request rejected: unknown kind"); - return false; - } - - if (descriptor->reusePolicy == EditorUtilityWindowReusePolicy::SingleInstance) { - if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); - existingWindow != nullptr && existingWindow->IsDestroyed() && - m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->ReapDestroyedWindows(); - } - - if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); - IsLiveWindow(existingWindow)) { - if (!existingWindow->IsUtilityWindow()) { - LogRuntimeTrace( - "utility", - "open utility window request rejected: existing window id is not utility"); - return false; - } - existingWindow->FocusHostWindow(); - LogRuntimeTrace( - "utility", - "reused utility window '" + std::string(descriptor->windowId) + "'"); - return true; - } - - if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); - existingWindow != nullptr) { - LogRuntimeTrace( - "utility", - "open utility window request rejected: existing utility window is not reusable"); - return false; - } - } - - EditorWindowCreateParams createParams = {}; - createParams.windowId = std::string(descriptor->windowId); - createParams.title = descriptor->title; - createParams.category = EditorWindowCategory::Utility; - createParams.chromePolicy = descriptor->chromePolicy; - createParams.nativeHostPolicy = descriptor->nativeHostPolicy; - createParams.primary = false; - createParams.initialWidth = ResolveOuterDimension( - descriptor->preferredOuterSize.width, - createParams.initialWidth); - createParams.initialHeight = ResolveOuterDimension( - descriptor->preferredOuterSize.height, - createParams.initialHeight); - if (request.useCursorPlacement) { - const EditorWindowScreenRect rect = m_hostRuntime.ResolveFloatingPlacement( - request.screenPoint, - createParams.initialWidth, - createParams.initialHeight); - createParams.initialX = rect.left; - createParams.initialY = rect.top; - createParams.initialWidth = rect.Width(); - createParams.initialHeight = rect.Height(); - } - - EditorHostWindow* utilityWindow = m_windowManager.CreateUtilityWindow( - *descriptor, - createParams); - if (utilityWindow == nullptr) { - LogRuntimeTrace("utility", "failed to create utility window"); - return false; - } - - utilityWindow->FocusHostWindow(); - LogRuntimeTrace( - "utility", - "opened utility window '" + std::string(descriptor->windowId) + "'"); - return true; -} - -void EditorUtilityWindowCoordinator::LogRuntimeTrace( - std::string_view channel, - std::string_view message) const { - m_hostRuntime.LogRuntimeTrace(channel, message); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h b/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h deleted file mode 100644 index ad934204..00000000 --- a/editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "Windowing/EditorWindowTransferRequests.h" -#include "EditorWindowHostInterfaces.h" - -#include - -namespace XCEngine::UI::Editor::Rendering::Host { -class EditorWindowRenderRuntimeFactory; -} - -namespace XCEngine::UI::Editor::App { - -class EditorWindowContentFactory; -class EditorWindowLifecycleCoordinator; -class EditorWindowManager; - -class EditorUtilityWindowCoordinator final { -public: - EditorUtilityWindowCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowManager& windowManager, - Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, - EditorWindowContentFactory& contentFactory); - ~EditorUtilityWindowCoordinator(); - - void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator); - void HandleWindowFrameTransferRequests( - EditorHostWindow& sourceWindow, - const EditorWindowFrameTransferRequests& transferRequests); - -private: - bool TryProcessOpenUtilityWindowRequest( - EditorHostWindow& sourceWindow, - const EditorWindowOpenUtilityWindowRequest& request); - void LogRuntimeTrace(std::string_view channel, std::string_view message) const; - - EditorWindowHost& m_hostRuntime; - EditorWindowManager& m_windowManager; - Rendering::Host::EditorWindowRenderRuntimeFactory& m_renderRuntimeFactory; - EditorWindowContentFactory& m_contentFactory; - EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp deleted file mode 100644 index c9b73db1..00000000 --- a/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "Coordinator/EditorWindowLifecycleCoordinator.h" - -#include "Coordinator/EditorWindowWorkspaceCoordinator.h" - -#include - -namespace XCEngine::UI::Editor::App { - -EditorWindowLifecycleCoordinator::EditorWindowLifecycleCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowWorkspaceCoordinator& workspaceCoordinator) - : m_hostRuntime(hostRuntime), - m_workspaceCoordinator(workspaceCoordinator) {} - -EditorWindowLifecycleCoordinator::~EditorWindowLifecycleCoordinator() = default; - -void EditorWindowLifecycleCoordinator::PostCloseRequest(EditorHostWindow& window) { - if (window.IsDestroyed()) { - return; - } - - if (!window.IsClosing()) { - window.MarkClosing(); - } - - if (!window.HasLiveHostWindow()) { - ShutdownRuntimeIfNeeded(window); - window.MarkDestroyed(); - return; - } - - LogRuntimeTrace( - "window-close", - "PostCloseRequest windowId='" + std::string(window.GetWindowId()) + - " primary=" + (window.IsPrimary() ? "1" : "0") + - " host=" + m_hostRuntime.DescribeWindows()); - window.PostCloseToHost(); -} - -void EditorWindowLifecycleCoordinator::ExecuteCloseRequest(EditorHostWindow& window) { - if (window.IsDestroyed()) { - return; - } - - LogRuntimeTrace( - "window-close", - "ExecuteCloseRequest begin windowId='" + std::string(window.GetWindowId()) + - " primary=" + (window.IsPrimary() ? "1" : "0") + - " lifecycleBefore=" + - std::string(GetEditorWindowLifecycleStateName(window.GetLifecycleState())) + - " workspace=" + m_workspaceCoordinator.DescribeWindowSet() + - " host=" + m_hostRuntime.DescribeWindows()); - - if (!window.IsClosing()) { - window.MarkClosing(); - } - - ShutdownRuntimeIfNeeded(window); - - if (window.HasLiveHostWindow()) { - window.DestroyHostWindow(); - } else { - window.MarkDestroyed(); - } - - LogRuntimeTrace( - "window-close", - "ExecuteCloseRequest end windowId='" + std::string(window.GetWindowId()) + - "' host=" + m_hostRuntime.DescribeWindows()); -} - -void EditorWindowLifecycleCoordinator::HandleNativeWindowDestroyed(EditorHostWindow& window) { - if (window.IsDestroyed()) { - return; - } - - const bool destroyedPrimary = - m_workspaceCoordinator.IsPrimaryWindowId(window.GetWindowId()); - LogRuntimeTrace( - "window-close", - "HandleNativeWindowDestroyed begin windowId='" + std::string(window.GetWindowId()) + - " destroyedPrimary=" + (destroyedPrimary ? "1" : "0") + - " localPrimary=" + (window.IsPrimary() ? "1" : "0") + - " workspaceBefore=" + m_workspaceCoordinator.DescribeWindowSet() + - " hostBefore=" + m_hostRuntime.DescribeWindows()); - - if (m_workspaceCoordinator.OwnsActiveGlobalTabDrag(window.GetWindowId())) { - m_workspaceCoordinator.EndGlobalTabDragSession(); - } - - ShutdownRuntimeIfNeeded(window); - window.MarkDestroyed(); - if (window.IsWorkspaceWindow()) { - m_workspaceCoordinator.HandleNativeWindowDestroyed(window.GetWindowId()); - } - - if (destroyedPrimary) { - std::vector closeTargets = {}; - const std::vector windows = m_hostRuntime.GetWindows(); - closeTargets.reserve(windows.size()); - for (EditorHostWindow* otherWindow : windows) { - if (otherWindow == nullptr || - otherWindow == &window || - !otherWindow->HasLiveHostWindow() || - otherWindow->IsClosing()) { - continue; - } - closeTargets.push_back(otherWindow); - } - - for (EditorHostWindow* closeTarget : closeTargets) { - if (closeTarget != nullptr) { - PostCloseRequest(*closeTarget); - } - } - } - - LogRuntimeTrace( - "window-close", - "HandleNativeWindowDestroyed end windowId='" + std::string(window.GetWindowId()) + - "' workspaceAfter=" + m_workspaceCoordinator.DescribeWindowSet() + - " hostAfter=" + m_hostRuntime.DescribeWindows()); -} - -void EditorWindowLifecycleCoordinator::AbortUnregisteredWindow(EditorHostWindow& window) { - LogRuntimeTrace( - "window-close", - "AbortUnregisteredWindow begin windowId='" + std::string(window.GetWindowId()) + - " hostBefore=" + m_hostRuntime.DescribeWindows()); - - if (!window.IsClosing()) { - window.MarkClosing(); - } - - ShutdownRuntimeIfNeeded(window); - if (window.HasLiveHostWindow()) { - window.DestroyHostWindow(); - } else { - window.MarkDestroyed(); - } - ReapDestroyedWindows(); - - LogRuntimeTrace( - "window-close", - "AbortUnregisteredWindow end windowId='" + std::string(window.GetWindowId()) + - "' hostAfter=" + m_hostRuntime.DescribeWindows()); -} - -void EditorWindowLifecycleCoordinator::ShutdownAllWindows() { - std::vector closeTargets = {}; - const std::vector windows = m_hostRuntime.GetWindows(); - closeTargets.reserve(windows.size()); - for (EditorHostWindow* window : windows) { - if (window != nullptr) { - closeTargets.push_back(window); - } - } - - for (EditorHostWindow* closeTarget : closeTargets) { - if (closeTarget != nullptr) { - ExecuteCloseRequest(*closeTarget); - } - } - - ReapDestroyedWindows(); -} - -void EditorWindowLifecycleCoordinator::ReapDestroyedWindows() { - m_hostRuntime.ReapDestroyedWindows(); -} - -void EditorWindowLifecycleCoordinator::ShutdownRuntimeIfNeeded(EditorHostWindow& window) { - if (window.IsRenderReady()) { - window.Shutdown(); - } -} - -void EditorWindowLifecycleCoordinator::LogRuntimeTrace( - std::string_view channel, - std::string_view message) const { - m_hostRuntime.LogRuntimeTrace(channel, message); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.h b/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.h deleted file mode 100644 index 3de5fa5c..00000000 --- a/editor/app/Windowing/Coordinator/EditorWindowLifecycleCoordinator.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "EditorWindowHostInterfaces.h" - -#include - -namespace XCEngine::UI::Editor::App { - -class EditorWindowWorkspaceCoordinator; - -class EditorWindowLifecycleCoordinator final { -public: - EditorWindowLifecycleCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowWorkspaceCoordinator& workspaceCoordinator); - ~EditorWindowLifecycleCoordinator(); - - void PostCloseRequest(EditorHostWindow& window); - void ExecuteCloseRequest(EditorHostWindow& window); - void HandleNativeWindowDestroyed(EditorHostWindow& window); - void AbortUnregisteredWindow(EditorHostWindow& window); - void ShutdownAllWindows(); - void ReapDestroyedWindows(); - -private: - void ShutdownRuntimeIfNeeded(EditorHostWindow& window); - void LogRuntimeTrace(std::string_view channel, std::string_view message) const; - - EditorWindowHost& m_hostRuntime; - EditorWindowWorkspaceCoordinator& m_workspaceCoordinator; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp deleted file mode 100644 index 2e1379a4..00000000 --- a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.cpp +++ /dev/null @@ -1,912 +0,0 @@ -#include "Coordinator/EditorWindowWorkspaceCoordinator.h" - -#include "Content/EditorWindowContentFactory.h" -#include "Content/EditorWindowContentController.h" -#include "Coordinator/EditorWindowLifecycleCoordinator.h" -#include "EditorWindowManager.h" -#include "Runtime/EditorWindowRuntimeController.h" - -#include "EditorWindowRenderRuntime.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -namespace { - -struct ExistingWindowSnapshot { - std::string windowId = {}; - EditorWorkspaceWindowProjection projection = {}; - bool primary = false; -}; - -using ::XCEngine::UI::UIPoint; - -constexpr std::int32_t kFallbackDragHotspotX = 40; -constexpr std::int32_t kFallbackDragHotspotY = 12; - -EditorWindowScreenPoint BuildFallbackGlobalTabDragHotspot() { - EditorWindowScreenPoint hotspot = {}; - hotspot.x = kFallbackDragHotspotX; - hotspot.y = kFallbackDragHotspotY; - return hotspot; -} - -bool CanStartGlobalTabDragFromWindow( - const EditorWindowSystem& windowSystem, - const EditorHostWindow& sourceWindow, - std::string_view sourceNodeId, - std::string_view panelId) { - const UIEditorWindowWorkspaceState* windowState = - windowSystem.FindWindowState(sourceWindow.GetWindowId()); - if (windowState == nullptr) { - return false; - } - - UIEditorWorkspaceModel workspace = windowState->workspace; - UIEditorWorkspaceSession session = windowState->session; - UIEditorWorkspaceExtractedPanel extractedPanel = {}; - return TryExtractUIEditorWorkspaceVisiblePanel( - workspace, - session, - sourceNodeId, - panelId, - extractedPanel); -} - -bool IsLiveInteractiveWindow(const EditorHostWindow* window) { - return window != nullptr && - window->HasLiveHostWindow() && - window->GetLifecycleState() == EditorWindowLifecycleState::Running; -} - -std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet) { - std::ostringstream stream = {}; - stream << "primary='" << windowSet.primaryWindowId - << "' active='" << windowSet.activeWindowId - << "' count=" << windowSet.windows.size() - << " windows=["; - bool first = true; - for (const UIEditorWindowWorkspaceState& state : windowSet.windows) { - if (!first) { - stream << ','; - } - first = false; - stream << state.windowId; - } - stream << ']'; - return stream.str(); -} - -} // namespace - -EditorWindowWorkspaceCoordinator::EditorWindowWorkspaceCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowManager& windowManager, - EditorWindowSystem& windowSystem, - Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, - EditorWindowContentFactory& contentFactory) - : m_hostRuntime(hostRuntime), - m_windowManager(windowManager), - m_windowSystem(windowSystem), - m_renderRuntimeFactory(renderRuntimeFactory), - m_contentFactory(contentFactory) {} - -EditorWindowWorkspaceCoordinator::~EditorWindowWorkspaceCoordinator() = default; - -void EditorWindowWorkspaceCoordinator::BindLifecycleCoordinator( - EditorWindowLifecycleCoordinator& lifecycleCoordinator) { - m_lifecycleCoordinator = &lifecycleCoordinator; -} - -void EditorWindowWorkspaceCoordinator::RegisterExistingWindow(EditorHostWindow& window) { - RefreshWorkspaceProjectionFromAuthoritativeState(window); - RefreshWindowTitle(window); -} - -void EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(EditorHostWindow& window) { - if (!window.IsWorkspaceWindow()) { - return; - } - - RefreshWorkspaceProjectionFromAuthoritativeState(window); - RefreshWindowTitle(window); -} - -void EditorWindowWorkspaceCoordinator::HandleNativeWindowDestroyed(std::string_view windowId) { - std::string error = {}; - EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForDestroyedWindow( - windowId, - CaptureHostSnapshots(), - m_hostRuntime.GetPrimaryWindowTitle(), - error); - if (!plan.valid) { - LogRuntimeTrace( - "window-close", - "workspace destroy reconciliation rejected for window '" + - std::string(windowId) + "': " + error); - return; - } - - if (!ApplySynchronizationPlan(plan)) { - LogRuntimeTrace( - "window-close", - "workspace destroy reconciliation execution failed for window '" + - std::string(windowId) + "'"); - } -} - -bool EditorWindowWorkspaceCoordinator::IsPrimaryWindowId(std::string_view windowId) const { - return m_windowSystem.IsPrimaryWindowId(windowId); -} - -std::string EditorWindowWorkspaceCoordinator::DescribeWindowSet() const { - return DescribeWindowSetState(m_windowSystem.GetWindowSet()); -} - -UIEditorWindowWorkspaceController -EditorWindowWorkspaceCoordinator::BuildWorkspaceMutationController() const { - return m_windowSystem.BuildWorkspaceMutationController(); -} - -UIEditorWindowWorkspaceState EditorWindowWorkspaceCoordinator::BuildWindowStateForWindow( - const EditorHostWindow& window) const { - if (const UIEditorWindowWorkspaceState* windowState = - m_windowSystem.FindWindowState(window.GetWindowId()); - windowState != nullptr) { - return *windowState; - } - - return {}; -} - -EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspaceProjectionForState( - const UIEditorWindowWorkspaceState& windowState, - bool primary, - std::wstring title) const { - EditorWorkspaceWindowProjection projection = BuildEditorWorkspaceWindowProjection( - m_hostRuntime.GetPrimaryWindowTitle(), - m_windowSystem.GetPanelRegistry(), - windowState, - primary); - if (!title.empty()) { - projection.windowTitle = std::move(title); - } - return projection; -} - -EditorWorkspaceWindowProjection EditorWindowWorkspaceCoordinator::BuildWorkspaceProjectionForWindow( - const EditorHostWindow& window) const { - if (const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection(); - projection != nullptr) { - return *projection; - } - - return BuildWorkspaceProjectionForState( - BuildWindowStateForWindow(window), - window.IsPrimary(), - window.GetTitle()); -} - -bool EditorWindowWorkspaceCoordinator::RefreshWorkspaceProjectionFromAuthoritativeState( - EditorHostWindow& window) const { - if (!window.IsWorkspaceWindow()) { - return false; - } - - const UIEditorWindowWorkspaceState* authoritativeState = - m_windowSystem.FindWindowState(window.GetWindowId()); - if (authoritativeState == nullptr) { - return false; - } - - const bool primary = m_windowSystem.IsPrimaryWindowId(window.GetWindowId()); - window.SetPrimary(primary); - window.RefreshWorkspaceProjection(BuildWorkspaceProjectionForState( - *authoritativeState, - primary, - {})); - return true; -} - -void EditorWindowWorkspaceCoordinator::RefreshWindowTitle(EditorHostWindow& window) const { - const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection(); - if (projection == nullptr || projection->windowTitle.empty()) { - return; - } - - const std::wstring& title = projection->windowTitle; - if (title == window.GetTitle()) { - return; - } - - window.SetTitle(title); - window.ApplyHostWindowTitle(); -} - -bool EditorWindowWorkspaceCoordinator::SynchronizeWindowsFromWindowSet( - const UIEditorWindowWorkspaceSet& windowSet, - std::string_view preferredNewWindowId, - const EditorWindowScreenPoint& preferredScreenPoint, - int preferredWidth, - int preferredHeight) { - EditorWindowSynchronizationPlacement preferredPlacement = {}; - if (!preferredNewWindowId.empty()) { - const EditorWindowScreenRect detachedRect = m_hostRuntime.ResolveFloatingPlacement( - preferredScreenPoint, - preferredWidth, - preferredHeight); - preferredPlacement.enabled = true; - preferredPlacement.initialX = detachedRect.left; - preferredPlacement.initialY = detachedRect.top; - preferredPlacement.initialWidth = detachedRect.Width(); - preferredPlacement.initialHeight = detachedRect.Height(); - } - - std::string error = {}; - EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForWindowSet( - windowSet, - CaptureHostSnapshots(), - m_hostRuntime.GetPrimaryWindowTitle(), - preferredNewWindowId, - preferredPlacement, - error); - if (!plan.valid) { - LogRuntimeTrace("window", "workspace synchronization rejected: " + error); - return false; - } - - return ApplySynchronizationPlan(plan); -} - -std::vector EditorWindowWorkspaceCoordinator::CaptureHostSnapshots() const { - std::vector snapshots = {}; - const std::vector windows = - std::as_const(m_hostRuntime).GetWindows(); - snapshots.reserve(windows.size()); - - for (const EditorHostWindow* window : windows) { - if (window == nullptr) { - continue; - } - - EditorWindowHostSnapshot snapshot = {}; - snapshot.windowId = std::string(window->GetWindowId()); - snapshot.workspaceWindow = window->IsWorkspaceWindow(); - snapshot.utilityWindow = window->IsUtilityWindow(); - snapshot.primary = window->IsPrimary(); - snapshot.running = window->GetLifecycleState() == EditorWindowLifecycleState::Running; - snapshot.destroyed = window->IsDestroyed(); - snapshot.hasNativeWindow = window->HasLiveHostWindow(); - snapshot.title = window->GetTitle(); - if (window->IsWorkspaceWindow()) { - if (const UIEditorWindowWorkspaceState* windowState = - m_windowSystem.FindWindowState(window->GetWindowId()); - windowState != nullptr) { - snapshot.hasWorkspaceState = true; - snapshot.workspaceState = *windowState; - } - } - snapshots.push_back(std::move(snapshot)); - } - - return snapshots; -} - -bool EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan( - const EditorWindowSynchronizationPlan& plan) { - const auto restoreWindowSnapshot = [this](const ExistingWindowSnapshot& snapshot) { - EditorHostWindow* const window = m_hostRuntime.FindWindowById(snapshot.windowId); - if (window == nullptr) { - return; - } - - window->SetPrimary(snapshot.primary); - window->RefreshWorkspaceProjection(snapshot.projection); - window->ResetInteractionState(); - RefreshWindowTitle(*window); - }; - - const auto destroyAndEraseWindowById = [this](std::string_view windowId) { - EditorHostWindow* const window = m_hostRuntime.FindWindowById(windowId); - if (window == nullptr || m_lifecycleCoordinator == nullptr) { - return false; - } - - m_lifecycleCoordinator->AbortUnregisteredWindow(*window); - return true; - }; - - std::vector existingWindowSnapshots = {}; - std::vector createdWindowIds = {}; - - for (const EditorWindowSynchronizationAction& action : plan.actions) { - switch (action.kind) { - case EditorWindowSynchronizationActionKind::UpdateWorkspaceWindow: { - EditorHostWindow* existingWindow = - m_hostRuntime.FindWindowById(action.update.windowState.windowId); - if (existingWindow == nullptr && m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->ReapDestroyedWindows(); - existingWindow = m_hostRuntime.FindWindowById(action.update.windowState.windowId); - } - if (existingWindow == nullptr) { - return false; - } - - existingWindowSnapshots.push_back(ExistingWindowSnapshot{ - std::string(existingWindow->GetWindowId()), - BuildWorkspaceProjectionForWindow(*existingWindow), - existingWindow->IsPrimary(), - }); - EditorWorkspaceWindowProjection projection = BuildWorkspaceProjectionForState( - action.update.windowState, - action.update.primary, - action.update.title); - existingWindow->SetPrimary(action.update.primary); - existingWindow->RefreshWorkspaceProjection(std::move(projection)); - existingWindow->ResetInteractionState(); - RefreshWindowTitle(*existingWindow); - break; - } - case EditorWindowSynchronizationActionKind::CreateWorkspaceWindow: { - EditorWindowCreateParams createParams = {}; - createParams.windowId = action.create.windowState.windowId; - createParams.category = EditorWindowCategory::Workspace; - createParams.primary = action.create.primary; - createParams.title = action.create.title; - if (action.create.placement.enabled) { - createParams.initialX = action.create.placement.initialX; - createParams.initialY = action.create.placement.initialY; - createParams.initialWidth = action.create.placement.initialWidth; - createParams.initialHeight = action.create.placement.initialHeight; - } - - EditorHostWindow* const createdWindow = m_windowManager.CreateWorkspaceWindow( - action.create.windowState, - createParams); - if (createdWindow == nullptr) { - for (const ExistingWindowSnapshot& snapshot : existingWindowSnapshots) { - restoreWindowSnapshot(snapshot); - } - for (auto it = createdWindowIds.rbegin(); it != createdWindowIds.rend(); ++it) { - destroyAndEraseWindowById(*it); - } - return false; - } - createdWindow->RefreshWorkspaceProjection(BuildWorkspaceProjectionForState( - action.create.windowState, - action.create.primary, - action.create.title)); - RefreshWindowTitle(*createdWindow); - createdWindowIds.push_back(action.create.windowState.windowId); - break; - } - case EditorWindowSynchronizationActionKind::CloseWorkspaceWindow: - if (m_lifecycleCoordinator != nullptr) { - if (EditorHostWindow* window = m_hostRuntime.FindWindowById(action.close.windowId); - window != nullptr) { - m_lifecycleCoordinator->PostCloseRequest(*window); - } - } - break; - } - } - - std::string storeError = {}; - if (!m_windowSystem.CommitSynchronizationPlan(plan, storeError)) { - LogRuntimeTrace( - "window", - "workspace synchronization produced invalid stored state: " + storeError); - return false; - } - - return true; -} - -bool EditorWindowWorkspaceCoordinator::CommitWindowWorkspaceMutation( - const UIEditorWindowWorkspaceController& windowWorkspaceController, - std::string_view preferredNewWindowId, - const EditorWindowScreenPoint& preferredScreenPoint, - int preferredWidth, - int preferredHeight) { - const UIEditorWindowWorkspaceSet nextWindowSet = windowWorkspaceController.GetWindowSet(); - std::string error = {}; - if (!m_windowSystem.ValidateWindowSet(nextWindowSet, error)) { - LogRuntimeTrace("window", "workspace mutation validation failed: " + error); - return false; - } - - if (!SynchronizeWindowsFromWindowSet( - nextWindowSet, - preferredNewWindowId, - preferredScreenPoint, - preferredWidth, - preferredHeight)) { - return false; - } - - return true; -} - -bool EditorWindowWorkspaceCoordinator::IsGlobalTabDragActive() const { - return m_globalTabDragSession.active; -} - -bool EditorWindowWorkspaceCoordinator::OwnsActiveGlobalTabDrag( - std::string_view windowId) const { - return m_globalTabDragSession.active && - m_globalTabDragSession.panelWindowId == windowId; -} - -void EditorWindowWorkspaceCoordinator::BeginGlobalTabDragSession( - std::string_view panelWindowId, - std::string_view sourceNodeId, - std::string_view panelId, - const EditorWindowScreenPoint& screenPoint, - const EditorWindowScreenPoint& dragHotspot) { - m_globalTabDragSession.active = true; - m_globalTabDragSession.panelWindowId = std::string(panelWindowId); - m_globalTabDragSession.sourceNodeId = std::string(sourceNodeId); - m_globalTabDragSession.panelId = std::string(panelId); - m_globalTabDragSession.screenPoint = screenPoint; - m_globalTabDragSession.dragHotspot = dragHotspot; -} - -bool EditorWindowWorkspaceCoordinator::TryResolveGlobalTabDragHotspot( - const EditorHostWindow& sourceWindow, - std::string_view nodeId, - std::string_view panelId, - const EditorWindowScreenPoint& screenPoint, - EditorWindowScreenPoint& outDragHotspot) const { - return sourceWindow.TryResolveDockTabDragHotspot( - nodeId, - panelId, - screenPoint, - outDragHotspot); -} - -void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragOwnerWindowPosition() { - if (!m_globalTabDragSession.active) { - return; - } - - EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); - if (!IsLiveInteractiveWindow(ownerWindow)) { - return; - } - - EditorWindowScreenRect windowRect = {}; - if (!ownerWindow->TryGetHostScreenRect(windowRect)) { - return; - } - - const std::int32_t targetLeft = - m_globalTabDragSession.screenPoint.x - m_globalTabDragSession.dragHotspot.x; - const std::int32_t targetTop = - m_globalTabDragSession.screenPoint.y - m_globalTabDragSession.dragHotspot.y; - if (windowRect.left == targetLeft && windowRect.top == targetTop) { - return; - } - - ownerWindow->SetHostScreenPosition(EditorWindowScreenPoint{ - .x = targetLeft, - .y = targetTop, - }); -} - -void EditorWindowWorkspaceCoordinator::ClearGlobalTabDragDropPreview() { - if (m_globalTabDragSession.previewWindowId.empty()) { - return; - } - - if (EditorHostWindow* previewWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.previewWindowId); - IsLiveInteractiveWindow(previewWindow)) { - if (EditorWindowDockHostBinding* dockHostBinding = - previewWindow->TryGetDockHostBinding(); - dockHostBinding != nullptr) { - dockHostBinding->ClearExternalDockHostDropPreview(); - previewWindow->InvalidateHostWindow(); - } - } - m_globalTabDragSession.previewWindowId.clear(); -} - -void EditorWindowWorkspaceCoordinator::UpdateGlobalTabDragDropPreview() { - if (!m_globalTabDragSession.active) { - return; - } - - EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint( - m_globalTabDragSession.screenPoint, - m_globalTabDragSession.panelWindowId); - if (!IsLiveInteractiveWindow(targetWindow)) { - ClearGlobalTabDragDropPreview(); - return; - } - - UIEditorDockHostTabDropTarget dropTarget = {}; - if (!targetWindow->TryResolveDockTabDropTarget( - m_globalTabDragSession.screenPoint, - dropTarget)) { - ClearGlobalTabDragDropPreview(); - return; - } - - if (!m_globalTabDragSession.previewWindowId.empty() && - m_globalTabDragSession.previewWindowId != targetWindow->GetWindowId()) { - ClearGlobalTabDragDropPreview(); - } - - Widgets::UIEditorDockHostDropPreviewState preview = {}; - preview.visible = true; - preview.sourceNodeId = m_globalTabDragSession.sourceNodeId; - preview.sourcePanelId = m_globalTabDragSession.panelId; - preview.targetNodeId = dropTarget.nodeId; - preview.placement = dropTarget.placement; - preview.insertionIndex = dropTarget.insertionIndex; - if (EditorWindowDockHostBinding* dockHostBinding = - targetWindow->TryGetDockHostBinding(); - dockHostBinding != nullptr) { - dockHostBinding->SetExternalDockHostDropPreview(preview); - targetWindow->InvalidateHostWindow(); - } - m_globalTabDragSession.previewWindowId = std::string(targetWindow->GetWindowId()); -} - -void EditorWindowWorkspaceCoordinator::EndGlobalTabDragSession() { - if (!m_globalTabDragSession.active) { - return; - } - - ClearGlobalTabDragDropPreview(); - - if (EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); - ownerWindow != nullptr) { - ownerWindow->ReleasePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag); - if (IsLiveInteractiveWindow(ownerWindow)) { - ownerWindow->ResetInteractionState(); - } - } - - m_globalTabDragSession = {}; -} - -bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerMove(EditorHostWindow& window) { - if (!m_globalTabDragSession.active) { - return false; - } - - const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); - if (!IsLiveInteractiveWindow(ownerWindow)) { - EndGlobalTabDragSession(); - return false; - } - if (ownerWindow->GetWindowId() != window.GetWindowId()) { - return false; - } - - EditorWindowScreenPoint screenPoint = {}; - if (m_hostRuntime.TryGetCursorScreenPoint(screenPoint)) { - m_globalTabDragSession.screenPoint = screenPoint; - UpdateGlobalTabDragOwnerWindowPosition(); - UpdateGlobalTabDragDropPreview(); - } - return true; -} - -bool EditorWindowWorkspaceCoordinator::HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window) { - if (!m_globalTabDragSession.active) { - return false; - } - - const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); - if (!IsLiveInteractiveWindow(ownerWindow)) { - EndGlobalTabDragSession(); - return false; - } - if (ownerWindow->GetWindowId() != window.GetWindowId()) { - return false; - } - - EditorWindowScreenPoint screenPoint = m_globalTabDragSession.screenPoint; - m_hostRuntime.TryGetCursorScreenPoint(screenPoint); - - const std::string panelWindowId = m_globalTabDragSession.panelWindowId; - const std::string sourceNodeId = m_globalTabDragSession.sourceNodeId; - const std::string panelId = m_globalTabDragSession.panelId; - EndGlobalTabDragSession(); - - EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint(screenPoint, panelWindowId); - if (!IsLiveInteractiveWindow(targetWindow)) { - return true; - } - - UIEditorDockHostTabDropTarget dropTarget = {}; - if (!targetWindow->TryResolveDockTabDropTarget(screenPoint, dropTarget)) { - return true; - } - - UIEditorWindowWorkspaceController windowWorkspaceController = - BuildWorkspaceMutationController(); - const UIEditorWindowWorkspaceOperationResult result = - dropTarget.placement == UIEditorWorkspaceDockPlacement::Center - ? windowWorkspaceController.MovePanelToStack( - panelWindowId, - sourceNodeId, - panelId, - targetWindow->GetWindowId(), - dropTarget.nodeId, - dropTarget.insertionIndex) - : windowWorkspaceController.DockPanelRelative( - panelWindowId, - sourceNodeId, - panelId, - targetWindow->GetWindowId(), - dropTarget.nodeId, - dropTarget.placement); - if (result.status != UIEditorWindowWorkspaceOperationStatus::Changed) { - LogRuntimeTrace("drag", "cross-window drop rejected: " + result.message); - return true; - } - - if (!CommitWindowWorkspaceMutation( - windowWorkspaceController, - {}, - screenPoint)) { - LogRuntimeTrace("drag", "failed to synchronize windows after cross-window drop"); - return true; - } - - if (EditorHostWindow* updatedTargetWindow = m_hostRuntime.FindWindowById(targetWindow->GetWindowId()); - IsLiveInteractiveWindow(updatedTargetWindow)) { - updatedTargetWindow->FocusHostWindow(); - } - LogRuntimeTrace( - "drag", - "committed cross-window drop panel '" + panelId + - "' into window '" + std::string(targetWindow->GetWindowId()) + "'"); - return true; -} - -bool EditorWindowWorkspaceCoordinator::TryStartGlobalTabDrag( - EditorHostWindow& sourceWindow, - const EditorWindowPanelTransferRequest& request) { - if (!sourceWindow.IsWorkspaceWindow()) { - LogRuntimeTrace( - "drag", - "failed to start global tab drag: source window is not a workspace window"); - return false; - } - - if (!IsLiveInteractiveWindow(&sourceWindow)) { - LogRuntimeTrace("drag", "failed to start global tab drag: source window is closing"); - return false; - } - - EditorWindowScreenPoint dragHotspot = BuildFallbackGlobalTabDragHotspot(); - TryResolveGlobalTabDragHotspot( - sourceWindow, - request.nodeId, - request.panelId, - request.screenPoint, - dragHotspot); - - const auto tryStartDetachedPanelGlobalDrag = - [this, &request, &dragHotspot]( - UIEditorWindowWorkspaceController& windowWorkspaceController, - const UIEditorWindowWorkspaceOperationResult& result) { - if (!CommitWindowWorkspaceMutation( - windowWorkspaceController, - result.targetWindowId, - request.screenPoint)) { - LogRuntimeTrace("drag", "failed to synchronize detached drag window state"); - return false; - } - - EditorHostWindow* detachedWindow = m_hostRuntime.FindWindowById(result.targetWindowId); - if (!IsLiveInteractiveWindow(detachedWindow)) { - LogRuntimeTrace("drag", "detached drag window was not created."); - return false; - } - - const UIEditorWindowWorkspaceState* detachedWindowState = - m_windowSystem.FindWindowState(result.targetWindowId); - if (detachedWindowState == nullptr) { - LogRuntimeTrace("drag", "detached drag window state was not committed."); - return false; - } - - BeginGlobalTabDragSession( - detachedWindow->GetWindowId(), - detachedWindowState->workspace.root.nodeId, - request.panelId, - request.screenPoint, - dragHotspot); - UpdateGlobalTabDragOwnerWindowPosition(); - detachedWindow->AcquirePointerCapture( - EditorWindowPointerCaptureOwner::GlobalTabDrag); - detachedWindow->FocusHostWindow(); - LogRuntimeTrace( - "drag", - "started global tab drag by detaching panel '" + request.panelId + - "' into window '" + std::string(detachedWindow->GetWindowId()) + "'"); - return true; - }; - - UIEditorWindowWorkspaceController windowWorkspaceController = - BuildWorkspaceMutationController(); - const UIEditorWindowWorkspaceOperationResult result = - windowWorkspaceController.DetachPanelToNewWindow( - sourceWindow.GetWindowId(), - request.nodeId, - request.panelId); - if (result.status == UIEditorWindowWorkspaceOperationStatus::Changed) { - return tryStartDetachedPanelGlobalDrag(windowWorkspaceController, result); - } - - if (sourceWindow.IsPrimary()) { - LogRuntimeTrace( - "drag", - "failed to start global tab drag from primary window: " + result.message); - return false; - } - - sourceWindow.ResetInteractionState(); - if (result.status != UIEditorWindowWorkspaceOperationStatus::NoOp) { - LogRuntimeTrace( - "drag", - "failed to start global tab drag from detached window: " + result.message); - return false; - } - if (!CanStartGlobalTabDragFromWindow( - m_windowSystem, - sourceWindow, - request.nodeId, - request.panelId)) { - LogRuntimeTrace( - "drag", - "failed to start global tab drag from detached window: invalid source panel request"); - return false; - } - - BeginGlobalTabDragSession( - sourceWindow.GetWindowId(), - request.nodeId, - request.panelId, - request.screenPoint, - dragHotspot); - UpdateGlobalTabDragOwnerWindowPosition(); - sourceWindow.AcquirePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag); - LogRuntimeTrace( - "drag", - "started global tab drag from detached window '" + - std::string(sourceWindow.GetWindowId()) + - "' panel '" + request.panelId + "'"); - return true; -} - -bool EditorWindowWorkspaceCoordinator::TryProcessDetachRequest( - EditorHostWindow& sourceWindow, - const EditorWindowPanelTransferRequest& request) { - if (!sourceWindow.IsWorkspaceWindow()) { - LogRuntimeTrace( - "detach", - "detach request rejected: source window is not a workspace window"); - return false; - } - - if (!IsLiveInteractiveWindow(&sourceWindow)) { - LogRuntimeTrace("detach", "detach request rejected: source window is closing"); - return false; - } - - const std::string sourceWindowId(sourceWindow.GetWindowId()); - UIEditorWindowWorkspaceController windowWorkspaceController = {}; - const UIEditorWindowWorkspaceOperationResult result = - m_windowSystem.EvaluateDetachPanelToNewWindow( - sourceWindowId, - request.nodeId, - request.panelId, - windowWorkspaceController); - if (result.status != UIEditorWindowWorkspaceOperationStatus::Changed) { - LogRuntimeTrace("detach", "detach request rejected: " + result.message); - return false; - } - - if (!CommitWindowWorkspaceMutation( - windowWorkspaceController, - result.targetWindowId, - request.screenPoint)) { - LogRuntimeTrace("detach", "failed to synchronize detached window state"); - return false; - } - - if (EditorHostWindow* detachedWindow = m_hostRuntime.FindWindowById(result.targetWindowId); - IsLiveInteractiveWindow(detachedWindow)) { - detachedWindow->FocusHostWindow(); - } - - LogRuntimeTrace( - "detach", - "detached panel '" + request.panelId + "' from window '" + sourceWindowId + - "' to window '" + result.targetWindowId + "'"); - return true; -} - -void EditorWindowWorkspaceCoordinator::HandleWindowFrameTransferRequests( - EditorHostWindow& sourceWindow, - const EditorWindowFrameTransferRequests& transferRequests) { - if (!m_globalTabDragSession.active && - transferRequests.workspace.beginGlobalTabDrag.has_value() && - transferRequests.workspace.beginGlobalTabDrag->IsValid()) { - TryStartGlobalTabDrag( - sourceWindow, - *transferRequests.workspace.beginGlobalTabDrag); - } - - if (!m_globalTabDragSession.active && - transferRequests.workspace.detachPanel.has_value() && - transferRequests.workspace.detachPanel->IsValid()) { - TryProcessDetachRequest( - sourceWindow, - *transferRequests.workspace.detachPanel); - } -} - -EditorHostWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint( - const EditorWindowScreenPoint& screenPoint, - std::string_view excludedWindowId) { - if (EditorHostWindow* window = m_hostRuntime.FindWindowFromScreenPoint(screenPoint); - IsLiveInteractiveWindow(window) && - window->IsWorkspaceWindow() && - window->GetWindowId() != excludedWindowId) { - return window; - } - - std::vector windows = m_hostRuntime.GetWindows(); - for (auto it = windows.rbegin(); it != windows.rend(); ++it) { - EditorHostWindow* const window = *it; - if (window == nullptr || - !IsLiveInteractiveWindow(window) || - !window->IsWorkspaceWindow() || - window->GetWindowId() == excludedWindowId) { - continue; - } - - EditorWindowScreenRect windowRect = {}; - if (window->TryGetHostScreenRect(windowRect) && - windowRect.Contains(screenPoint)) { - return window; - } - } - - return nullptr; -} - -const EditorHostWindow* EditorWindowWorkspaceCoordinator::FindTopmostWindowAtScreenPoint( - const EditorWindowScreenPoint& screenPoint, - std::string_view excludedWindowId) const { - return const_cast(this)->FindTopmostWindowAtScreenPoint( - screenPoint, - excludedWindowId); -} - -void EditorWindowWorkspaceCoordinator::LogRuntimeTrace( - std::string_view channel, - std::string_view message) const { - m_hostRuntime.LogRuntimeTrace(channel, message); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h b/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h deleted file mode 100644 index d2a549eb..00000000 --- a/editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.h +++ /dev/null @@ -1,132 +0,0 @@ -#pragma once - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include "Windowing/EditorWindowTransferRequests.h" -#include "EditorWindowHostInterfaces.h" - -#include -#include -#include -#include - -#include -#include -#include - -namespace XCEngine::UI::Editor { -class EditorWindowSystem; - -namespace Rendering::Host { -class EditorWindowRenderRuntimeFactory; -} -} - -namespace XCEngine::UI::Editor::App { - -class EditorWindowContentFactory; -class EditorWindowLifecycleCoordinator; -class EditorWindowManager; - -class EditorWindowWorkspaceCoordinator final { -public: - EditorWindowWorkspaceCoordinator( - EditorWindowHost& hostRuntime, - EditorWindowManager& windowManager, - EditorWindowSystem& windowSystem, - Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, - EditorWindowContentFactory& contentFactory); - ~EditorWindowWorkspaceCoordinator(); - - void BindLifecycleCoordinator(EditorWindowLifecycleCoordinator& lifecycleCoordinator); - void RegisterExistingWindow(EditorHostWindow& window); - void RefreshWindowPresentation(EditorHostWindow& window); - void HandleNativeWindowDestroyed(std::string_view windowId); - bool IsPrimaryWindowId(std::string_view windowId) const; - std::string DescribeWindowSet() const; - - bool IsGlobalTabDragActive() const; - bool OwnsActiveGlobalTabDrag(std::string_view windowId) const; - void EndGlobalTabDragSession(); - bool HandleGlobalTabDragPointerMove(EditorHostWindow& window); - bool HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window); - void HandleWindowFrameTransferRequests( - EditorHostWindow& sourceWindow, - const EditorWindowFrameTransferRequests& transferRequests); - -private: - struct GlobalTabDragSession { - bool active = false; - std::string panelWindowId = {}; - std::string sourceNodeId = {}; - std::string panelId = {}; - std::string previewWindowId = {}; - EditorWindowScreenPoint screenPoint = {}; - EditorWindowScreenPoint dragHotspot = {}; - }; - - UIEditorWindowWorkspaceController BuildWorkspaceMutationController() const; - bool SynchronizeWindowsFromWindowSet( - const UIEditorWindowWorkspaceSet& windowSet, - std::string_view preferredNewWindowId, - const EditorWindowScreenPoint& preferredScreenPoint, - int preferredWidth = 0, - int preferredHeight = 0); - bool ApplySynchronizationPlan(const EditorWindowSynchronizationPlan& plan); - std::vector CaptureHostSnapshots() const; - bool CommitWindowWorkspaceMutation( - const UIEditorWindowWorkspaceController& windowWorkspaceController, - std::string_view preferredNewWindowId, - const EditorWindowScreenPoint& preferredScreenPoint, - int preferredWidth = 0, - int preferredHeight = 0); - UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorHostWindow& window) const; - EditorWorkspaceWindowProjection BuildWorkspaceProjectionForState( - const UIEditorWindowWorkspaceState& windowState, - bool primary, - std::wstring title) const; - EditorWorkspaceWindowProjection BuildWorkspaceProjectionForWindow( - const EditorHostWindow& window) const; - bool RefreshWorkspaceProjectionFromAuthoritativeState(EditorHostWindow& window) const; - void RefreshWindowTitle(EditorHostWindow& window) const; - void BeginGlobalTabDragSession( - std::string_view panelWindowId, - std::string_view sourceNodeId, - std::string_view panelId, - const EditorWindowScreenPoint& screenPoint, - const EditorWindowScreenPoint& dragHotspot); - bool TryResolveGlobalTabDragHotspot( - const EditorHostWindow& sourceWindow, - std::string_view nodeId, - std::string_view panelId, - const EditorWindowScreenPoint& screenPoint, - EditorWindowScreenPoint& outDragHotspot) const; - void ClearGlobalTabDragDropPreview(); - void UpdateGlobalTabDragDropPreview(); - void UpdateGlobalTabDragOwnerWindowPosition(); - EditorHostWindow* FindTopmostWindowAtScreenPoint( - const EditorWindowScreenPoint& screenPoint, - std::string_view excludedWindowId = {}); - const EditorHostWindow* FindTopmostWindowAtScreenPoint( - const EditorWindowScreenPoint& screenPoint, - std::string_view excludedWindowId = {}) const; - bool TryStartGlobalTabDrag( - EditorHostWindow& sourceWindow, - const EditorWindowPanelTransferRequest& request); - bool TryProcessDetachRequest( - EditorHostWindow& sourceWindow, - const EditorWindowPanelTransferRequest& request); - void LogRuntimeTrace(std::string_view channel, std::string_view message) const; - - EditorWindowHost& m_hostRuntime; - EditorWindowManager& m_windowManager; - EditorWindowSystem& m_windowSystem; - Rendering::Host::EditorWindowRenderRuntimeFactory& m_renderRuntimeFactory; - EditorWindowContentFactory& m_contentFactory; - EditorWindowLifecycleCoordinator* m_lifecycleCoordinator = nullptr; - GlobalTabDragSession m_globalTabDragSession = {}; -}; - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/EditorWindowManager.cpp b/editor/app/Windowing/EditorWindowManager.cpp deleted file mode 100644 index bf89cfd8..00000000 --- a/editor/app/Windowing/EditorWindowManager.cpp +++ /dev/null @@ -1,348 +0,0 @@ -#include "EditorWindowManager.h" - -#include "Content/EditorWindowContentController.h" -#include "Content/EditorWindowContentFactory.h" -#include "Coordinator/EditorWindowLifecycleCoordinator.h" -#include "Coordinator/EditorUtilityWindowCoordinator.h" -#include "Coordinator/EditorWindowWorkspaceCoordinator.h" -#include "EditorWindowInstance.h" -#include "Runtime/EditorWindowRuntimeController.h" - -#include "EditorWindowRenderRuntime.h" - -#include - -#include -#include -#include -#include - -namespace XCEngine::UI::Editor::App { - -EditorWindowManager::EditorWindowManager( - const EditorFrameValidation& frameValidation, - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, - std::function()> consumeOpenUtilityWindowRequest, - SceneViewportEngineBridge& sceneViewportEngineBridge, - GameViewportEngineBridge& gameViewportEngineBridge, - EditorShaderProvider& shaderProvider, - EditorWindowSystem& windowSystem, - Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, - Host::EditorHostResourceService& resourceService, - EditorWindowHostRuntimeServices& hostRuntime, - EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, - EditorUtilityWindowPanelFactory utilityPanelFactory) - : m_frameValidation(frameValidation) - , m_shellDefinitionProvider(shellDefinitionProvider) - , m_frameStatusService(frameStatusService) - , m_consumeOpenUtilityWindowRequest(std::move(consumeOpenUtilityWindowRequest)) - , m_sceneViewportEngineBridge(sceneViewportEngineBridge) - , m_gameViewportEngineBridge(gameViewportEngineBridge) - , m_shaderProvider(shaderProvider) - , m_renderRuntimeFactory(renderRuntimeFactory) - , m_resourceService(resourceService) - , m_hostRuntime(hostRuntime) { - m_contentFactory = CreateDefaultEditorWindowContentFactory( - windowSystem, - std::move(workspaceShellRuntimeFactory), - std::move(utilityPanelFactory)); - m_workspaceCoordinator = - std::make_unique( - m_hostRuntime, - *this, - windowSystem, - m_renderRuntimeFactory, - *m_contentFactory); - m_utilityCoordinator = - std::make_unique( - m_hostRuntime, - *this, - m_renderRuntimeFactory, - *m_contentFactory); - m_lifecycleCoordinator = std::make_unique( - m_hostRuntime, - *m_workspaceCoordinator); - m_hostRuntime.BindHostCoordinator(*this); - m_workspaceCoordinator->BindLifecycleCoordinator(*m_lifecycleCoordinator); - m_utilityCoordinator->BindLifecycleCoordinator(*m_lifecycleCoordinator); -} - -EditorWindowManager::~EditorWindowManager() = default; - -EditorHostWindow* EditorWindowManager::CreateWorkspaceWindow( - const UIEditorWindowWorkspaceState& windowState, - const EditorWindowCreateParams& params) { - if (m_contentFactory == nullptr) { - return nullptr; - } - if (windowState.windowId.empty()) { - return nullptr; - } - if (!params.windowId.empty() && windowState.windowId != params.windowId) { - return nullptr; - } - - auto windowInstance = CreateEditorWindowInstance( - params.windowId, - params.title, - params.category, - params.chromePolicy, - params.primary, - std::make_unique( - m_frameValidation, - m_shellDefinitionProvider, - m_frameStatusService, - m_consumeOpenUtilityWindowRequest, - m_sceneViewportEngineBridge, - m_gameViewportEngineBridge, - m_shaderProvider, - m_resourceService, - m_contentFactory->CreateWorkspaceContentController(windowState), - m_renderRuntimeFactory.CreateWindowRenderRuntime())); - EditorHostWindow* const window = windowInstance.get(); - if (!m_hostRuntime.CreateHostWindow(*window, params)) { - return nullptr; - } - m_windows.push_back(std::move(windowInstance)); - if (window != nullptr) { - m_workspaceCoordinator->RegisterExistingWindow(*window); - } - return window; -} - -EditorHostWindow* EditorWindowManager::CreateUtilityWindow( - const EditorUtilityWindowDescriptor& descriptor, - const EditorWindowCreateParams& params) { - if (m_contentFactory == nullptr) { - return nullptr; - } - - auto windowInstance = CreateEditorWindowInstance( - params.windowId, - params.title, - params.category, - params.chromePolicy, - params.primary, - std::make_unique( - m_frameValidation, - m_shellDefinitionProvider, - m_frameStatusService, - m_consumeOpenUtilityWindowRequest, - m_sceneViewportEngineBridge, - m_gameViewportEngineBridge, - m_shaderProvider, - m_resourceService, - m_contentFactory->CreateUtilityContentController(descriptor), - m_renderRuntimeFactory.CreateWindowRenderRuntime())); - EditorHostWindow* const window = windowInstance.get(); - if (!m_hostRuntime.CreateHostWindow(*window, params)) { - return nullptr; - } - m_windows.push_back(std::move(windowInstance)); - if (window != nullptr) { - m_workspaceCoordinator->RegisterExistingWindow(*window); - } - return window; -} - -void EditorWindowManager::Shutdown() { - m_workspaceCoordinator->EndGlobalTabDragSession(); - m_lifecycleCoordinator->ShutdownAllWindows(); - ReapDestroyedWindows(); - m_windows.clear(); -} - -bool EditorWindowManager::RequestPrimaryWindowClose() { - if (m_lifecycleCoordinator == nullptr) { - return false; - } - - for (const std::unique_ptr& window : m_windows) { - if (window == nullptr || !window->IsPrimary()) { - continue; - } - - m_lifecycleCoordinator->PostCloseRequest(*window); - return true; - } - - return false; -} - -bool EditorWindowManager::HasWindows() const { - return !m_windows.empty(); -} - -void EditorWindowManager::DestroyClosedWindows() { - ReapDestroyedWindows(); -} - -void EditorWindowManager::RenderAllWindows() { - struct WindowFrameTransferBatch { - EditorHostWindow* sourceWindow = nullptr; - EditorWindowFrameTransferRequests requests = {}; - }; - - std::vector transferBatches = {}; - std::vector windows = {}; - windows.reserve(m_windows.size()); - for (const std::unique_ptr& window : m_windows) { - if (window != nullptr) { - windows.push_back(window.get()); - } - } - transferBatches.reserve(windows.size()); - - for (EditorHostWindow* window : windows) { - if (window == nullptr || - !window->HasLiveHostWindow() || - window->GetLifecycleState() != EditorWindowLifecycleState::Running) { - continue; - } - - if (window->ConsumeSkipNextSteadyStateFrame()) { - RefreshWindowPresentation(*window); - continue; - } - - EditorWindowFrameTransferRequests transferRequests = DriveWindowFrame(*window); - RefreshWindowPresentation(*window); - if (!transferRequests.HasPendingRequests()) { - continue; - } - - transferBatches.push_back(WindowFrameTransferBatch{ - window, - std::move(transferRequests), - }); - } - - for (WindowFrameTransferBatch& batch : transferBatches) { - if (batch.sourceWindow == nullptr || - !batch.sourceWindow->HasLiveHostWindow() || - batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) { - continue; - } - - DispatchWindowFrameTransferRequests( - *batch.sourceWindow, - batch.requests); - } -} - -bool EditorWindowManager::InitializeHostWindow( - EditorHostWindow& window, - const EditorHostWindowRuntimeInitializationParams& params) { - return window.InitializeRuntime(params); -} - -EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrame( - EditorHostWindow& window) { - return DriveWindowFrameInternal(window, false); -} - -EditorWindowFrameTransferRequests EditorWindowManager::DriveImmediateWindowFrame( - EditorHostWindow& window) { - return DriveWindowFrameInternal(window, true); -} - -EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrameInternal( - EditorHostWindow& window, - bool requestSkipNextSteadyStateFrame) { - if (!window.IsRenderReady() || - !window.HasLiveHostWindow() || - window.GetLifecycleState() != EditorWindowLifecycleState::Running) { - return {}; - } - - EditorWindowFrameTransferRequests transferRequests = - window.RenderHostFrame(IsGlobalTabDragActive()); - window.ValidateHostFrame(); - if (requestSkipNextSteadyStateFrame) { - window.RequestSkipNextSteadyStateFrame(); - } - - return transferRequests; -} - -bool EditorWindowManager::IsGlobalTabDragActive() const { - return m_workspaceCoordinator != nullptr && - m_workspaceCoordinator->IsGlobalTabDragActive(); -} - -bool EditorWindowManager::OwnsActiveGlobalTabDrag(std::string_view windowId) const { - return m_workspaceCoordinator != nullptr && - m_workspaceCoordinator->OwnsActiveGlobalTabDrag(windowId); -} - -bool EditorWindowManager::HandleGlobalTabDragPointerMove(EditorHostWindow& window) { - return m_workspaceCoordinator != nullptr && - m_workspaceCoordinator->HandleGlobalTabDragPointerMove(window); -} - -bool EditorWindowManager::HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window) { - return m_workspaceCoordinator != nullptr && - m_workspaceCoordinator->HandleGlobalTabDragPointerButtonUp(window); -} - -void EditorWindowManager::EndGlobalTabDragSession() { - if (m_workspaceCoordinator != nullptr) { - m_workspaceCoordinator->EndGlobalTabDragSession(); - } -} - -void EditorWindowManager::RefreshWindowPresentation(EditorHostWindow& window) { - if (m_workspaceCoordinator != nullptr) { - m_workspaceCoordinator->RefreshWindowPresentation(window); - } -} - -void EditorWindowManager::DispatchWindowFrameTransferRequests( - EditorHostWindow& sourceWindow, - const EditorWindowFrameTransferRequests& transferRequests) { - if (m_workspaceCoordinator != nullptr) { - m_workspaceCoordinator->HandleWindowFrameTransferRequests( - sourceWindow, - transferRequests); - } - if (m_utilityCoordinator != nullptr) { - m_utilityCoordinator->HandleWindowFrameTransferRequests( - sourceWindow, - transferRequests); - } -} - -void EditorWindowManager::ExecuteCloseRequest(EditorHostWindow& window) { - if (m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->ExecuteCloseRequest(window); - } -} - -void EditorWindowManager::HandleNativeWindowDestroyed(EditorHostWindow& window) { - if (m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->HandleNativeWindowDestroyed(window); - } -} - -void EditorWindowManager::AbortUnregisteredWindow(EditorHostWindow& window) { - if (m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->AbortUnregisteredWindow(window); - } -} - -void EditorWindowManager::ReapDestroyedWindows() { - if (m_lifecycleCoordinator != nullptr) { - m_lifecycleCoordinator->ReapDestroyedWindows(); - } - m_windows.erase( - std::remove_if( - m_windows.begin(), - m_windows.end(), - [](const std::unique_ptr& window) { - return window == nullptr || window->IsDestroyed(); - }), - m_windows.end()); -} - -} // namespace XCEngine::UI::Editor::App diff --git a/editor/include/XCEditor/Windowing/System/EditorWindowSystem.h b/editor/include/XCEditor/Windowing/System/EditorWindowSystem.h index 283a1f65..14068bb0 100644 --- a/editor/include/XCEditor/Windowing/System/EditorWindowSystem.h +++ b/editor/include/XCEditor/Windowing/System/EditorWindowSystem.h @@ -4,14 +4,22 @@ #include #include -#include +#include #include #include #include namespace XCEngine::UI::Editor { -class EditorWindowWorkspaceStore; +struct EditorWindowWorkspaceMutation { + UIEditorWindowWorkspaceOperationResult operation = {}; + UIEditorWindowWorkspaceSet targetWindowSet = {}; + std::string preferredNewWindowId = {}; + + [[nodiscard]] bool HasStateChange() const { + return operation.status == UIEditorWindowWorkspaceOperationStatus::Changed; + } +}; class EditorWindowSystem final { public: @@ -25,22 +33,19 @@ public: const UIEditorPanelRegistry& GetPanelRegistry() const; - bool BootstrapPrimaryWindow( - std::string_view primaryWindowId, - const UIEditorWindowWorkspaceState& primaryWindowState, - std::string& outError); - bool ValidateWindowSet( const UIEditorWindowWorkspaceSet& windowSet, std::string& outError) const; - bool IsPrimaryWindowId(std::string_view windowId) const; - - const UIEditorWindowWorkspaceSet& GetWindowSet() const; - const UIEditorWindowWorkspaceState* FindWindowState(std::string_view windowId) const; - bool TryBuildLiveWindowWorkspaceController( + bool IsPrimaryWindowId( + const UIEditorWindowWorkspaceSet& windowSet, + std::string_view windowId) const; + const UIEditorWindowWorkspaceState* FindWindowState( + const UIEditorWindowWorkspaceSet& windowSet, + std::string_view windowId) const; + bool TryBuildWindowWorkspaceController( + const UIEditorWindowWorkspaceSet& windowSet, std::string_view windowId, - UIEditorWorkspaceController& outController); - UIEditorWindowWorkspaceController BuildWorkspaceMutationController() const; + UIEditorWorkspaceController& outController) const; EditorWindowSynchronizationPlan BuildPlanForWindowSet( const UIEditorWindowWorkspaceSet& targetWindowSet, const std::vector& hostWindows, @@ -49,6 +54,7 @@ public: const EditorWindowSynchronizationPlacement& preferredPlacement, std::string& outError) const; EditorWindowSynchronizationPlan BuildPlanForDestroyedWindow( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, std::string_view windowId, const std::vector& hostWindows, std::wstring_view primaryWindowTitle, @@ -56,22 +62,41 @@ public: EditorWindowSynchronizationPlan BuildSynchronizationPlan( const EditorWindowSynchronizationPlannerInput& input, std::string& outError) const; - bool CommitSynchronizationPlan( - const EditorWindowSynchronizationPlan& plan, - std::string& outError); - UIEditorWindowWorkspaceOperationResult EvaluateDetachPanelToNewWindow( + EditorWindowWorkspaceMutation EvaluateDetachPanelToNewWindow( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId) const; + EditorWindowWorkspaceMutation EvaluateMovePanelToStack( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, std::string_view sourceWindowId, std::string_view sourceNodeId, std::string_view panelId, - UIEditorWindowWorkspaceController& outController) const; + std::string_view targetWindowId, + std::string_view targetNodeId, + std::size_t targetVisibleInsertionIndex) const; + EditorWindowWorkspaceMutation EvaluateDockPanelRelative( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + UIEditorWorkspaceDockPlacement placement, + float splitRatio = 0.5f) const; private: + EditorWindowWorkspaceMutation BuildWorkspaceMutation( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + const std::function& evaluator, + std::string preferredNewWindowId = {}) const; static bool RemoveWindowStateFromSet( UIEditorWindowWorkspaceSet& windowSet, std::string_view windowId); - std::unique_ptr m_workspaceStore = {}; + UIEditorPanelRegistry m_panelRegistry = {}; }; } // namespace XCEngine::UI::Editor diff --git a/editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h b/editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h index 1db3b2e7..be4f0c3e 100644 --- a/editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h +++ b/editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h @@ -83,26 +83,32 @@ public: UIEditorPanelRegistry panelRegistry, UIEditorWorkspaceModel workspace, UIEditorWorkspaceSession session); + static UIEditorWorkspaceController Bind( + const UIEditorPanelRegistry& panelRegistry, + UIEditorWorkspaceModel& workspace, + UIEditorWorkspaceSession& session); + void Rebind( + UIEditorWorkspaceModel& workspace, + UIEditorWorkspaceSession& session); UIEditorWorkspaceController(const UIEditorWorkspaceController& other); UIEditorWorkspaceController& operator=(const UIEditorWorkspaceController& other); UIEditorWorkspaceController(UIEditorWorkspaceController&&) noexcept = default; UIEditorWorkspaceController& operator=(UIEditorWorkspaceController&&) noexcept = default; - static UIEditorWorkspaceController BindToState( - UIEditorPanelRegistry panelRegistry, - UIEditorWorkspaceModel& workspace, - UIEditorWorkspaceSession& session); - const UIEditorPanelRegistry& GetPanelRegistry() const { return m_panelRegistry; } const UIEditorWorkspaceModel& GetWorkspace() const { - return m_workspace; + return m_workspaceBinding != nullptr + ? *m_workspaceBinding + : m_workspace; } const UIEditorWorkspaceSession& GetSession() const { - return m_session; + return m_sessionBinding != nullptr + ? *m_sessionBinding + : m_session; } UIEditorWorkspaceControllerValidationResult ValidateState() const; @@ -128,8 +134,6 @@ public: UIEditorWorkspaceCommandResult Dispatch(const UIEditorWorkspaceCommand& command); private: - void SyncBoundState(); - UIEditorWorkspaceCommandResult BuildResult( const UIEditorWorkspaceCommand& command, UIEditorWorkspaceCommandStatus status, @@ -148,14 +152,16 @@ private: std::string message) const; const UIEditorPanelDescriptor* FindPanelDescriptor(std::string_view panelId) const; + UIEditorWorkspaceModel& GetMutableWorkspace(); + UIEditorWorkspaceSession& GetMutableSession(); UIEditorPanelRegistry m_panelRegistry = {}; UIEditorWorkspaceModel m_baselineWorkspace = {}; UIEditorWorkspaceSession m_baselineSession = {}; UIEditorWorkspaceModel m_workspace = {}; UIEditorWorkspaceSession m_session = {}; - UIEditorWorkspaceModel* m_boundWorkspace = nullptr; - UIEditorWorkspaceSession* m_boundSession = nullptr; + UIEditorWorkspaceModel* m_workspaceBinding = nullptr; + UIEditorWorkspaceSession* m_sessionBinding = nullptr; }; UIEditorWorkspaceController BuildDefaultUIEditorWorkspaceController( diff --git a/editor/src/Product/Commands/EditorCommand.cpp b/editor/src/Product/Commands/EditorCommand.cpp new file mode 100644 index 00000000..ed5f45f7 --- /dev/null +++ b/editor/src/Product/Commands/EditorCommand.cpp @@ -0,0 +1,78 @@ +#include "Product/Commands/EditorCommand.h" + +#include + +namespace XCEngine::UI::Editor::Product { + +std::string_view GetEditorCommandKindName(EditorCommandKind kind) { + switch (kind) { + case EditorCommandKind::ClearStatus: + return "ClearStatus"; + case EditorCommandKind::SetStatus: + return "SetStatus"; + case EditorCommandKind::AppendConsoleEntry: + return "AppendConsoleEntry"; + case EditorCommandKind::SetSelection: + return "SetSelection"; + case EditorCommandKind::RequestOpenUtilityWindow: + return "RequestOpenUtilityWindow"; + case EditorCommandKind::ConsumePendingUtilityWindowRequest: + return "ConsumePendingUtilityWindowRequest"; + case EditorCommandKind::None: + default: + return "None"; + } +} + +EditorCommand EditorCommand::ClearStatus() { + EditorCommand command = {}; + command.kind = EditorCommandKind::ClearStatus; + return command; +} + +EditorCommand EditorCommand::SetStatus( + std::string channel, + std::string message) { + EditorCommand command = {}; + command.kind = EditorCommandKind::SetStatus; + command.status.channel = std::move(channel); + command.status.message = std::move(message); + return command; +} + +EditorCommand EditorCommand::SetReadyStatus() { + return SetStatus("Ready", "Application shell loaded."); +} + +EditorCommand EditorCommand::AppendConsoleEntry( + std::string channel, + std::string message) { + EditorCommand command = {}; + command.kind = EditorCommandKind::AppendConsoleEntry; + command.consoleEntry.channel = std::move(channel); + command.consoleEntry.message = std::move(message); + return command; +} + +EditorCommand EditorCommand::SetSelection(App::EditorSelectionState selection) { + EditorCommand command = {}; + command.kind = EditorCommandKind::SetSelection; + command.selection = std::move(selection); + return command; +} + +EditorCommand EditorCommand::RequestOpenUtilityWindow( + App::EditorUtilityWindowKind kind) { + EditorCommand command = {}; + command.kind = EditorCommandKind::RequestOpenUtilityWindow; + command.utilityWindowKind = kind; + return command; +} + +EditorCommand EditorCommand::ConsumePendingUtilityWindowRequest() { + EditorCommand command = {}; + command.kind = EditorCommandKind::ConsumePendingUtilityWindowRequest; + return command; +} + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/src/Product/Commands/EditorCommand.h b/editor/src/Product/Commands/EditorCommand.h new file mode 100644 index 00000000..8bf3b477 --- /dev/null +++ b/editor/src/Product/Commands/EditorCommand.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Product/State/EditorState.h" + +#include + +#include +#include + +namespace XCEngine::UI::Editor::Product { + +enum class EditorCommandKind : std::uint8_t { + None = 0, + ClearStatus, + SetStatus, + AppendConsoleEntry, + SetSelection, + RequestOpenUtilityWindow, + ConsumePendingUtilityWindowRequest, +}; + +std::string_view GetEditorCommandKindName(EditorCommandKind kind); + +struct EditorCommand { + EditorCommandKind kind = EditorCommandKind::None; + EditorStatusState status = {}; + App::EditorConsoleEntry consoleEntry = {}; + App::EditorSelectionState selection = {}; + App::EditorUtilityWindowKind utilityWindowKind = + App::EditorUtilityWindowKind::None; + + static EditorCommand ClearStatus(); + static EditorCommand SetStatus(std::string channel, std::string message); + static EditorCommand SetReadyStatus(); + static EditorCommand AppendConsoleEntry(std::string channel, std::string message); + static EditorCommand SetSelection(App::EditorSelectionState selection); + static EditorCommand RequestOpenUtilityWindow(App::EditorUtilityWindowKind kind); + static EditorCommand ConsumePendingUtilityWindowRequest(); +}; + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/app/Core/Commands/EditorHostCommandBridge.cpp b/editor/src/Product/Commands/EditorHostCommandBridge.cpp similarity index 99% rename from editor/app/Core/Commands/EditorHostCommandBridge.cpp rename to editor/src/Product/Commands/EditorHostCommandBridge.cpp index d1cbcc92..cc817d55 100644 --- a/editor/app/Core/Commands/EditorHostCommandBridge.cpp +++ b/editor/src/Product/Commands/EditorHostCommandBridge.cpp @@ -1,4 +1,4 @@ -#include "Commands/EditorHostCommandBridge.h" +#include "Product/Commands/EditorHostCommandBridge.h" #include #include diff --git a/editor/app/Core/Commands/EditorHostCommandBridge.h b/editor/src/Product/Commands/EditorHostCommandBridge.h similarity index 97% rename from editor/app/Core/Commands/EditorHostCommandBridge.h rename to editor/src/Product/Commands/EditorHostCommandBridge.h index f19edbfd..63fbed17 100644 --- a/editor/app/Core/Commands/EditorHostCommandBridge.h +++ b/editor/src/Product/Commands/EditorHostCommandBridge.h @@ -1,7 +1,7 @@ #pragma once -#include "Commands/EditorEditCommandRoute.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" +#include "Product/State/EditorCommandFocusService.h" #include diff --git a/editor/app/Core/Assets/EditorIconService.h b/editor/src/Product/Core/Assets/EditorIconService.h similarity index 100% rename from editor/app/Core/Assets/EditorIconService.h rename to editor/src/Product/Core/Assets/EditorIconService.h diff --git a/editor/app/Core/Commands/EditorEditCommandRoute.h b/editor/src/Product/Core/Commands/EditorEditCommandRoute.h similarity index 100% rename from editor/app/Core/Commands/EditorEditCommandRoute.h rename to editor/src/Product/Core/Commands/EditorEditCommandRoute.h diff --git a/editor/app/Core/Engine/EditorEngineLifecycle.h b/editor/src/Product/Core/Engine/EditorEngineLifecycle.h similarity index 100% rename from editor/app/Core/Engine/EditorEngineLifecycle.h rename to editor/src/Product/Core/Engine/EditorEngineLifecycle.h diff --git a/editor/app/Core/Engine/EditorSceneBackendFactory.h b/editor/src/Product/Core/Engine/EditorSceneBackendFactory.h similarity index 84% rename from editor/app/Core/Engine/EditorSceneBackendFactory.h rename to editor/src/Product/Core/Engine/EditorSceneBackendFactory.h index 5e3ccec5..40d2e862 100644 --- a/editor/app/Core/Engine/EditorSceneBackendFactory.h +++ b/editor/src/Product/Core/Engine/EditorSceneBackendFactory.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include diff --git a/editor/app/Core/Engine/EditorShaderProvider.h b/editor/src/Product/Core/Engine/EditorShaderProvider.h similarity index 100% rename from editor/app/Core/Engine/EditorShaderProvider.h rename to editor/src/Product/Core/Engine/EditorShaderProvider.h diff --git a/editor/app/Core/Engine/GameViewportEngineBridge.h b/editor/src/Product/Core/Engine/GameViewportEngineBridge.h similarity index 100% rename from editor/app/Core/Engine/GameViewportEngineBridge.h rename to editor/src/Product/Core/Engine/GameViewportEngineBridge.h diff --git a/editor/app/Core/Engine/SceneViewportEngineBridge.h b/editor/src/Product/Core/Engine/SceneViewportEngineBridge.h similarity index 95% rename from editor/app/Core/Engine/SceneViewportEngineBridge.h rename to editor/src/Product/Core/Engine/SceneViewportEngineBridge.h index 2c8ca1a1..f1757381 100644 --- a/editor/app/Core/Engine/SceneViewportEngineBridge.h +++ b/editor/src/Product/Core/Engine/SceneViewportEngineBridge.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/SceneViewportRenderRequest.h" +#include "Product/Core/Scene/SceneViewportRenderRequest.h" #include #include diff --git a/editor/app/Core/Environment/EditorRuntimePaths.h b/editor/src/Product/Core/Environment/EditorRuntimePaths.h similarity index 100% rename from editor/app/Core/Environment/EditorRuntimePaths.h rename to editor/src/Product/Core/Environment/EditorRuntimePaths.h diff --git a/editor/app/Core/Scene/EditorSceneBackend.h b/editor/src/Product/Core/Scene/EditorSceneBackend.h similarity index 100% rename from editor/app/Core/Scene/EditorSceneBackend.h rename to editor/src/Product/Core/Scene/EditorSceneBackend.h diff --git a/editor/app/Core/Scene/SceneViewportRenderRequest.h b/editor/src/Product/Core/Scene/SceneViewportRenderRequest.h similarity index 90% rename from editor/app/Core/Scene/SceneViewportRenderRequest.h rename to editor/src/Product/Core/Scene/SceneViewportRenderRequest.h index 55b76563..aa225ac3 100644 --- a/editor/app/Core/Scene/SceneViewportRenderRequest.h +++ b/editor/src/Product/Core/Scene/SceneViewportRenderRequest.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include #include diff --git a/editor/app/Core/Viewport/EditorViewportPicking.h b/editor/src/Product/Core/Viewport/EditorViewportPicking.h similarity index 100% rename from editor/app/Core/Viewport/EditorViewportPicking.h rename to editor/src/Product/Core/Viewport/EditorViewportPicking.h diff --git a/editor/app/Core/Viewport/EditorViewportRuntimeServices.h b/editor/src/Product/Core/Viewport/EditorViewportRuntimeServices.h similarity index 88% rename from editor/app/Core/Viewport/EditorViewportRuntimeServices.h rename to editor/src/Product/Core/Viewport/EditorViewportRuntimeServices.h index 9c4fcf56..8baff378 100644 --- a/editor/app/Core/Viewport/EditorViewportRuntimeServices.h +++ b/editor/src/Product/Core/Viewport/EditorViewportRuntimeServices.h @@ -1,9 +1,9 @@ #pragma once -#include "Scene/SceneViewportRenderRequest.h" -#include "Environment/EditorRuntimePaths.h" -#include "Viewport/EditorViewportPicking.h" -#include "Viewport/EditorViewportTypes.h" +#include "Product/Core/Scene/SceneViewportRenderRequest.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Viewport/EditorViewportPicking.h" +#include "Product/Core/Viewport/EditorViewportTypes.h" #include namespace XCEngine::Rendering { diff --git a/editor/app/Core/Viewport/EditorViewportTypes.h b/editor/src/Product/Core/Viewport/EditorViewportTypes.h similarity index 100% rename from editor/app/Core/Viewport/EditorViewportTypes.h rename to editor/src/Product/Core/Viewport/EditorViewportTypes.h diff --git a/editor/app/Core/Windowing/EditorShellVariant.h b/editor/src/Product/Core/Windowing/EditorShellVariant.h similarity index 100% rename from editor/app/Core/Windowing/EditorShellVariant.h rename to editor/src/Product/Core/Windowing/EditorShellVariant.h diff --git a/editor/app/Core/Windowing/EditorWindowGeometry.h b/editor/src/Product/Core/Windowing/EditorWindowGeometry.h similarity index 100% rename from editor/app/Core/Windowing/EditorWindowGeometry.h rename to editor/src/Product/Core/Windowing/EditorWindowGeometry.h diff --git a/editor/app/Core/Windowing/EditorWindowMetrics.h b/editor/src/Product/Core/Windowing/EditorWindowMetrics.h similarity index 100% rename from editor/app/Core/Windowing/EditorWindowMetrics.h rename to editor/src/Product/Core/Windowing/EditorWindowMetrics.h diff --git a/editor/app/Core/Windowing/EditorWindowTransferRequests.h b/editor/src/Product/Core/Windowing/EditorWindowTransferRequests.h similarity index 92% rename from editor/app/Core/Windowing/EditorWindowTransferRequests.h rename to editor/src/Product/Core/Windowing/EditorWindowTransferRequests.h index 4bed0e49..04ae45ef 100644 --- a/editor/app/Core/Windowing/EditorWindowTransferRequests.h +++ b/editor/src/Product/Core/Windowing/EditorWindowTransferRequests.h @@ -4,8 +4,8 @@ #define NOMINMAX #endif -#include "EditorWindowGeometry.h" -#include "UtilityWindows/EditorUtilityWindowRuntime.h" +#include "Product/Core/Windowing/EditorWindowGeometry.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" #include #include diff --git a/editor/app/Core/Windowing/EditorWindowTypes.h b/editor/src/Product/Core/Windowing/EditorWindowTypes.h similarity index 100% rename from editor/app/Core/Windowing/EditorWindowTypes.h rename to editor/src/Product/Core/Windowing/EditorWindowTypes.h diff --git a/editor/src/Product/Effects/EditorEffect.cpp b/editor/src/Product/Effects/EditorEffect.cpp new file mode 100644 index 00000000..656ac1f9 --- /dev/null +++ b/editor/src/Product/Effects/EditorEffect.cpp @@ -0,0 +1,17 @@ +#include "Product/Effects/EditorEffect.h" + +namespace XCEngine::UI::Editor::Product { + +std::string_view GetEditorEffectKindName(EditorEffectKind kind) { + switch (kind) { + case EditorEffectKind::UtilityWindowRequestQueued: + return "UtilityWindowRequestQueued"; + case EditorEffectKind::UtilityWindowRequestConsumed: + return "UtilityWindowRequestConsumed"; + case EditorEffectKind::None: + default: + return "None"; + } +} + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/src/Product/Effects/EditorEffect.h b/editor/src/Product/Effects/EditorEffect.h new file mode 100644 index 00000000..7dfcd707 --- /dev/null +++ b/editor/src/Product/Effects/EditorEffect.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" + +#include +#include + +namespace XCEngine::UI::Editor::Product { + +enum class EditorEffectKind : std::uint8_t { + None = 0, + UtilityWindowRequestQueued, + UtilityWindowRequestConsumed, +}; + +std::string_view GetEditorEffectKindName(EditorEffectKind kind); + +struct EditorEffect { + EditorEffectKind kind = EditorEffectKind::None; + App::EditorUtilityWindowKind utilityWindowKind = + App::EditorUtilityWindowKind::None; +}; + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/app/Features/Inspector/AddComponentPanel.cpp b/editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.cpp similarity index 96% rename from editor/app/Features/Inspector/AddComponentPanel.cpp rename to editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.cpp index 8d0df899..8db8f3d5 100644 --- a/editor/app/Features/Inspector/AddComponentPanel.cpp +++ b/editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.cpp @@ -1,8 +1,8 @@ -#include "Inspector/AddComponentPanel.h" +#include "Product/Features/Utility/AddComponent/AddComponentPanel.h" -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorRegistry.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" #include diff --git a/editor/app/Features/Inspector/AddComponentPanel.h b/editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.h similarity index 88% rename from editor/app/Features/Inspector/AddComponentPanel.h rename to editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.h index 2816faae..020849cf 100644 --- a/editor/app/Features/Inspector/AddComponentPanel.h +++ b/editor/src/Product/Features/Utility/AddComponent/AddComponentPanel.h @@ -1,7 +1,7 @@ #pragma once -#include "Scene/EditorSceneBackend.h" -#include "UtilityWindows/EditorUtilityWindowRuntime.h" +#include "Product/Core/Scene/EditorSceneBackend.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" #include #include @@ -11,7 +11,7 @@ namespace XCEngine::UI::Editor::App { class EditorSceneRuntime; -class AddComponentPanel final : public EditorUtilityWindowPanel { +class AddComponentPanel final : public EditorUtilityWindowContent { public: explicit AddComponentPanel(EditorSceneRuntime& sceneRuntime) : m_sceneRuntime(sceneRuntime) {} diff --git a/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.cpp b/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.cpp new file mode 100644 index 00000000..8effd60b --- /dev/null +++ b/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.cpp @@ -0,0 +1,47 @@ +#include "Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h" + +#include "Product/Features/Utility/AddComponent/AddComponentPanel.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +namespace XCEngine::UI::Editor::App { + +namespace { + +using ::XCEngine::UI::UISize; + +constexpr EditorWindowChromePolicy kUtilityWindowChromePolicy = { + .allowDetachedTitleBarTabStrip = false, + .showFrameStats = false, + .showTopmostButton = true, + .topmostByDefault = true, +}; + +constexpr EditorWindowNativeHostPolicy kUtilityWindowNativeHostPolicy = { + .shellRole = EditorWindowNativeShellRole::ToolWindow, +}; + +std::unique_ptr CreateAddComponentUtilityContent( + const EditorUtilityFeatureDescriptor&, + const EditorUtilityFeatureContext& context) { + return std::make_unique(context.sceneRuntime); +} + +} // namespace + +EditorUtilityFeatureDescriptor GetAddComponentUtilityFeature() { + return EditorUtilityFeatureDescriptor{ + .window = EditorUtilityWindowDescriptor{ + .kind = EditorUtilityWindowKind::AddComponent, + .windowId = "utility.add-component", + .title = L"Add Component", + .reusePolicy = EditorUtilityWindowReusePolicy::SingleInstance, + .chromePolicy = kUtilityWindowChromePolicy, + .nativeHostPolicy = kUtilityWindowNativeHostPolicy, + .preferredOuterSize = UISize(352.0f, 500.0f), + .minimumOuterSize = UISize(320.0f, 460.0f), + }, + .createContent = &CreateAddComponentUtilityContent, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h b/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h new file mode 100644 index 00000000..16683b15 --- /dev/null +++ b/editor/src/Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +namespace XCEngine::UI::Editor::App { + +EditorUtilityFeatureDescriptor GetAddComponentUtilityFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/ColorPicker/ColorPickerPanel.cpp b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.cpp similarity index 98% rename from editor/app/Features/ColorPicker/ColorPickerPanel.cpp rename to editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.cpp index fd650126..3a135cf9 100644 --- a/editor/app/Features/ColorPicker/ColorPickerPanel.cpp +++ b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.cpp @@ -1,6 +1,6 @@ -#include "ColorPickerPanel.h" +#include "Product/Features/Utility/ColorPicker/ColorPickerPanel.h" -#include "State/EditorColorPickerToolState.h" +#include "Product/State/EditorColorPickerToolState.h" #include #include diff --git a/editor/app/Features/ColorPicker/ColorPickerPanel.h b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.h similarity index 89% rename from editor/app/Features/ColorPicker/ColorPickerPanel.h rename to editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.h index 26502d08..14ef1a12 100644 --- a/editor/app/Features/ColorPicker/ColorPickerPanel.h +++ b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerPanel.h @@ -1,6 +1,6 @@ #pragma once -#include "UtilityWindows/EditorUtilityWindowRuntime.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" #include @@ -10,7 +10,7 @@ namespace XCEngine::UI::Editor::App { struct EditorColorPickerToolState; -class ColorPickerPanel final : public EditorUtilityWindowPanel { +class ColorPickerPanel final : public EditorUtilityWindowContent { public: explicit ColorPickerPanel(EditorColorPickerToolState& toolState) : m_toolState(toolState) {} diff --git a/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.cpp b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.cpp new file mode 100644 index 00000000..a9efd90c --- /dev/null +++ b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.cpp @@ -0,0 +1,47 @@ +#include "Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h" + +#include "Product/Features/Utility/ColorPicker/ColorPickerPanel.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +namespace XCEngine::UI::Editor::App { + +namespace { + +using ::XCEngine::UI::UISize; + +constexpr EditorWindowChromePolicy kUtilityWindowChromePolicy = { + .allowDetachedTitleBarTabStrip = false, + .showFrameStats = false, + .showTopmostButton = true, + .topmostByDefault = true, +}; + +constexpr EditorWindowNativeHostPolicy kUtilityWindowNativeHostPolicy = { + .shellRole = EditorWindowNativeShellRole::ToolWindow, +}; + +std::unique_ptr CreateColorPickerUtilityContent( + const EditorUtilityFeatureDescriptor&, + const EditorUtilityFeatureContext& context) { + return std::make_unique(context.colorPickerToolState); +} + +} // namespace + +EditorUtilityFeatureDescriptor GetColorPickerUtilityFeature() { + return EditorUtilityFeatureDescriptor{ + .window = EditorUtilityWindowDescriptor{ + .kind = EditorUtilityWindowKind::ColorPicker, + .windowId = "utility.color-picker", + .title = L"Color Picker", + .reusePolicy = EditorUtilityWindowReusePolicy::SingleInstance, + .chromePolicy = kUtilityWindowChromePolicy, + .nativeHostPolicy = kUtilityWindowNativeHostPolicy, + .preferredOuterSize = UISize(400.0f, 600.0f), + .minimumOuterSize = UISize(360.0f, 560.0f), + }, + .createContent = &CreateColorPickerUtilityContent, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h new file mode 100644 index 00000000..ebf633c4 --- /dev/null +++ b/editor/src/Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +namespace XCEngine::UI::Editor::App { + +EditorUtilityFeatureDescriptor GetColorPickerUtilityFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Console/ConsolePanel.cpp b/editor/src/Product/Features/Workspace/Console/ConsolePanel.cpp similarity index 97% rename from editor/app/Features/Console/ConsolePanel.cpp rename to editor/src/Product/Features/Workspace/Console/ConsolePanel.cpp index 0eb629db..51cc853e 100644 --- a/editor/app/Features/Console/ConsolePanel.cpp +++ b/editor/src/Product/Features/Workspace/Console/ConsolePanel.cpp @@ -1,4 +1,4 @@ -#include "ConsolePanel.h" +#include "Product/Features/Workspace/Console/ConsolePanel.h" #include diff --git a/editor/app/Features/Console/ConsolePanel.h b/editor/src/Product/Features/Workspace/Console/ConsolePanel.h similarity index 93% rename from editor/app/Features/Console/ConsolePanel.h rename to editor/src/Product/Features/Workspace/Console/ConsolePanel.h index bde4a682..5062fbf3 100644 --- a/editor/app/Features/Console/ConsolePanel.h +++ b/editor/src/Product/Features/Workspace/Console/ConsolePanel.h @@ -1,6 +1,6 @@ #pragma once -#include "State/EditorSession.h" +#include "Product/State/EditorSession.h" #include diff --git a/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.cpp new file mode 100644 index 00000000..d43d4b12 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.cpp @@ -0,0 +1,89 @@ +#include "Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h" + +#include "Product/Features/Workspace/Console/ConsolePanel.h" +#include "Product/Features/Workspace/WorkspaceFeatureHelpers.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kConsoleUpdatePriority = 40; + +class ConsoleWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + ConsoleWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const std::vector& entries) + : m_panelId(feature.panelId) + , m_actionRoute(feature.actionRoute) + , m_entries(entries) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Console"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + EditorWorkspaceHostedContentUpdatePhase GetUpdatePhase() const override { + return EditorWorkspaceHostedContentUpdatePhase::AfterCommandFocusSync; + } + + int GetUpdatePriority() const override { + return kConsoleUpdatePriority; + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_panel.Update( + m_entries, + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId())); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + +private: + std::string_view m_panelId = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + const std::vector& m_entries; + ConsolePanel m_panel = {}; +}; + +std::unique_ptr CreateConsoleWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.consoleEntries); +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetConsoleWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kConsoleWorkspaceFeaturePanelId, + .defaultTitle = "Console", + .presentationKind = UIEditorPanelPresentationKind::HostedContent, + .placeholder = true, + .canHide = false, + .canClose = false, + .showViewportTopBar = false, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Console, + .viewportKind = EditorProductViewportKind::None, + .viewportPlaceholderStatus = {}, + .createContent = &CreateConsoleWorkspaceContent, + .customizePresentation = nullptr, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h new file mode 100644 index 00000000..cf8d15fd --- /dev/null +++ b/editor/src/Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kConsoleWorkspaceFeaturePanelId = "console"; + +EditorWorkspaceFeatureDescriptor GetConsoleWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Game/GameViewportController.cpp b/editor/src/Product/Features/Workspace/Game/GameViewportController.cpp similarity index 79% rename from editor/app/Features/Game/GameViewportController.cpp rename to editor/src/Product/Features/Workspace/Game/GameViewportController.cpp index 6ea19ea3..1c6c1631 100644 --- a/editor/app/Features/Game/GameViewportController.cpp +++ b/editor/src/Product/Features/Workspace/Game/GameViewportController.cpp @@ -1,7 +1,7 @@ -#include "Game/GameViewportController.h" +#include "Product/Features/Workspace/Game/GameViewportController.h" -#include "Panels/EditorPanelIds.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Features/Workspace/Game/GameWorkspaceFeature.h" +#include "Product/State/EditorCommandFocusService.h" #include @@ -31,9 +31,13 @@ void GameViewportController::Update( const UIEditorWorkspaceComposeState& composeState, const UIEditorWorkspaceComposeFrame& composeFrame) { const UIEditorWorkspaceViewportComposeFrame* viewportFrame = - FindUIEditorWorkspaceViewportPresentationFrame(composeFrame, kGamePanelId); + FindUIEditorWorkspaceViewportPresentationFrame( + composeFrame, + kGameWorkspaceFeaturePanelId); const UIEditorWorkspacePanelPresentationState* panelState = - FindUIEditorWorkspacePanelPresentationState(composeState, kGamePanelId); + FindUIEditorWorkspacePanelPresentationState( + composeState, + kGameWorkspaceFeaturePanelId); if (viewportFrame == nullptr || panelState == nullptr) { return; } diff --git a/editor/app/Features/Game/GameViewportController.h b/editor/src/Product/Features/Workspace/Game/GameViewportController.h similarity index 100% rename from editor/app/Features/Game/GameViewportController.h rename to editor/src/Product/Features/Workspace/Game/GameViewportController.h diff --git a/editor/app/Features/Game/GameViewportFeature.cpp b/editor/src/Product/Features/Workspace/Game/GameViewportFeature.cpp similarity index 92% rename from editor/app/Features/Game/GameViewportFeature.cpp rename to editor/src/Product/Features/Workspace/Game/GameViewportFeature.cpp index 25fa9c2d..b412d8ad 100644 --- a/editor/app/Features/Game/GameViewportFeature.cpp +++ b/editor/src/Product/Features/Workspace/Game/GameViewportFeature.cpp @@ -1,4 +1,4 @@ -#include "Game/GameViewportFeature.h" +#include "Product/Features/Workspace/Game/GameViewportFeature.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Game/GameViewportFeature.h b/editor/src/Product/Features/Workspace/Game/GameViewportFeature.h similarity index 90% rename from editor/app/Features/Game/GameViewportFeature.h rename to editor/src/Product/Features/Workspace/Game/GameViewportFeature.h index b237e92d..705759bf 100644 --- a/editor/app/Features/Game/GameViewportFeature.h +++ b/editor/src/Product/Features/Workspace/Game/GameViewportFeature.h @@ -1,6 +1,6 @@ #pragma once -#include "Game/GameViewportController.h" +#include "Product/Features/Workspace/Game/GameViewportController.h" #include diff --git a/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.cpp new file mode 100644 index 00000000..d0bdb231 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.cpp @@ -0,0 +1,95 @@ +#include "Product/Features/Workspace/Game/GameWorkspaceFeature.h" + +#include "Product/Features/Workspace/Game/GameViewportFeature.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kGameUpdatePriority = 5; + +class GameWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + GameWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + EditorCommandFocusService& commandFocusService) + : m_panelId(feature.panelId) + , m_actionRoute(feature.actionRoute) + , m_commandFocusService(commandFocusService) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.GameOverlay"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + int GetUpdatePriority() const override { + return kGameUpdatePriority; + } + + void Shutdown(const EditorWorkspaceHostedContentShutdownContext& context) override { + (void)context; + m_feature.Shutdown(); + } + + void ResetInteractionState() override { + m_feature.ResetInteractionState(); + } + + void PrepareForShellDefinition(UIEditorWorkspaceController&) override { + m_feature.SetCommandFocusService(&m_commandFocusService); + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_feature.Update( + context.shellInteractionState.workspaceInteractionState.composeState, + context.shellFrame.workspaceInteractionFrame.composeFrame); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_feature.Append(drawList); + } + +private: + std::string_view m_panelId = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + EditorCommandFocusService& m_commandFocusService; + GameViewportFeature m_feature = {}; +}; + +std::unique_ptr CreateGameWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.commandFocusService); +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetGameWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kGameWorkspaceFeaturePanelId, + .defaultTitle = "Game", + .presentationKind = UIEditorPanelPresentationKind::ViewportShell, + .placeholder = false, + .canHide = false, + .canClose = false, + .showViewportTopBar = false, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Game, + .viewportKind = EditorProductViewportKind::Game, + .viewportPlaceholderStatus = {}, + .createContent = &CreateGameWorkspaceContent, + .customizePresentation = nullptr, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.h new file mode 100644 index 00000000..6daa8d9c --- /dev/null +++ b/editor/src/Product/Features/Workspace/Game/GameWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kGameWorkspaceFeaturePanelId = "game"; + +EditorWorkspaceFeatureDescriptor GetGameWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Hierarchy/HierarchyModel.cpp b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.cpp similarity index 98% rename from editor/app/Features/Hierarchy/HierarchyModel.cpp rename to editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.cpp index 6e9f04d2..75b574a0 100644 --- a/editor/app/Features/Hierarchy/HierarchyModel.cpp +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.cpp @@ -1,6 +1,6 @@ -#include "HierarchyModel.h" +#include "Product/Features/Workspace/Hierarchy/HierarchyModel.h" -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include #include diff --git a/editor/app/Features/Hierarchy/HierarchyModel.h b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.h similarity index 97% rename from editor/app/Features/Hierarchy/HierarchyModel.h rename to editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.h index 60874da0..f9683d60 100644 --- a/editor/app/Features/Hierarchy/HierarchyModel.h +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyModel.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include #include diff --git a/editor/app/Features/Hierarchy/HierarchyPanel.cpp b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.cpp similarity index 99% rename from editor/app/Features/Hierarchy/HierarchyPanel.cpp rename to editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.cpp index a7c91d5f..bf75a4d3 100644 --- a/editor/app/Features/Hierarchy/HierarchyPanel.cpp +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.cpp @@ -1,9 +1,9 @@ -#include "HierarchyPanel.h" -#include "Assets/EditorIconService.h" +#include "Product/Features/Workspace/Hierarchy/HierarchyPanel.h" +#include "Product/Core/Assets/EditorIconService.h" #include #include -#include "Scene/EditorSceneRuntime.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/State/EditorCommandFocusService.h" #include #include #include diff --git a/editor/app/Features/Hierarchy/HierarchyPanel.h b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.h similarity index 97% rename from editor/app/Features/Hierarchy/HierarchyPanel.h rename to editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.h index 11ccb12b..911af04f 100644 --- a/editor/app/Features/Hierarchy/HierarchyPanel.h +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyPanel.h @@ -1,8 +1,8 @@ #pragma once -#include "HierarchyModel.h" +#include "Product/Features/Workspace/Hierarchy/HierarchyModel.h" -#include "Commands/EditorEditCommandRoute.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" #include #include #include diff --git a/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.cpp new file mode 100644 index 00000000..6fd65a95 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.cpp @@ -0,0 +1,161 @@ +#include "Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h" + +#include "Product/Features/Workspace/Hierarchy/HierarchyPanel.h" +#include "Product/Features/Workspace/WorkspaceFeatureHelpers.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kHierarchyUpdatePriority = 10; + +std::string DescribeHierarchyPanelEvent(const HierarchyPanel::Event& event) { + std::ostringstream stream = {}; + switch (event.kind) { + case HierarchyPanel::EventKind::SelectionChanged: + stream << "SelectionChanged"; + break; + case HierarchyPanel::EventKind::Reparented: + stream << "Reparented"; + break; + case HierarchyPanel::EventKind::MovedToRoot: + stream << "MovedToRoot"; + break; + case HierarchyPanel::EventKind::RenameRequested: + stream << "RenameRequested"; + break; + case HierarchyPanel::EventKind::None: + default: + stream << "None"; + break; + } + + if (!event.itemId.empty()) { + stream << " item=" << event.itemId; + } + if (!event.targetItemId.empty()) { + stream << " target=" << event.targetItemId; + } + if (!event.label.empty()) { + stream << " label=" << event.label; + } + return stream.str(); +} + +class HierarchyWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + HierarchyWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + EditorSceneRuntime& sceneRuntime, + EditorCommandFocusService& commandFocusService) + : m_panelId(feature.panelId) + , m_panelTitle(feature.defaultTitle) + , m_actionRoute(feature.actionRoute) + , m_sceneRuntime(sceneRuntime) + , m_commandFocusService(commandFocusService) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Hierarchy"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + int GetUpdatePriority() const override { + return kHierarchyUpdatePriority; + } + + void Initialize(const EditorWorkspaceHostedContentInitializationContext& context) override { + m_panel.SetIconService(&context.iconService); + m_panel.SetTextMeasurer(&context.textMeasurer); + m_panel.Initialize(); + } + + void ResetInteractionState() override { + m_panel.ResetInteractionState(); + } + + void PrepareForShellDefinition(UIEditorWorkspaceController&) override { + m_panel.SetSceneRuntime(&m_sceneRuntime); + m_panel.SetCommandFocusService(&m_commandFocusService); + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_panel.Update( + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + bool HasActivePointerCapture() const override { + return m_panel.HasActivePointerCapture(); + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + + std::vector CollectFrameEvents() const override { + std::vector events = {}; + for (const HierarchyPanel::Event& event : m_panel.GetFrameEvents()) { + events.push_back(EditorWorkspaceHostedContentFrameEvent{ + .status = std::string(m_panelTitle), + .traceChannel = std::string(m_panelId), + .message = DescribeHierarchyPanelEvent(event), + }); + } + return events; + } + +private: + std::string_view m_panelId = {}; + std::string_view m_panelTitle = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + EditorSceneRuntime& m_sceneRuntime; + EditorCommandFocusService& m_commandFocusService; + HierarchyPanel m_panel = {}; +}; + +std::unique_ptr CreateHierarchyWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.sceneRuntime, + context.commandFocusService); +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetHierarchyWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kHierarchyWorkspaceFeaturePanelId, + .defaultTitle = "Hierarchy", + .presentationKind = UIEditorPanelPresentationKind::HostedContent, + .placeholder = true, + .canHide = false, + .canClose = false, + .showViewportTopBar = false, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Hierarchy, + .viewportKind = EditorProductViewportKind::None, + .viewportPlaceholderStatus = {}, + .createContent = &CreateHierarchyWorkspaceContent, + .customizePresentation = nullptr, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h new file mode 100644 index 00000000..75b126cf --- /dev/null +++ b/editor/src/Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kHierarchyWorkspaceFeaturePanelId = "hierarchy"; + +EditorWorkspaceFeatureDescriptor GetHierarchyWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Inspector/Components/AudioListenerInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/AudioListenerInspectorComponentEditor.h similarity index 98% rename from editor/app/Features/Inspector/Components/AudioListenerInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/AudioListenerInspectorComponentEditor.h index 86045273..34bc383f 100644 --- a/editor/app/Features/Inspector/Components/AudioListenerInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/AudioListenerInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/AudioSourceInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/AudioSourceInspectorComponentEditor.h similarity index 99% rename from editor/app/Features/Inspector/Components/AudioSourceInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/AudioSourceInspectorComponentEditor.h index dc439752..c5ede79e 100644 --- a/editor/app/Features/Inspector/Components/AudioSourceInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/AudioSourceInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" #include diff --git a/editor/app/Features/Inspector/Components/BoxColliderInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/BoxColliderInspectorComponentEditor.h similarity index 95% rename from editor/app/Features/Inspector/Components/BoxColliderInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/BoxColliderInspectorComponentEditor.h index bc83ba0c..061713c0 100644 --- a/editor/app/Features/Inspector/Components/BoxColliderInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/BoxColliderInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/ColliderInspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/CameraInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/CameraInspectorComponentEditor.h similarity index 99% rename from editor/app/Features/Inspector/Components/CameraInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/CameraInspectorComponentEditor.h index f1a316d9..b83e2679 100644 --- a/editor/app/Features/Inspector/Components/CameraInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/CameraInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/CapsuleColliderInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/CapsuleColliderInspectorComponentEditor.h similarity index 97% rename from editor/app/Features/Inspector/Components/CapsuleColliderInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/CapsuleColliderInspectorComponentEditor.h index 073d9281..526cbf37 100644 --- a/editor/app/Features/Inspector/Components/CapsuleColliderInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/CapsuleColliderInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/ColliderInspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/ColliderInspectorComponentEditorUtils.h b/editor/src/Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h similarity index 97% rename from editor/app/Features/Inspector/Components/ColliderInspectorComponentEditorUtils.h rename to editor/src/Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h index 26015ea8..86a65c56 100644 --- a/editor/app/Features/Inspector/Components/ColliderInspectorComponentEditorUtils.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" #include diff --git a/editor/app/Features/Inspector/Components/IInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h similarity index 97% rename from editor/app/Features/Inspector/Components/IInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h index a86c4629..792f784f 100644 --- a/editor/app/Features/Inspector/Components/IInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneRuntime.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" #include diff --git a/editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.cpp b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.cpp similarity index 99% rename from editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.cpp rename to editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.cpp index 265ccd87..32297f05 100644 --- a/editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.cpp @@ -1,4 +1,4 @@ -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" #include diff --git a/editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h similarity index 95% rename from editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h index 4702c6b4..a1b5547b 100644 --- a/editor/app/Features/Inspector/Components/InspectorBindingComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h @@ -1,7 +1,7 @@ #pragma once -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorUtils.h" #include #include diff --git a/editor/app/Features/Inspector/Components/InspectorComponentEditorRegistry.cpp b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.cpp similarity index 59% rename from editor/app/Features/Inspector/Components/InspectorComponentEditorRegistry.cpp rename to editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.cpp index f50c38a2..063ed956 100644 --- a/editor/app/Features/Inspector/Components/InspectorComponentEditorRegistry.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.cpp @@ -1,19 +1,19 @@ -#include "Inspector/Components/InspectorComponentEditorRegistry.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h" -#include "Inspector/Components/AudioListenerInspectorComponentEditor.h" -#include "Inspector/Components/AudioSourceInspectorComponentEditor.h" -#include "Inspector/Components/BoxColliderInspectorComponentEditor.h" -#include "Inspector/Components/CameraInspectorComponentEditor.h" -#include "Inspector/Components/CapsuleColliderInspectorComponentEditor.h" -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/LightInspectorComponentEditor.h" -#include "Inspector/Components/MeshFilterInspectorComponentEditor.h" -#include "Inspector/Components/MeshRendererInspectorComponentEditor.h" -#include "Inspector/Components/RigidbodyInspectorComponentEditor.h" -#include "Inspector/Components/ScriptComponentInspectorComponentEditor.h" -#include "Inspector/Components/SphereColliderInspectorComponentEditor.h" -#include "Inspector/Components/TransformInspectorComponentEditor.h" -#include "Inspector/Components/VolumeRendererInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/AudioListenerInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/AudioSourceInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/BoxColliderInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/CameraInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/CapsuleColliderInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/LightInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/MeshFilterInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/MeshRendererInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/RigidbodyInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/SphereColliderInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/VolumeRendererInspectorComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/InspectorComponentEditorRegistry.h b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h similarity index 100% rename from editor/app/Features/Inspector/Components/InspectorComponentEditorRegistry.h rename to editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h diff --git a/editor/app/Features/Inspector/Components/InspectorComponentEditorUtils.h b/editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorUtils.h similarity index 100% rename from editor/app/Features/Inspector/Components/InspectorComponentEditorUtils.h rename to editor/src/Product/Features/Workspace/Inspector/Components/InspectorComponentEditorUtils.h diff --git a/editor/app/Features/Inspector/Components/LightInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/LightInspectorComponentEditor.h similarity index 99% rename from editor/app/Features/Inspector/Components/LightInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/LightInspectorComponentEditor.h index b9ba6534..dbbb3a0b 100644 --- a/editor/app/Features/Inspector/Components/LightInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/LightInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/MeshFilterInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/MeshFilterInspectorComponentEditor.h similarity index 95% rename from editor/app/Features/Inspector/Components/MeshFilterInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/MeshFilterInspectorComponentEditor.h index 6e07464f..7ce52762 100644 --- a/editor/app/Features/Inspector/Components/MeshFilterInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/MeshFilterInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/MeshRendererInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/MeshRendererInspectorComponentEditor.h similarity index 98% rename from editor/app/Features/Inspector/Components/MeshRendererInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/MeshRendererInspectorComponentEditor.h index 6b90c257..95d94881 100644 --- a/editor/app/Features/Inspector/Components/MeshRendererInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/MeshRendererInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" #include #include diff --git a/editor/app/Features/Inspector/Components/RigidbodyInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/RigidbodyInspectorComponentEditor.h similarity index 98% rename from editor/app/Features/Inspector/Components/RigidbodyInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/RigidbodyInspectorComponentEditor.h index 172f386b..7769f16f 100644 --- a/editor/app/Features/Inspector/Components/RigidbodyInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/RigidbodyInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp b/editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp similarity index 99% rename from editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp rename to editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp index 31dfe53c..f033e7b8 100644 --- a/editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.cpp @@ -1,6 +1,6 @@ -#include "Inspector/Components/ScriptComponentInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorUtils.h" #include #include diff --git a/editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.h similarity index 94% rename from editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.h index 607cabc9..f2be0256 100644 --- a/editor/app/Features/Inspector/Components/ScriptComponentInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/ScriptComponentInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/SphereColliderInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/SphereColliderInspectorComponentEditor.h similarity index 95% rename from editor/app/Features/Inspector/Components/SphereColliderInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/SphereColliderInspectorComponentEditor.h index 5ff5c404..f4816791 100644 --- a/editor/app/Features/Inspector/Components/SphereColliderInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/SphereColliderInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/ColliderInspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/ColliderInspectorComponentEditorUtils.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.cpp b/editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.cpp similarity index 96% rename from editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.cpp rename to editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.cpp index 03ceaafc..df4ab559 100644 --- a/editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.cpp @@ -1,4 +1,4 @@ -#include "Inspector/Components/TransformInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.h similarity index 89% rename from editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.h index 1bf04605..05289747 100644 --- a/editor/app/Features/Inspector/Components/TransformInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/TransformInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/Components/VolumeRendererInspectorComponentEditor.h b/editor/src/Product/Features/Workspace/Inspector/Components/VolumeRendererInspectorComponentEditor.h similarity index 97% rename from editor/app/Features/Inspector/Components/VolumeRendererInspectorComponentEditor.h rename to editor/src/Product/Features/Workspace/Inspector/Components/VolumeRendererInspectorComponentEditor.h index 606c7e4e..4ddf4633 100644 --- a/editor/app/Features/Inspector/Components/VolumeRendererInspectorComponentEditor.h +++ b/editor/src/Product/Features/Workspace/Inspector/Components/VolumeRendererInspectorComponentEditor.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/Components/InspectorBindingComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorBindingComponentEditor.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/InspectorFieldValueApplier.h b/editor/src/Product/Features/Workspace/Inspector/InspectorFieldValueApplier.h similarity index 78% rename from editor/app/Features/Inspector/InspectorFieldValueApplier.h rename to editor/src/Product/Features/Workspace/Inspector/InspectorFieldValueApplier.h index 95841523..e931be5d 100644 --- a/editor/app/Features/Inspector/InspectorFieldValueApplier.h +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorFieldValueApplier.h @@ -1,10 +1,10 @@ #pragma once -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorRegistry.h" -#include "Inspector/InspectorPresentationModel.h" -#include "Inspector/InspectorSubject.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h" +#include "Product/Features/Workspace/Inspector/InspectorPresentationModel.h" +#include "Product/Features/Workspace/Inspector/InspectorSubject.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/InspectorPanel.cpp b/editor/src/Product/Features/Workspace/Inspector/InspectorPanel.cpp similarity index 98% rename from editor/app/Features/Inspector/InspectorPanel.cpp rename to editor/src/Product/Features/Workspace/Inspector/InspectorPanel.cpp index 80119c0c..2b1885f9 100644 --- a/editor/app/Features/Inspector/InspectorPanel.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorPanel.cpp @@ -1,18 +1,18 @@ -#include "InspectorPanel.h" +#include "Product/Features/Workspace/Inspector/InspectorPanel.h" -#include "UtilityWindows/EditorUtilityWindowRuntime.h" -#include "State/EditorColorPickerToolState.h" -#include "Inspector/InspectorFieldValueApplier.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" +#include "Product/State/EditorColorPickerToolState.h" +#include "Product/Features/Workspace/Inspector/InspectorFieldValueApplier.h" #include #include #include #include -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorRegistry.h" -#include "Scene/EditorSceneRuntime.h" -#include "Project/EditorProjectRuntime.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/Services/Project/EditorProjectRuntime.h" +#include "Product/State/EditorCommandFocusService.h" #include #include diff --git a/editor/app/Features/Inspector/InspectorPanel.h b/editor/src/Product/Features/Workspace/Inspector/InspectorPanel.h similarity index 96% rename from editor/app/Features/Inspector/InspectorPanel.h rename to editor/src/Product/Features/Workspace/Inspector/InspectorPanel.h index 7484f17e..4b8827ca 100644 --- a/editor/app/Features/Inspector/InspectorPanel.h +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorPanel.h @@ -1,9 +1,9 @@ #pragma once -#include "Inspector/InspectorPresentationModel.h" -#include "Inspector/InspectorSubject.h" +#include "Product/Features/Workspace/Inspector/InspectorPresentationModel.h" +#include "Product/Features/Workspace/Inspector/InspectorSubject.h" -#include "Commands/EditorEditCommandRoute.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" #include #include #include diff --git a/editor/app/Features/Inspector/InspectorPresentationModel.cpp b/editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.cpp similarity index 97% rename from editor/app/Features/Inspector/InspectorPresentationModel.cpp rename to editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.cpp index 9c0bb7ea..cff703dd 100644 --- a/editor/app/Features/Inspector/InspectorPresentationModel.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.cpp @@ -1,9 +1,9 @@ -#include "Inspector/InspectorPresentationModel.h" +#include "Product/Features/Workspace/Inspector/InspectorPresentationModel.h" -#include "Inspector/Components/IInspectorComponentEditor.h" -#include "Inspector/Components/InspectorComponentEditorUtils.h" -#include "Inspector/Components/InspectorComponentEditorRegistry.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Inspector/Components/IInspectorComponentEditor.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorUtils.h" +#include "Product/Features/Workspace/Inspector/Components/InspectorComponentEditorRegistry.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" #include #include diff --git a/editor/app/Features/Inspector/InspectorPresentationModel.h b/editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.h similarity index 95% rename from editor/app/Features/Inspector/InspectorPresentationModel.h rename to editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.h index 18ea0285..a047682e 100644 --- a/editor/app/Features/Inspector/InspectorPresentationModel.h +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorPresentationModel.h @@ -1,6 +1,6 @@ #pragma once -#include "Inspector/InspectorSubject.h" +#include "Product/Features/Workspace/Inspector/InspectorSubject.h" #include diff --git a/editor/app/Features/Inspector/InspectorSubject.cpp b/editor/src/Product/Features/Workspace/Inspector/InspectorSubject.cpp similarity index 89% rename from editor/app/Features/Inspector/InspectorSubject.cpp rename to editor/src/Product/Features/Workspace/Inspector/InspectorSubject.cpp index 46727c61..5883b466 100644 --- a/editor/app/Features/Inspector/InspectorSubject.cpp +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorSubject.cpp @@ -1,7 +1,7 @@ -#include "Inspector/InspectorSubject.h" +#include "Product/Features/Workspace/Inspector/InspectorSubject.h" -#include "Project/EditorProjectRuntime.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Services/Project/EditorProjectRuntime.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Inspector/InspectorSubject.h b/editor/src/Product/Features/Workspace/Inspector/InspectorSubject.h similarity index 92% rename from editor/app/Features/Inspector/InspectorSubject.h rename to editor/src/Product/Features/Workspace/Inspector/InspectorSubject.h index a9c2dde6..dccbf34b 100644 --- a/editor/app/Features/Inspector/InspectorSubject.h +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorSubject.h @@ -1,7 +1,7 @@ #pragma once -#include "Scene/EditorSceneBackend.h" -#include "State/EditorSession.h" +#include "Product/Core/Scene/EditorSceneBackend.h" +#include "Product/State/EditorSession.h" #include diff --git a/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.cpp new file mode 100644 index 00000000..62e210e2 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.cpp @@ -0,0 +1,129 @@ +#include "Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h" + +#include "Product/Features/Workspace/Inspector/InspectorPanel.h" +#include "Product/Features/Workspace/WorkspaceFeatureHelpers.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kInspectorUpdatePriority = 30; + +class InspectorWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + InspectorWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + EditorCommandFocusService& commandFocusService, + EditorProjectRuntime& projectRuntime, + EditorSceneRuntime& sceneRuntime, + EditorColorPickerToolState& colorPickerToolState, + RequestOpenUtilityWindowCallback requestOpenUtilityWindow) + : m_panelId(feature.panelId) + , m_actionRoute(feature.actionRoute) + , m_commandFocusService(commandFocusService) + , m_projectRuntime(projectRuntime) + , m_sceneRuntime(sceneRuntime) + , m_colorPickerToolState(colorPickerToolState) + , m_requestOpenUtilityWindow(std::move(requestOpenUtilityWindow)) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Inspector"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + int GetUpdatePriority() const override { + return kInspectorUpdatePriority; + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_panel.SetCommandFocusService(&m_commandFocusService); + InspectorPanelContext panelContext{ + .projectRuntime = m_projectRuntime, + .sceneRuntime = m_sceneRuntime, + .colorPickerToolState = m_colorPickerToolState, + .textMeasurer = m_textMeasurer, + .requestOpenUtilityWindow = m_requestOpenUtilityWindow, + }; + m_panel.Update( + panelContext, + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.AppendOverlay(drawList); + } + + std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const override { + return m_panel.CollectInteractiveOverlayBounds(); + } + + void Initialize(const EditorWorkspaceHostedContentInitializationContext& context) override { + m_textMeasurer = &context.textMeasurer; + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + +private: + std::string_view m_panelId = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + EditorCommandFocusService& m_commandFocusService; + EditorProjectRuntime& m_projectRuntime; + EditorSceneRuntime& m_sceneRuntime; + EditorColorPickerToolState& m_colorPickerToolState; + RequestOpenUtilityWindowCallback m_requestOpenUtilityWindow = {}; + const UIEditorTextMeasurer* m_textMeasurer = nullptr; + InspectorPanel m_panel = {}; +}; + +std::unique_ptr CreateInspectorWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.commandFocusService, + context.projectRuntime, + context.sceneRuntime, + context.colorPickerToolState, + context.requestOpenUtilityWindow); +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetInspectorWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kInspectorWorkspaceFeaturePanelId, + .defaultTitle = "Inspector", + .presentationKind = UIEditorPanelPresentationKind::HostedContent, + .placeholder = true, + .canHide = false, + .canClose = false, + .showViewportTopBar = false, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Inspector, + .viewportKind = EditorProductViewportKind::None, + .viewportPlaceholderStatus = {}, + .createContent = &CreateInspectorWorkspaceContent, + .customizePresentation = nullptr, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h new file mode 100644 index 00000000..61c586c0 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kInspectorWorkspaceFeaturePanelId = "inspector"; + +EditorWorkspaceFeatureDescriptor GetInspectorWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Project/ProjectPanel.cpp b/editor/src/Product/Features/Workspace/Project/ProjectPanel.cpp similarity index 99% rename from editor/app/Features/Project/ProjectPanel.cpp rename to editor/src/Product/Features/Workspace/Project/ProjectPanel.cpp index b9658aae..057c3b46 100644 --- a/editor/app/Features/Project/ProjectPanel.cpp +++ b/editor/src/Product/Features/Workspace/Project/ProjectPanel.cpp @@ -1,5 +1,5 @@ -#include "ProjectPanel.h" -#include "Assets/EditorIconService.h" +#include "Product/Features/Workspace/Project/ProjectPanel.h" +#include "Product/Core/Assets/EditorIconService.h" #include #include #include @@ -7,8 +7,8 @@ #include #include #include "SystemInteractionService.h" -#include "Project/EditorProjectRuntime.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Services/Project/EditorProjectRuntime.h" +#include "Product/State/EditorCommandFocusService.h" #include #include #include diff --git a/editor/app/Features/Project/ProjectPanel.h b/editor/src/Product/Features/Workspace/Project/ProjectPanel.h similarity index 98% rename from editor/app/Features/Project/ProjectPanel.h rename to editor/src/Product/Features/Workspace/Project/ProjectPanel.h index 78682d21..93bffbd1 100644 --- a/editor/app/Features/Project/ProjectPanel.h +++ b/editor/src/Product/Features/Workspace/Project/ProjectPanel.h @@ -1,9 +1,9 @@ #pragma once -#include "Project/EditorProjectRuntime.h" -#include "Project/ProjectBrowserModel.h" +#include "Product/Services/Project/EditorProjectRuntime.h" +#include "Product/Services/Project/ProjectBrowserModel.h" -#include "Commands/EditorEditCommandRoute.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" #include #include #include diff --git a/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.cpp new file mode 100644 index 00000000..deb90eec --- /dev/null +++ b/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.cpp @@ -0,0 +1,245 @@ +#include "Product/Features/Workspace/Project/ProjectWorkspaceFeature.h" + +#include "Product/Features/Workspace/Project/ProjectPanel.h" +#include "Product/Features/Workspace/WorkspaceFeatureHelpers.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kProjectUpdatePriority = 20; + +std::string_view DescribeProjectItemKind(ProjectBrowserModel::ItemKind kind) { + switch (kind) { + case ProjectBrowserModel::ItemKind::Folder: + return "Folder"; + case ProjectBrowserModel::ItemKind::Scene: + return "Scene"; + case ProjectBrowserModel::ItemKind::Model: + return "Model"; + case ProjectBrowserModel::ItemKind::Material: + return "Material"; + case ProjectBrowserModel::ItemKind::Texture: + return "Texture"; + case ProjectBrowserModel::ItemKind::Script: + return "Script"; + case ProjectBrowserModel::ItemKind::File: + default: + return "File"; + } +} + +std::string DescribeProjectPanelEvent(const ProjectPanel::Event& event) { + std::ostringstream stream = {}; + switch (event.kind) { + case ProjectPanel::EventKind::AssetSelected: + stream << "AssetSelected"; + break; + case ProjectPanel::EventKind::AssetSelectionCleared: + stream << "AssetSelectionCleared"; + break; + case ProjectPanel::EventKind::FolderNavigated: + stream << "FolderNavigated"; + break; + case ProjectPanel::EventKind::AssetOpened: + stream << "AssetOpened"; + break; + case ProjectPanel::EventKind::RenameRequested: + stream << "RenameRequested"; + break; + case ProjectPanel::EventKind::ContextMenuRequested: + stream << "ContextMenuRequested"; + break; + case ProjectPanel::EventKind::None: + default: + stream << "None"; + break; + } + + stream << " source="; + switch (event.source) { + case ProjectPanel::EventSource::Tree: + stream << "Tree"; + break; + case ProjectPanel::EventSource::Breadcrumb: + stream << "Breadcrumb"; + break; + case ProjectPanel::EventSource::GridPrimary: + stream << "GridPrimary"; + break; + case ProjectPanel::EventSource::GridDoubleClick: + stream << "GridDoubleClick"; + break; + case ProjectPanel::EventSource::GridSecondary: + stream << "GridSecondary"; + break; + case ProjectPanel::EventSource::GridDrag: + stream << "GridDrag"; + break; + case ProjectPanel::EventSource::Command: + stream << "Command"; + break; + case ProjectPanel::EventSource::Background: + stream << "Background"; + break; + case ProjectPanel::EventSource::None: + default: + stream << "None"; + break; + } + + if (!event.itemId.empty()) { + stream << " item=" << event.itemId; + } + if (!event.displayName.empty()) { + stream << " label=" << event.displayName; + } + if (!event.itemId.empty()) { + stream << " kind=" << DescribeProjectItemKind(event.itemKind); + } + return stream.str(); +} + +class ProjectWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + ProjectWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + EditorProjectRuntime& projectRuntime, + EditorCommandFocusService& commandFocusService, + ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, + RequestOpenSceneAssetCallback requestOpenSceneAsset) + : m_panelId(feature.panelId) + , m_panelTitle(feature.defaultTitle) + , m_actionRoute(feature.actionRoute) + , m_projectRuntime(projectRuntime) + , m_commandFocusService(commandFocusService) + , m_systemInteractionHost(systemInteractionHost) + , m_requestOpenSceneAsset(std::move(requestOpenSceneAsset)) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.Project"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + int GetUpdatePriority() const override { + return kProjectUpdatePriority; + } + + void Initialize(const EditorWorkspaceHostedContentInitializationContext& context) override { + m_panel.SetIconService(&context.iconService); + m_panel.SetTextMeasurer(&context.textMeasurer); + } + + void ResetInteractionState() override { + m_panel.ResetInteractionState(); + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_panel.SetProjectRuntime(&m_projectRuntime); + m_panel.SetCommandFocusService(&m_commandFocusService); + m_panel.SetSystemInteractionHost(m_systemInteractionHost); + m_panel.SetSceneAssetOpenRequestHandler(m_requestOpenSceneAsset); + m_panel.Update( + ResolveHostedPanelDispatchEntry( + context.shellFrame.hostedPanelDispatchFrame, + GetPanelId()), + context.hostedContentEvents); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.Append(drawList); + } + + void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const override { + m_panel.AppendOverlay(drawList); + } + + std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const override { + return m_panel.CollectInteractiveOverlayBounds(); + } + + bool HasActivePointerCapture() const override { + return m_panel.HasActivePointerCapture(); + } + + EditorWorkspaceHostedContentCursorKind GetCursorKind() const override { + switch (m_panel.GetCursorKind()) { + case ProjectPanel::CursorKind::ResizeEW: + return EditorWorkspaceHostedContentCursorKind::ResizeEW; + case ProjectPanel::CursorKind::Arrow: + default: + return EditorWorkspaceHostedContentCursorKind::Arrow; + } + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_panel; + } + + std::vector CollectFrameEvents() const override { + std::vector events = {}; + for (const ProjectPanel::Event& event : m_panel.GetFrameEvents()) { + events.push_back(EditorWorkspaceHostedContentFrameEvent{ + .status = std::string(m_panelTitle), + .traceChannel = std::string(m_panelId), + .message = DescribeProjectPanelEvent(event), + }); + } + return events; + } + +private: + std::string_view m_panelId = {}; + std::string_view m_panelTitle = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + EditorProjectRuntime& m_projectRuntime; + EditorCommandFocusService& m_commandFocusService; + ::XCEngine::UI::Editor::System::SystemInteractionService* m_systemInteractionHost = + nullptr; + RequestOpenSceneAssetCallback m_requestOpenSceneAsset = {}; + ProjectPanel m_panel = {}; +}; + +std::unique_ptr CreateProjectWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.projectRuntime, + context.commandFocusService, + context.systemInteractionHost, + context.requestOpenSceneAsset); +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetProjectWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kProjectWorkspaceFeaturePanelId, + .defaultTitle = "Project", + .presentationKind = UIEditorPanelPresentationKind::HostedContent, + .placeholder = false, + .canHide = false, + .canClose = false, + .showViewportTopBar = false, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Project, + .viewportKind = EditorProductViewportKind::None, + .viewportPlaceholderStatus = {}, + .createContent = &CreateProjectWorkspaceContent, + .customizePresentation = nullptr, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.h new file mode 100644 index 00000000..082700d3 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Project/ProjectWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kProjectWorkspaceFeaturePanelId = "project"; + +EditorWorkspaceFeatureDescriptor GetProjectWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Features/Scene/SceneEditCommandRoute.cpp b/editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.cpp similarity index 97% rename from editor/app/Features/Scene/SceneEditCommandRoute.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.cpp index 757fb0ee..c7e11d93 100644 --- a/editor/app/Features/Scene/SceneEditCommandRoute.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.cpp @@ -1,6 +1,6 @@ -#include "Scene/SceneEditCommandRoute.h" +#include "Product/Features/Workspace/Scene/SceneEditCommandRoute.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" #include diff --git a/editor/app/Features/Scene/SceneEditCommandRoute.h b/editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.h similarity index 90% rename from editor/app/Features/Scene/SceneEditCommandRoute.h rename to editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.h index 83169a38..817b4658 100644 --- a/editor/app/Features/Scene/SceneEditCommandRoute.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneEditCommandRoute.h @@ -1,6 +1,6 @@ #pragma once -#include "Commands/EditorEditCommandRoute.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" #include diff --git a/editor/app/Features/Scene/SceneViewportController.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportController.cpp similarity index 96% rename from editor/app/Features/Scene/SceneViewportController.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportController.cpp index 068297e0..2af137ff 100644 --- a/editor/app/Features/Scene/SceneViewportController.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportController.cpp @@ -1,11 +1,11 @@ -#include "Scene/SceneViewportController.h" +#include "Product/Features/Workspace/Scene/SceneViewportController.h" -#include "Viewport/EditorViewportPicking.h" -#include "Scene/EditorSceneRuntime.h" -#include "Scene/SceneViewportSession.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Core/Viewport/EditorViewportPicking.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Scene/SceneWorkspaceFeature.h" +#include "Product/Features/Workspace/Scene/SceneViewportSession.h" +#include "Product/State/EditorCommandFocusService.h" -#include "Panels/EditorPanelIds.h" #include #include @@ -169,9 +169,13 @@ void SceneViewportController::Update( const UIEditorWorkspaceComposeState& composeState, const UIEditorWorkspaceComposeFrame& composeFrame) { const UIEditorWorkspaceViewportComposeFrame* viewportFrame = - FindUIEditorWorkspaceViewportPresentationFrame(composeFrame, kScenePanelId); + FindUIEditorWorkspaceViewportPresentationFrame( + composeFrame, + kSceneWorkspaceFeaturePanelId); const UIEditorWorkspacePanelPresentationState* panelState = - FindUIEditorWorkspacePanelPresentationState(composeState, kScenePanelId); + FindUIEditorWorkspacePanelPresentationState( + composeState, + kSceneWorkspaceFeaturePanelId); if (viewportFrame == nullptr || panelState == nullptr) { if (m_transformGizmo.IsActive()) { m_transformGizmo.CancelDrag(sceneRuntime); diff --git a/editor/app/Features/Scene/SceneViewportController.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportController.h similarity index 91% rename from editor/app/Features/Scene/SceneViewportController.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportController.h index f4071e2c..33da45b7 100644 --- a/editor/app/Features/Scene/SceneViewportController.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportController.h @@ -1,8 +1,8 @@ #pragma once -#include "Scene/SceneViewportSceneOverlay.h" -#include "Scene/SceneViewportTransformGizmo.h" -#include "Scene/SceneViewportToolOverlay.h" +#include "Product/Features/Workspace/Scene/SceneViewportSceneOverlay.h" +#include "Product/Features/Workspace/Scene/SceneViewportTransformGizmo.h" +#include "Product/Features/Workspace/Scene/SceneViewportToolOverlay.h" #include diff --git a/editor/app/Features/Scene/SceneViewportFeature.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.cpp similarity index 89% rename from editor/app/Features/Scene/SceneViewportFeature.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.cpp index 722af237..5cea1b35 100644 --- a/editor/app/Features/Scene/SceneViewportFeature.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.cpp @@ -1,7 +1,7 @@ -#include "Scene/SceneViewportFeature.h" +#include "Product/Features/Workspace/Scene/SceneViewportFeature.h" -#include "Scene/EditorSceneRuntime.h" -#include "State/EditorCommandFocusService.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/State/EditorCommandFocusService.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Scene/SceneViewportFeature.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.h similarity index 80% rename from editor/app/Features/Scene/SceneViewportFeature.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.h index 748b7d5b..061d10ca 100644 --- a/editor/app/Features/Scene/SceneViewportFeature.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportFeature.h @@ -1,9 +1,9 @@ #pragma once -#include "Scene/SceneViewportSession.h" -#include "Assets/EditorIconService.h" -#include "Scene/SceneViewportController.h" -#include "Viewport/EditorViewportRuntimeServices.h" +#include "Product/Features/Workspace/Scene/SceneViewportSession.h" +#include "Product/Core/Assets/EditorIconService.h" +#include "Product/Features/Workspace/Scene/SceneViewportController.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" #include diff --git a/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.cpp similarity index 94% rename from editor/app/Features/Scene/SceneViewportSceneOverlay.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.cpp index cc0d5970..81ba8e72 100644 --- a/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.cpp @@ -1,9 +1,9 @@ -#include "Scene/SceneViewportSceneOverlay.h" +#include "Product/Features/Workspace/Scene/SceneViewportSceneOverlay.h" -#include "Scene/SceneViewportTransformGizmoSupport.h" -#include "Assets/EditorIconService.h" -#include "Scene/EditorSceneRuntime.h" -#include "Scene/SceneViewportSession.h" +#include "Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h" +#include "Product/Core/Assets/EditorIconService.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Scene/SceneViewportSession.h" #include #include diff --git a/editor/app/Features/Scene/SceneViewportSceneOverlay.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.h similarity index 100% rename from editor/app/Features/Scene/SceneViewportSceneOverlay.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportSceneOverlay.h diff --git a/editor/app/Features/Scene/SceneViewportSession.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportSession.cpp similarity index 96% rename from editor/app/Features/Scene/SceneViewportSession.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportSession.cpp index 9e7d09db..d855550d 100644 --- a/editor/app/Features/Scene/SceneViewportSession.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportSession.cpp @@ -1,6 +1,6 @@ -#include "Scene/SceneViewportSession.h" +#include "Product/Features/Workspace/Scene/SceneViewportSession.h" -#include "Scene/EditorSceneRuntime.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Features/Scene/SceneViewportSession.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportSession.h similarity index 84% rename from editor/app/Features/Scene/SceneViewportSession.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportSession.h index 5481051f..6d7c1bad 100644 --- a/editor/app/Features/Scene/SceneViewportSession.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportSession.h @@ -1,9 +1,9 @@ #pragma once -#include "Scene/EditorSceneBackend.h" -#include "Scene/SceneToolState.h" -#include "Scene/SceneViewportCameraController.h" -#include "Scene/SceneViewportRenderRequest.h" +#include "Product/Core/Scene/EditorSceneBackend.h" +#include "Product/Services/Scene/SceneToolState.h" +#include "Product/Services/Scene/SceneViewportCameraController.h" +#include "Product/Core/Scene/SceneViewportRenderRequest.h" #include diff --git a/editor/app/Features/Scene/SceneViewportToolOverlay.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.cpp similarity index 99% rename from editor/app/Features/Scene/SceneViewportToolOverlay.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.cpp index dcab9632..cff34d13 100644 --- a/editor/app/Features/Scene/SceneViewportToolOverlay.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.cpp @@ -1,4 +1,4 @@ -#include "Scene/SceneViewportToolOverlay.h" +#include "Product/Features/Workspace/Scene/SceneViewportToolOverlay.h" #include diff --git a/editor/app/Features/Scene/SceneViewportToolOverlay.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.h similarity index 96% rename from editor/app/Features/Scene/SceneViewportToolOverlay.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.h index 9c1e555b..1de47d79 100644 --- a/editor/app/Features/Scene/SceneViewportToolOverlay.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportToolOverlay.h @@ -1,7 +1,7 @@ #pragma once -#include "Scene/SceneToolState.h" -#include "Assets/EditorIconService.h" +#include "Product/Services/Scene/SceneToolState.h" +#include "Product/Core/Assets/EditorIconService.h" #include #include diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.cpp similarity index 98% rename from editor/app/Features/Scene/SceneViewportTransformGizmo.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.cpp index a5e141f3..f8af5096 100644 --- a/editor/app/Features/Scene/SceneViewportTransformGizmo.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.cpp @@ -1,9 +1,9 @@ -#include "Scene/SceneViewportTransformGizmo.h" +#include "Product/Features/Workspace/Scene/SceneViewportTransformGizmo.h" -#include "Scene/SceneViewportTransformGizmoSupport.h" -#include "Scene/EditorSceneRuntime.h" -#include "Scene/SceneViewportSession.h" -#include "Scene/SceneToolState.h" +#include "Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/Features/Workspace/Scene/SceneViewportSession.h" +#include "Product/Services/Scene/SceneToolState.h" #include #include diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmo.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.h similarity index 100% rename from editor/app/Features/Scene/SceneViewportTransformGizmo.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmo.h diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmoSupport.cpp b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.cpp similarity index 99% rename from editor/app/Features/Scene/SceneViewportTransformGizmoSupport.cpp rename to editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.cpp index da235c7b..9e5efec2 100644 --- a/editor/app/Features/Scene/SceneViewportTransformGizmoSupport.cpp +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.cpp @@ -1,4 +1,4 @@ -#include "Scene/SceneViewportTransformGizmoSupport.h" +#include "Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h" #include #include diff --git a/editor/app/Features/Scene/SceneViewportTransformGizmoSupport.h b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h similarity index 99% rename from editor/app/Features/Scene/SceneViewportTransformGizmoSupport.h rename to editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h index ed7a47ef..ce8c3827 100644 --- a/editor/app/Features/Scene/SceneViewportTransformGizmoSupport.h +++ b/editor/src/Product/Features/Workspace/Scene/SceneViewportTransformGizmoSupport.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include #include diff --git a/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.cpp b/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.cpp new file mode 100644 index 00000000..f223e51d --- /dev/null +++ b/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.cpp @@ -0,0 +1,131 @@ +#include "Product/Features/Workspace/Scene/SceneWorkspaceFeature.h" + +#include "Product/Features/Workspace/Scene/SceneEditCommandRoute.h" +#include "Product/Features/Workspace/Scene/SceneViewportFeature.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +constexpr int kSceneUpdatePriority = 0; + +class SceneWorkspaceContent final : public EditorWorkspaceHostedContent { +public: + SceneWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + EditorSceneRuntime& sceneRuntime, + EditorCommandFocusService& commandFocusService) + : m_panelId(feature.panelId) + , m_actionRoute(feature.actionRoute) + , m_sceneRuntime(sceneRuntime) + , m_commandFocusService(commandFocusService) {} + + std::string_view GetPanelId() const override { + return m_panelId; + } + + std::string_view GetDrawListId() const override { + return "XCEditorPanel.SceneOverlay"; + } + + EditorActionRoute GetActionRoute() const override { + return m_actionRoute; + } + + int GetUpdatePriority() const override { + return kSceneUpdatePriority; + } + + void Initialize(const EditorWorkspaceHostedContentInitializationContext& context) override { + if (context.sceneViewportRuntime != nullptr) { + m_feature.Initialize( + &context.iconService, + *context.sceneViewportRuntime); + } + } + + void Shutdown(const EditorWorkspaceHostedContentShutdownContext& context) override { + (void)context; + m_feature.Shutdown(); + m_commandRoute.BindSceneRuntime(nullptr); + } + + void ResetInteractionState() override { + m_feature.ResetInteractionState(); + } + + void PrepareForShellDefinition(UIEditorWorkspaceController&) override { + m_commandRoute.BindSceneRuntime(&m_sceneRuntime); + m_feature.SetCommandFocusService(&m_commandFocusService); + m_feature.SyncRenderRequest(m_sceneRuntime); + } + + void Update(const EditorWorkspaceHostedContentUpdateContext& context) override { + m_feature.Update( + m_sceneRuntime, + context.shellInteractionState.workspaceInteractionState.composeState, + context.shellFrame.workspaceInteractionFrame.composeFrame); + } + + void Append(::XCEngine::UI::UIDrawList& drawList) const override { + m_feature.Append(drawList); + } + + EditorEditCommandRoute* GetEditCommandRoute() override { + return &m_commandRoute; + } + +private: + std::string_view m_panelId = {}; + EditorActionRoute m_actionRoute = EditorActionRoute::None; + EditorSceneRuntime& m_sceneRuntime; + EditorCommandFocusService& m_commandFocusService; + SceneViewportFeature m_feature = {}; + SceneEditCommandRoute m_commandRoute = {}; +}; + +std::unique_ptr CreateSceneWorkspaceContent( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context) { + return std::make_unique( + feature, + context.sceneRuntime, + context.commandFocusService); +} + +void CustomizeScenePresentation(UIEditorWorkspacePanelPresentationModel& presentation) { + presentation.viewportShellModel.spec.chrome.showTopBar = true; + presentation.viewportShellModel.spec.chrome.topBarHeight = 24.0f; + presentation.viewportShellModel.spec.chrome.title = {}; + presentation.viewportShellModel.spec.chrome.subtitle = {}; + presentation.viewportShellModel.spec.toolItems.clear(); + presentation.viewportShellModel.spec.visualState.hoveredToolIndex = + Widgets::UIEditorViewportSlotInvalidIndex; + presentation.viewportShellModel.spec.visualState.activeToolIndex = + Widgets::UIEditorViewportSlotInvalidIndex; +} + +} // namespace + +EditorWorkspaceFeatureDescriptor GetSceneWorkspaceFeature() { + return EditorWorkspaceFeatureDescriptor{ + .panelId = kSceneWorkspaceFeaturePanelId, + .defaultTitle = "Scene", + .presentationKind = UIEditorPanelPresentationKind::ViewportShell, + .placeholder = false, + .canHide = false, + .canClose = false, + .showViewportTopBar = true, + .showViewportBottomBar = false, + .actionRoute = EditorActionRoute::Scene, + .viewportKind = EditorProductViewportKind::Scene, + .viewportPlaceholderStatus = {}, + .createContent = &CreateSceneWorkspaceContent, + .customizePresentation = &CustomizeScenePresentation, + }; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.h b/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.h new file mode 100644 index 00000000..4e54a5b9 --- /dev/null +++ b/editor/src/Product/Features/Workspace/Scene/SceneWorkspaceFeature.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Product/Registry/EditorProductRegistry.h" + +#include + +namespace XCEngine::UI::Editor::App { + +inline constexpr std::string_view kSceneWorkspaceFeaturePanelId = "scene"; + +EditorWorkspaceFeatureDescriptor GetSceneWorkspaceFeature(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Features/Workspace/WorkspaceFeatureHelpers.h b/editor/src/Product/Features/Workspace/WorkspaceFeatureHelpers.h new file mode 100644 index 00000000..fce4b7f0 --- /dev/null +++ b/editor/src/Product/Features/Workspace/WorkspaceFeatureHelpers.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace XCEngine::UI::Editor::App { + +inline const UIEditorHostedPanelDispatchEntry& ResolveHostedPanelDispatchEntry( + const UIEditorHostedPanelDispatchFrame& dispatchFrame, + std::string_view panelId) { + static const UIEditorHostedPanelDispatchEntry kEmptyEntry = {}; + if (const UIEditorHostedPanelDispatchEntry* entry = + FindUIEditorHostedPanelDispatchEntry(dispatchFrame, panelId); + entry != nullptr) { + return *entry; + } + + return kEmptyEntry; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Core/UtilityWindows/EditorUtilityWindowRuntime.h b/editor/src/Product/Framework/UtilityWindow/EditorUtilityWindowContent.h similarity index 84% rename from editor/app/Core/UtilityWindows/EditorUtilityWindowRuntime.h rename to editor/src/Product/Framework/UtilityWindow/EditorUtilityWindowContent.h index a4fa75b2..1861a77d 100644 --- a/editor/app/Core/UtilityWindows/EditorUtilityWindowRuntime.h +++ b/editor/src/Product/Framework/UtilityWindow/EditorUtilityWindowContent.h @@ -1,6 +1,6 @@ #pragma once -#include "Windowing/EditorWindowTypes.h" +#include "Product/Core/Windowing/EditorWindowTypes.h" #include #include @@ -44,9 +44,9 @@ struct EditorUtilityWindowHostContext { bool focusLost = false; }; -class EditorUtilityWindowPanel { +class EditorUtilityWindowContent { public: - virtual ~EditorUtilityWindowPanel() = default; + virtual ~EditorUtilityWindowContent() = default; virtual std::string_view GetDrawListId() const = 0; virtual void ResetInteractionState() = 0; @@ -56,7 +56,7 @@ public: virtual void Append(::XCEngine::UI::UIDrawList& drawList) const = 0; }; -using EditorUtilityWindowPanelFactory = - std::function(EditorUtilityWindowKind)>; +using EditorUtilityWindowContentFactory = + std::function(EditorUtilityWindowKind)>; } // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContent.h b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContent.h new file mode 100644 index 00000000..89b04cdf --- /dev/null +++ b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContent.h @@ -0,0 +1,126 @@ +#pragma once + +#include "Product/Core/Assets/EditorIconService.h" +#include "Product/Core/Commands/EditorEditCommandRoute.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" +#include "Product/State/EditorColorPickerToolState.h" +#include "Product/State/EditorCommandFocusService.h" +#include "Product/State/EditorSession.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace XCEngine::UI { + +struct UIInputEvent; + +} // namespace XCEngine::UI + +namespace XCEngine::UI::Editor::System { + +class SystemInteractionService; + +} // namespace XCEngine::UI::Editor::System + +namespace XCEngine::UI::Editor::App { + +class EditorCommandFocusService; +class EditorEditCommandRoute; +class EditorProjectRuntime; +class EditorSceneRuntime; +enum class EditorUtilityWindowKind : std::uint8_t; + +using RequestOpenSceneAssetCallback = + std::function; +using RequestOpenUtilityWindowCallback = + std::function; + +enum class EditorWorkspaceHostedContentCursorKind : std::uint8_t { + Arrow = 0, + ResizeEW +}; + +enum class EditorWorkspaceHostedContentUpdatePhase : std::uint8_t { + Main = 0, + AfterCommandFocusSync +}; + +struct EditorWorkspaceHostedContentFrameEvent { + std::string status = {}; + std::string traceChannel = {}; + std::string message = {}; +}; + +struct EditorWorkspaceHostedContentCommandRouteBinding { + EditorActionRoute route = EditorActionRoute::None; + EditorEditCommandRoute* editCommandRoute = nullptr; +}; + +struct EditorWorkspaceHostedContentInitializationContext { + const EditorRuntimePaths& runtimePaths; + UIEditorTextMeasurer& textMeasurer; + EditorIconService& iconService; + EditorSceneViewportRuntime* sceneViewportRuntime = nullptr; +}; + +struct EditorWorkspaceHostedContentShutdownContext {}; + +struct EditorWorkspaceHostedContentUpdateContext { + UIEditorShellInteractionFrame& shellFrame; + UIEditorShellInteractionState& shellInteractionState; + const std::vector<::XCEngine::UI::UIInputEvent>& hostedContentEvents; +}; + +class EditorWorkspaceHostedContent { +public: + virtual ~EditorWorkspaceHostedContent() = default; + + virtual std::string_view GetPanelId() const = 0; + virtual std::string_view GetDrawListId() const = 0; + virtual EditorActionRoute GetActionRoute() const = 0; + + virtual void Initialize(const EditorWorkspaceHostedContentInitializationContext&) {} + virtual void Shutdown(const EditorWorkspaceHostedContentShutdownContext&) {} + virtual void ResetInteractionState() {} + virtual void PrepareForShellDefinition(UIEditorWorkspaceController&) {} + virtual EditorWorkspaceHostedContentUpdatePhase GetUpdatePhase() const { + return EditorWorkspaceHostedContentUpdatePhase::Main; + } + virtual int GetUpdatePriority() const { + return 0; + } + virtual void Update(const EditorWorkspaceHostedContentUpdateContext& context) = 0; + virtual void Append(::XCEngine::UI::UIDrawList& drawList) const = 0; + virtual void AppendOverlay(::XCEngine::UI::UIDrawList& drawList) const {} + virtual std::vector<::XCEngine::UI::UIRect> CollectInteractiveOverlayBounds() const { + return {}; + } + virtual bool HasActivePointerCapture() const { + return false; + } + virtual EditorWorkspaceHostedContentCursorKind GetCursorKind() const { + return EditorWorkspaceHostedContentCursorKind::Arrow; + } + virtual EditorEditCommandRoute* GetEditCommandRoute() { + return nullptr; + } + virtual std::vector CollectFrameEvents() const { + return {}; + } +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.cpp b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.cpp new file mode 100644 index 00000000..4ff98c70 --- /dev/null +++ b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.cpp @@ -0,0 +1,207 @@ +#include "Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h" + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +template +void AppendDrawPacket( + ::XCEngine::UI::UIDrawData& drawData, + std::string debugName, + AppendFn&& appendFn) { + ::XCEngine::UI::UIDrawList drawList(std::move(debugName)); + appendFn(drawList); + if (!drawList.Empty()) { + drawData.AddDrawList(std::move(drawList)); + } +} + +} // namespace + +void EditorWorkspaceHostedContentSet::AddContent( + std::unique_ptr content) { + if (content != nullptr) { + m_contents.push_back(std::move(content)); + } +} + +EditorCommandFocusService& EditorWorkspaceHostedContentSet::GetCommandFocusService() { + return m_commandFocusService; +} + +const EditorCommandFocusService& +EditorWorkspaceHostedContentSet::GetCommandFocusService() const { + return m_commandFocusService; +} + +void EditorWorkspaceHostedContentSet::Initialize( + const EditorWorkspaceHostedContentInitializationContext& context) { + for (const std::unique_ptr& content : m_contents) { + content->Initialize(context); + } +} + +void EditorWorkspaceHostedContentSet::Shutdown( + const EditorWorkspaceHostedContentShutdownContext& context) { + for (const std::unique_ptr& content : m_contents) { + content->Shutdown(context); + } +} + +void EditorWorkspaceHostedContentSet::ResetInteractionState() { + m_commandFocusService.ClearFocus(); + for (const std::unique_ptr& content : m_contents) { + content->ResetInteractionState(); + } +} + +void EditorWorkspaceHostedContentSet::PrepareForShellDefinition( + UIEditorWorkspaceController& workspaceController) { + for (const std::unique_ptr& content : m_contents) { + content->PrepareForShellDefinition(workspaceController); + } +} + +void EditorWorkspaceHostedContentSet::UpdatePhase( + const EditorWorkspaceHostedContentUpdateContext& context, + EditorWorkspaceHostedContentUpdatePhase phase) { + const std::vector phaseContents = + BuildUpdateOrder(phase); + for (EditorWorkspaceHostedContent* content : phaseContents) { + content->Update(context); + } +} + +void EditorWorkspaceHostedContentSet::AppendDrawPackets( + ::XCEngine::UI::UIDrawData& drawData) const { + for (const std::unique_ptr& content : m_contents) { + AppendDrawPacket( + drawData, + std::string(content->GetDrawListId()), + [&](::XCEngine::UI::UIDrawList& drawList) { + content->Append(drawList); + }); + } +} + +void EditorWorkspaceHostedContentSet::AppendOverlayDrawPackets( + ::XCEngine::UI::UIDrawData& drawData) const { + for (const std::unique_ptr& content : m_contents) { + AppendDrawPacket( + drawData, + std::string(content->GetDrawListId()) + ".Overlay", + [&](::XCEngine::UI::UIDrawList& drawList) { + content->AppendOverlay(drawList); + }); + } +} + +std::vector +EditorWorkspaceHostedContentSet::CollectPanelOverlayRegions() const { + std::vector regions = {}; + for (const std::unique_ptr& content : m_contents) { + const std::vector<::XCEngine::UI::UIRect> overlayBounds = + content->CollectInteractiveOverlayBounds(); + regions.reserve(regions.size() + overlayBounds.size()); + for (const ::XCEngine::UI::UIRect& bounds : overlayBounds) { + UIEditorWorkspacePanelOverlayRegion region = {}; + region.panelId = std::string(content->GetPanelId()); + region.bounds = bounds; + regions.push_back(std::move(region)); + } + } + return regions; +} + +bool EditorWorkspaceHostedContentSet::HasActivePointerCapture() const { + for (const std::unique_ptr& content : m_contents) { + if (content->HasActivePointerCapture()) { + return true; + } + } + + return false; +} + +EditorWorkspaceHostedContentCursorKind +EditorWorkspaceHostedContentSet::GetHostedContentCursorKind() const { + for (const std::unique_ptr& content : m_contents) { + const EditorWorkspaceHostedContentCursorKind cursorKind = content->GetCursorKind(); + if (cursorKind != EditorWorkspaceHostedContentCursorKind::Arrow) { + return cursorKind; + } + } + + return EditorWorkspaceHostedContentCursorKind::Arrow; +} + +EditorEditCommandRoute* EditorWorkspaceHostedContentSet::FindCommandRoute( + EditorActionRoute route) { + for (const std::unique_ptr& content : m_contents) { + if (content->GetActionRoute() == route) { + return content->GetEditCommandRoute(); + } + } + + return nullptr; +} + +std::vector +EditorWorkspaceHostedContentSet::CollectCommandRoutes() const { + std::vector bindings = {}; + bindings.reserve(m_contents.size()); + for (const std::unique_ptr& content : m_contents) { + const EditorActionRoute route = content->GetActionRoute(); + if (route == EditorActionRoute::None) { + continue; + } + + bindings.push_back(EditorWorkspaceHostedContentCommandRouteBinding{ + .route = route, + .editCommandRoute = content->GetEditCommandRoute(), + }); + } + return bindings; +} + +std::vector +EditorWorkspaceHostedContentSet::CollectFrameEvents() const { + std::vector events = {}; + for (const std::unique_ptr& content : m_contents) { + std::vector contentEvents = + content->CollectFrameEvents(); + events.insert( + events.end(), + std::make_move_iterator(contentEvents.begin()), + std::make_move_iterator(contentEvents.end())); + } + return events; +} + +std::vector +EditorWorkspaceHostedContentSet::BuildUpdateOrder( + EditorWorkspaceHostedContentUpdatePhase phase) const { + std::vector contents = {}; + contents.reserve(m_contents.size()); + for (const std::unique_ptr& content : m_contents) { + if (content->GetUpdatePhase() == phase) { + contents.push_back(content.get()); + } + } + + std::sort( + contents.begin(), + contents.end(), + [](const EditorWorkspaceHostedContent* left, + const EditorWorkspaceHostedContent* right) { + return left->GetUpdatePriority() < right->GetUpdatePriority(); + }); + return contents; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h new file mode 100644 index 00000000..8bedc960 --- /dev/null +++ b/editor/src/Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Product/Framework/Workspace/EditorWorkspaceHostedContent.h" + +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorWorkspaceHostedContentSet final { +public: + EditorWorkspaceHostedContentSet() = default; + EditorWorkspaceHostedContentSet(const EditorWorkspaceHostedContentSet&) = delete; + EditorWorkspaceHostedContentSet& operator=(const EditorWorkspaceHostedContentSet&) = delete; + EditorWorkspaceHostedContentSet(EditorWorkspaceHostedContentSet&&) noexcept = default; + EditorWorkspaceHostedContentSet& operator=(EditorWorkspaceHostedContentSet&&) noexcept = + default; + + void AddContent(std::unique_ptr content); + EditorCommandFocusService& GetCommandFocusService(); + const EditorCommandFocusService& GetCommandFocusService() const; + void Initialize(const EditorWorkspaceHostedContentInitializationContext& context); + void Shutdown(const EditorWorkspaceHostedContentShutdownContext& context); + void ResetInteractionState(); + void PrepareForShellDefinition(UIEditorWorkspaceController& workspaceController); + void UpdatePhase( + const EditorWorkspaceHostedContentUpdateContext& context, + EditorWorkspaceHostedContentUpdatePhase phase); + void AppendDrawPackets(::XCEngine::UI::UIDrawData& drawData) const; + void AppendOverlayDrawPackets(::XCEngine::UI::UIDrawData& drawData) const; + std::vector CollectPanelOverlayRegions() const; + + bool HasActivePointerCapture() const; + EditorWorkspaceHostedContentCursorKind GetHostedContentCursorKind() const; + EditorEditCommandRoute* FindCommandRoute(EditorActionRoute route); + std::vector CollectCommandRoutes() const; + std::vector CollectFrameEvents() const; + +private: + std::vector BuildUpdateOrder( + EditorWorkspaceHostedContentUpdatePhase phase) const; + + EditorCommandFocusService m_commandFocusService = {}; + std::vector> m_contents = {}; +}; + +using EditorWorkspaceHostedContentSetFactory = + std::function; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Registry/EditorProductRegistry.cpp b/editor/src/Product/Registry/EditorProductRegistry.cpp new file mode 100644 index 00000000..7ef2b5fc --- /dev/null +++ b/editor/src/Product/Registry/EditorProductRegistry.cpp @@ -0,0 +1,207 @@ +#include "Product/Registry/EditorProductRegistry.h" + +#include "Product/Features/Utility/AddComponent/AddComponentUtilityFeature.h" +#include "Product/Features/Utility/ColorPicker/ColorPickerUtilityFeature.h" +#include "Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h" +#include "Product/Features/Workspace/Game/GameWorkspaceFeature.h" +#include "Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h" +#include "Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h" +#include "Product/Features/Workspace/Project/ProjectWorkspaceFeature.h" +#include "Product/Features/Workspace/Scene/SceneWorkspaceFeature.h" + +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +EditorProductRegistryValidationResult BuildValidationError( + EditorProductRegistryValidationCode code, + std::string message) { + EditorProductRegistryValidationResult result = {}; + result.code = code; + result.message = std::move(message); + return result; +} + +} // namespace + +std::span GetEditorWorkspaceFeatures() { + static const std::array kWorkspaceFeatures = { + GetHierarchyWorkspaceFeature(), + GetSceneWorkspaceFeature(), + GetGameWorkspaceFeature(), + GetInspectorWorkspaceFeature(), + GetConsoleWorkspaceFeature(), + GetProjectWorkspaceFeature(), + }; + return kWorkspaceFeatures; +} + +const EditorWorkspaceFeatureDescriptor* FindEditorWorkspaceFeature( + std::string_view panelId) { + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + if (feature.panelId == panelId) { + return &feature; + } + } + + return nullptr; +} + +std::span GetEditorUtilityFeatures() { + static const std::array kUtilityFeatures = { + GetColorPickerUtilityFeature(), + GetAddComponentUtilityFeature(), + }; + return kUtilityFeatures; +} + +const EditorUtilityFeatureDescriptor* FindEditorUtilityFeature( + EditorUtilityWindowKind kind) { + for (const EditorUtilityFeatureDescriptor& feature : GetEditorUtilityFeatures()) { + if (feature.window.kind == kind) { + return &feature; + } + } + + return nullptr; +} + +const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor( + EditorUtilityWindowKind kind) { + if (const EditorUtilityFeatureDescriptor* feature = FindEditorUtilityFeature(kind); + feature != nullptr) { + return &feature->window; + } + + return nullptr; +} + +bool IsEditorWorkspaceViewportFeature(std::string_view panelId) { + const EditorWorkspaceFeatureDescriptor* feature = + FindEditorWorkspaceFeature(panelId); + return feature != nullptr && + feature->presentationKind == UIEditorPanelPresentationKind::ViewportShell; +} + +EditorProductRegistryValidationResult ValidateEditorProductRegistry() { + const std::span workspaceFeatures = + GetEditorWorkspaceFeatures(); + for (std::size_t index = 0u; index < workspaceFeatures.size(); ++index) { + const EditorWorkspaceFeatureDescriptor& feature = workspaceFeatures[index]; + if (feature.panelId.empty()) { + return BuildValidationError( + EditorProductRegistryValidationCode::EmptyWorkspacePanelId, + "Workspace feature registry contains a panel with an empty id."); + } + if (feature.defaultTitle.empty()) { + return BuildValidationError( + EditorProductRegistryValidationCode::EmptyWorkspacePanelTitle, + "Workspace feature registry contains a panel with an empty title."); + } + if (feature.createContent == nullptr) { + std::ostringstream message = {}; + message << "Workspace feature '" << feature.panelId + << "' has no hosted content factory."; + return BuildValidationError( + EditorProductRegistryValidationCode::MissingWorkspaceContentFactory, + message.str()); + } + + for (std::size_t other = index + 1u; other < workspaceFeatures.size(); ++other) { + if (workspaceFeatures[other].panelId == feature.panelId) { + std::ostringstream message = {}; + message << "Workspace feature registry duplicates panel id '" + << feature.panelId << "'."; + return BuildValidationError( + EditorProductRegistryValidationCode::DuplicateWorkspacePanelId, + message.str()); + } + } + + if (feature.presentationKind == UIEditorPanelPresentationKind::ViewportShell && + feature.viewportKind == EditorProductViewportKind::None) { + std::ostringstream message = {}; + message << "Viewport workspace feature '" << feature.panelId + << "' declares no viewport kind."; + return BuildValidationError( + EditorProductRegistryValidationCode::WorkspaceViewportMissingViewportKind, + message.str()); + } + + if (feature.presentationKind != UIEditorPanelPresentationKind::ViewportShell && + feature.viewportKind != EditorProductViewportKind::None) { + std::ostringstream message = {}; + message << "Non-viewport workspace feature '" << feature.panelId + << "' declares a viewport kind."; + return BuildValidationError( + EditorProductRegistryValidationCode::WorkspaceNonViewportHasViewportKind, + message.str()); + } + + if (feature.viewportKind == EditorProductViewportKind::Placeholder && + feature.viewportPlaceholderStatus.empty()) { + std::ostringstream message = {}; + message << "Placeholder viewport workspace feature '" << feature.panelId + << "' has no status text."; + return BuildValidationError( + EditorProductRegistryValidationCode::WorkspacePlaceholderViewportMissingStatus, + message.str()); + } + } + + const std::span utilityFeatures = + GetEditorUtilityFeatures(); + for (std::size_t index = 0u; index < utilityFeatures.size(); ++index) { + const EditorUtilityFeatureDescriptor& feature = utilityFeatures[index]; + if (feature.window.kind == EditorUtilityWindowKind::None) { + return BuildValidationError( + EditorProductRegistryValidationCode::InvalidUtilityWindowKind, + "Utility feature registry contains a utility window with kind None."); + } + if (feature.window.windowId.empty()) { + return BuildValidationError( + EditorProductRegistryValidationCode::EmptyUtilityWindowId, + "Utility feature registry contains a utility window with an empty id."); + } + if (feature.window.title == nullptr || feature.window.title[0] == L'\0') { + return BuildValidationError( + EditorProductRegistryValidationCode::EmptyUtilityWindowTitle, + "Utility feature registry contains a utility window with an empty title."); + } + if (feature.createContent == nullptr) { + std::ostringstream message = {}; + message << "Utility feature '" << feature.window.windowId + << "' has no content factory."; + return BuildValidationError( + EditorProductRegistryValidationCode::MissingUtilityContentFactory, + message.str()); + } + + for (std::size_t other = index + 1u; other < utilityFeatures.size(); ++other) { + if (utilityFeatures[other].window.kind == feature.window.kind) { + std::ostringstream message = {}; + message << "Utility feature registry duplicates utility kind " + << static_cast(feature.window.kind) << '.'; + return BuildValidationError( + EditorProductRegistryValidationCode::DuplicateUtilityWindowKind, + message.str()); + } + if (utilityFeatures[other].window.windowId == feature.window.windowId) { + std::ostringstream message = {}; + message << "Utility feature registry duplicates utility window id '" + << feature.window.windowId << "'."; + return BuildValidationError( + EditorProductRegistryValidationCode::DuplicateUtilityWindowId, + message.str()); + } + } + } + + return {}; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Registry/EditorProductRegistry.h b/editor/src/Product/Registry/EditorProductRegistry.h new file mode 100644 index 00000000..c1e5e7f2 --- /dev/null +++ b/editor/src/Product/Registry/EditorProductRegistry.h @@ -0,0 +1,110 @@ +#pragma once + +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" +#include "Product/Framework/Workspace/EditorWorkspaceHostedContent.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::System { + +class SystemInteractionService; + +} // namespace XCEngine::UI::Editor::System + +namespace XCEngine::UI::Editor::App { + +class EditorProjectRuntime; +class EditorSceneRuntime; +struct EditorWorkspaceFeatureContext; +struct EditorUtilityFeatureContext; +struct EditorWorkspaceFeatureDescriptor; +struct EditorUtilityFeatureDescriptor; + +enum class EditorProductViewportKind : std::uint8_t { + None = 0, + Scene, + Game, + Placeholder, +}; + +using EditorWorkspaceFeatureContentFactory = + std::unique_ptr(*)( + const EditorWorkspaceFeatureDescriptor& feature, + const EditorWorkspaceFeatureContext& context); +using EditorWorkspacePresentationCustomizer = + void(*)(UIEditorWorkspacePanelPresentationModel& presentation); +using EditorUtilityFeatureContentFactory = + std::unique_ptr(*)( + const EditorUtilityFeatureDescriptor& feature, + const EditorUtilityFeatureContext& context); + +struct EditorWorkspaceFeatureDescriptor { + std::string_view panelId = {}; + std::string_view defaultTitle = {}; + UIEditorPanelPresentationKind presentationKind = + UIEditorPanelPresentationKind::Placeholder; + bool placeholder = true; + bool canHide = true; + bool canClose = true; + bool showViewportTopBar = false; + bool showViewportBottomBar = false; + EditorActionRoute actionRoute = EditorActionRoute::None; + EditorProductViewportKind viewportKind = EditorProductViewportKind::None; + std::string_view viewportPlaceholderStatus = {}; + EditorWorkspaceFeatureContentFactory createContent = nullptr; + EditorWorkspacePresentationCustomizer customizePresentation = nullptr; +}; + +struct EditorUtilityFeatureDescriptor { + EditorUtilityWindowDescriptor window = {}; + EditorUtilityFeatureContentFactory createContent = nullptr; +}; + +enum class EditorProductRegistryValidationCode : std::uint8_t { + None = 0, + EmptyWorkspacePanelId, + EmptyWorkspacePanelTitle, + DuplicateWorkspacePanelId, + WorkspaceViewportMissingViewportKind, + WorkspaceNonViewportHasViewportKind, + WorkspacePlaceholderViewportMissingStatus, + MissingWorkspaceContentFactory, + InvalidUtilityWindowKind, + EmptyUtilityWindowId, + EmptyUtilityWindowTitle, + DuplicateUtilityWindowKind, + DuplicateUtilityWindowId, + MissingUtilityContentFactory, +}; + +struct EditorProductRegistryValidationResult { + EditorProductRegistryValidationCode code = + EditorProductRegistryValidationCode::None; + std::string message = {}; + + [[nodiscard]] bool IsValid() const { + return code == EditorProductRegistryValidationCode::None; + } +}; + +std::span GetEditorWorkspaceFeatures(); +const EditorWorkspaceFeatureDescriptor* FindEditorWorkspaceFeature( + std::string_view panelId); + +std::span GetEditorUtilityFeatures(); +const EditorUtilityFeatureDescriptor* FindEditorUtilityFeature( + EditorUtilityWindowKind kind); +const EditorUtilityWindowDescriptor* ResolveEditorUtilityWindowDescriptor( + EditorUtilityWindowKind kind); + +bool IsEditorWorkspaceViewportFeature(std::string_view panelId); +EditorProductRegistryValidationResult ValidateEditorProductRegistry(); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Rendering/Assets/BuiltInIcons.cpp b/editor/src/Product/Rendering/Assets/BuiltInIcons.cpp similarity index 99% rename from editor/app/Rendering/Assets/BuiltInIcons.cpp rename to editor/src/Product/Rendering/Assets/BuiltInIcons.cpp index 992cbb01..b5eeb2cc 100644 --- a/editor/app/Rendering/Assets/BuiltInIcons.cpp +++ b/editor/src/Product/Rendering/Assets/BuiltInIcons.cpp @@ -1,4 +1,4 @@ -#include "BuiltInIcons.h" +#include "Product/Rendering/Assets/BuiltInIcons.h" #include "EditorHostResourceService.h" #include "UiTextureHost.h" diff --git a/editor/app/Rendering/Assets/BuiltInIcons.h b/editor/src/Product/Rendering/Assets/BuiltInIcons.h similarity index 98% rename from editor/app/Rendering/Assets/BuiltInIcons.h rename to editor/src/Product/Rendering/Assets/BuiltInIcons.h index 9f8ad44d..5f1cfd25 100644 --- a/editor/app/Rendering/Assets/BuiltInIcons.h +++ b/editor/src/Product/Rendering/Assets/BuiltInIcons.h @@ -1,6 +1,6 @@ #pragma once -#include "Assets/EditorIconService.h" +#include "Product/Core/Assets/EditorIconService.h" #include #include diff --git a/editor/app/Rendering/Assets/EditorIconServiceFactory.cpp b/editor/src/Product/Rendering/Assets/EditorIconServiceFactory.cpp similarity index 62% rename from editor/app/Rendering/Assets/EditorIconServiceFactory.cpp rename to editor/src/Product/Rendering/Assets/EditorIconServiceFactory.cpp index 25be3205..1c1e6bc2 100644 --- a/editor/app/Rendering/Assets/EditorIconServiceFactory.cpp +++ b/editor/src/Product/Rendering/Assets/EditorIconServiceFactory.cpp @@ -1,6 +1,6 @@ -#include "Assets/EditorIconServiceFactory.h" +#include "Product/Rendering/Assets/EditorIconServiceFactory.h" -#include "Assets/BuiltInIcons.h" +#include "Product/Rendering/Assets/BuiltInIcons.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Rendering/Assets/EditorIconServiceFactory.h b/editor/src/Product/Rendering/Assets/EditorIconServiceFactory.h similarity index 77% rename from editor/app/Rendering/Assets/EditorIconServiceFactory.h rename to editor/src/Product/Rendering/Assets/EditorIconServiceFactory.h index 4d6d5263..e9a9fa9c 100644 --- a/editor/app/Rendering/Assets/EditorIconServiceFactory.h +++ b/editor/src/Product/Rendering/Assets/EditorIconServiceFactory.h @@ -1,6 +1,6 @@ #pragma once -#include "Assets/EditorIconService.h" +#include "Product/Core/Assets/EditorIconService.h" #include diff --git a/editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp b/editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp similarity index 62% rename from editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp rename to editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp index 31a9157d..41db5a44 100644 --- a/editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp +++ b/editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.cpp @@ -1,6 +1,6 @@ -#include "Viewport/EditorViewportRuntimeServicesFactory.h" +#include "Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h" -#include "Viewport/ViewportHostService.h" +#include "Product/Rendering/Viewport/ViewportHostService.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h b/editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h similarity index 75% rename from editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h rename to editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h index 11422c84..5f16565d 100644 --- a/editor/app/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h +++ b/editor/src/Product/Rendering/Viewport/EditorViewportRuntimeServicesFactory.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/EditorViewportRuntimeServices.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" #include diff --git a/editor/app/Rendering/Viewport/GameViewportRenderService.cpp b/editor/src/Product/Rendering/Viewport/GameViewportRenderService.cpp similarity index 93% rename from editor/app/Rendering/Viewport/GameViewportRenderService.cpp rename to editor/src/Product/Rendering/Viewport/GameViewportRenderService.cpp index 3882a47f..d39902cf 100644 --- a/editor/app/Rendering/Viewport/GameViewportRenderService.cpp +++ b/editor/src/Product/Rendering/Viewport/GameViewportRenderService.cpp @@ -1,7 +1,7 @@ -#include "Viewport/GameViewportRenderService.h" +#include "Product/Rendering/Viewport/GameViewportRenderService.h" -#include "Engine/GameViewportEngineBridge.h" -#include "Viewport/ViewportRenderTargets.h" +#include "Product/Core/Engine/GameViewportEngineBridge.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include diff --git a/editor/app/Rendering/Viewport/GameViewportRenderService.h b/editor/src/Product/Rendering/Viewport/GameViewportRenderService.h similarity index 90% rename from editor/app/Rendering/Viewport/GameViewportRenderService.h rename to editor/src/Product/Rendering/Viewport/GameViewportRenderService.h index 3edf3c52..26ccf65b 100644 --- a/editor/app/Rendering/Viewport/GameViewportRenderService.h +++ b/editor/src/Product/Rendering/Viewport/GameViewportRenderService.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/ViewportContentRenderer.h" +#include "Product/Rendering/Viewport/ViewportContentRenderer.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.cpp b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.cpp similarity index 99% rename from editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.cpp rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.cpp index 880cbbb2..b9500e2f 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.cpp +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.cpp @@ -1,6 +1,6 @@ -#include "Viewport/Passes/SceneViewportGridPass.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportGridPass.h" -#include "Engine/EditorShaderProvider.h" +#include "Product/Core/Engine/EditorShaderProvider.h" #include #include diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.h b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.h similarity index 95% rename from editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.h rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.h index b57c8c8d..0c4eb6db 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportGridPass.h +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportGridPass.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/SceneViewportPassSpecs.h" +#include "Product/Rendering/Viewport/SceneViewportPassSpecs.h" #include #include diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp similarity index 99% rename from editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp index d3e49a43..1df11875 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.cpp @@ -1,4 +1,4 @@ -#include "Viewport/Passes/SceneViewportSelectedHelpersPass.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h" #include #include diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h similarity index 95% rename from editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h index c5d6fbe8..682dfdd3 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/SceneViewportRenderRequest.h" +#include "Product/Core/Scene/SceneViewportRenderRequest.h" #include diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp similarity index 99% rename from editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp index 1950c4f3..e7894989 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp @@ -1,7 +1,7 @@ -#include "Viewport/Passes/SceneViewportSelectionOutlinePass.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h" -#include "Engine/EditorShaderProvider.h" -#include "Viewport/ViewportRenderTargets.h" +#include "Product/Core/Engine/EditorShaderProvider.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include #include diff --git a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h similarity index 96% rename from editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h rename to editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h index b77dc34b..3bcf3fe1 100644 --- a/editor/app/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h +++ b/editor/src/Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/SceneViewportPassSpecs.h" +#include "Product/Rendering/Viewport/SceneViewportPassSpecs.h" #include #include diff --git a/editor/app/Rendering/Viewport/SceneViewportPassSpecs.h b/editor/src/Product/Rendering/Viewport/SceneViewportPassSpecs.h similarity index 100% rename from editor/app/Rendering/Viewport/SceneViewportPassSpecs.h rename to editor/src/Product/Rendering/Viewport/SceneViewportPassSpecs.h diff --git a/editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.cpp b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.cpp similarity index 95% rename from editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.cpp rename to editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.cpp index 05ad6abb..7c56d913 100644 --- a/editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.cpp +++ b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.cpp @@ -1,4 +1,4 @@ -#include "Viewport/SceneViewportRenderPassBundle.h" +#include "Product/Rendering/Viewport/SceneViewportRenderPassBundle.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.h b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.h similarity index 67% rename from editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.h rename to editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.h index a3e0ae41..9ce9c346 100644 --- a/editor/app/Rendering/Viewport/SceneViewportRenderPassBundle.h +++ b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPassBundle.h @@ -1,10 +1,10 @@ #pragma once -#include "Viewport/Passes/SceneViewportGridPass.h" -#include "Viewport/Passes/SceneViewportSelectionOutlinePass.h" -#include "Viewport/Passes/SceneViewportSelectedHelpersPass.h" -#include "Viewport/SceneViewportRenderPlan.h" -#include "Viewport/SceneViewportResourcePaths.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportGridPass.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportSelectionOutlinePass.h" +#include "Product/Rendering/Viewport/Passes/SceneViewportSelectedHelpersPass.h" +#include "Product/Rendering/Viewport/SceneViewportRenderPlan.h" +#include "Product/Rendering/Viewport/SceneViewportResourcePaths.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Rendering/Viewport/SceneViewportRenderPlan.h b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPlan.h similarity index 97% rename from editor/app/Rendering/Viewport/SceneViewportRenderPlan.h rename to editor/src/Product/Rendering/Viewport/SceneViewportRenderPlan.h index 483bf525..62c2db4a 100644 --- a/editor/app/Rendering/Viewport/SceneViewportRenderPlan.h +++ b/editor/src/Product/Rendering/Viewport/SceneViewportRenderPlan.h @@ -1,8 +1,8 @@ #pragma once -#include "Scene/SceneViewportRenderRequest.h" -#include "Viewport/SceneViewportPassSpecs.h" -#include "Viewport/ViewportRenderTargets.h" +#include "Product/Core/Scene/SceneViewportRenderRequest.h" +#include "Product/Rendering/Viewport/SceneViewportPassSpecs.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include #include diff --git a/editor/app/Rendering/Viewport/SceneViewportRenderService.cpp b/editor/src/Product/Rendering/Viewport/SceneViewportRenderService.cpp similarity index 96% rename from editor/app/Rendering/Viewport/SceneViewportRenderService.cpp rename to editor/src/Product/Rendering/Viewport/SceneViewportRenderService.cpp index 4ff4be93..f7bbdd25 100644 --- a/editor/app/Rendering/Viewport/SceneViewportRenderService.cpp +++ b/editor/src/Product/Rendering/Viewport/SceneViewportRenderService.cpp @@ -1,8 +1,8 @@ -#include "Viewport/SceneViewportRenderService.h" +#include "Product/Rendering/Viewport/SceneViewportRenderService.h" -#include "Engine/EditorShaderProvider.h" -#include "Engine/SceneViewportEngineBridge.h" -#include "Viewport/ViewportObjectIdPicker.h" +#include "Product/Core/Engine/EditorShaderProvider.h" +#include "Product/Core/Engine/SceneViewportEngineBridge.h" +#include "Product/Rendering/Viewport/ViewportObjectIdPicker.h" #include diff --git a/editor/app/Rendering/Viewport/SceneViewportRenderService.h b/editor/src/Product/Rendering/Viewport/SceneViewportRenderService.h similarity index 90% rename from editor/app/Rendering/Viewport/SceneViewportRenderService.h rename to editor/src/Product/Rendering/Viewport/SceneViewportRenderService.h index 63fbfebe..4ff17e0f 100644 --- a/editor/app/Rendering/Viewport/SceneViewportRenderService.h +++ b/editor/src/Product/Rendering/Viewport/SceneViewportRenderService.h @@ -1,8 +1,8 @@ #pragma once -#include "Viewport/EditorViewportRuntimeServices.h" -#include "Viewport/ViewportContentRenderer.h" -#include "Viewport/SceneViewportRenderPassBundle.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" +#include "Product/Rendering/Viewport/ViewportContentRenderer.h" +#include "Product/Rendering/Viewport/SceneViewportRenderPassBundle.h" #include #include diff --git a/editor/app/Rendering/Viewport/SceneViewportResourcePaths.h b/editor/src/Product/Rendering/Viewport/SceneViewportResourcePaths.h similarity index 97% rename from editor/app/Rendering/Viewport/SceneViewportResourcePaths.h rename to editor/src/Product/Rendering/Viewport/SceneViewportResourcePaths.h index f9579d45..3e698d96 100644 --- a/editor/app/Rendering/Viewport/SceneViewportResourcePaths.h +++ b/editor/src/Product/Rendering/Viewport/SceneViewportResourcePaths.h @@ -1,6 +1,6 @@ #pragma once -#include "Environment/EditorRuntimePaths.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" #include diff --git a/editor/app/Rendering/Viewport/ViewportContentRenderer.h b/editor/src/Product/Rendering/Viewport/ViewportContentRenderer.h similarity index 92% rename from editor/app/Rendering/Viewport/ViewportContentRenderer.h rename to editor/src/Product/Rendering/Viewport/ViewportContentRenderer.h index 2133ec6c..f106ae35 100644 --- a/editor/app/Rendering/Viewport/ViewportContentRenderer.h +++ b/editor/src/Product/Rendering/Viewport/ViewportContentRenderer.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/ViewportRenderTargets.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include #include diff --git a/editor/app/Rendering/Viewport/ViewportHostService.cpp b/editor/src/Product/Rendering/Viewport/ViewportHostService.cpp similarity index 93% rename from editor/app/Rendering/Viewport/ViewportHostService.cpp rename to editor/src/Product/Rendering/Viewport/ViewportHostService.cpp index 7aa88a1a..c7621139 100644 --- a/editor/app/Rendering/Viewport/ViewportHostService.cpp +++ b/editor/src/Product/Rendering/Viewport/ViewportHostService.cpp @@ -1,7 +1,7 @@ -#include "ViewportHostService.h" +#include "Product/Rendering/Viewport/ViewportHostService.h" -#include "Product/EditorProductManifest.h" -#include "Viewport/SceneViewportResourcePaths.h" +#include "Product/Registry/EditorProductRegistry.h" +#include "Product/Rendering/Viewport/SceneViewportResourcePaths.h" #include "ViewportRenderHost.h" #include @@ -56,26 +56,26 @@ void ViewportHostService::Initialize( sceneViewportEngineBridge, shaderProvider); m_placeholderRenderers.clear(); - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - if (panel.presentationKind != UIEditorPanelPresentationKind::ViewportShell) { + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + if (feature.presentationKind != UIEditorPanelPresentationKind::ViewportShell) { continue; } - switch (panel.viewportKind) { + switch (feature.viewportKind) { case EditorProductViewportKind::Scene: - BindSceneViewport(panel.panelId); + BindSceneViewport(feature.panelId); break; case EditorProductViewportKind::Game: - BindGameViewport(panel.panelId); + BindGameViewport(feature.panelId); break; case EditorProductViewportKind::Placeholder: BindPlaceholderViewport( - panel.panelId, - panel.viewportPlaceholderStatus); + feature.panelId, + feature.viewportPlaceholderStatus); break; case EditorProductViewportKind::None: default: - BindNullViewport(panel.panelId); + BindNullViewport(feature.panelId); break; } } @@ -140,9 +140,9 @@ void ViewportHostService::BindPlaceholderViewport( } void ViewportHostService::Shutdown() { - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - if (panel.presentationKind == UIEditorPanelPresentationKind::ViewportShell) { - SetContentRenderer(panel.panelId, nullptr, {}); + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + if (feature.presentationKind == UIEditorPanelPresentationKind::ViewportShell) { + SetContentRenderer(feature.panelId, nullptr, {}); } } m_placeholderRenderers.clear(); diff --git a/editor/app/Rendering/Viewport/ViewportHostService.h b/editor/src/Product/Rendering/Viewport/ViewportHostService.h similarity index 91% rename from editor/app/Rendering/Viewport/ViewportHostService.h rename to editor/src/Product/Rendering/Viewport/ViewportHostService.h index ad0a32af..9633f67d 100644 --- a/editor/app/Rendering/Viewport/ViewportHostService.h +++ b/editor/src/Product/Rendering/Viewport/ViewportHostService.h @@ -1,11 +1,11 @@ #pragma once -#include "Viewport/EditorViewportRuntimeServices.h" -#include "Viewport/GameViewportRenderService.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" +#include "Product/Rendering/Viewport/GameViewportRenderService.h" #include "HostFwd.h" -#include "Viewport/SceneViewportRenderService.h" -#include "Viewport/ViewportContentRenderer.h" -#include "ViewportRenderTargets.h" +#include "Product/Rendering/Viewport/SceneViewportRenderService.h" +#include "Product/Rendering/Viewport/ViewportContentRenderer.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include #include diff --git a/editor/app/Rendering/Viewport/ViewportObjectIdPicker.h b/editor/src/Product/Rendering/Viewport/ViewportObjectIdPicker.h similarity index 98% rename from editor/app/Rendering/Viewport/ViewportObjectIdPicker.h rename to editor/src/Product/Rendering/Viewport/ViewportObjectIdPicker.h index 02d91d84..e6f9d936 100644 --- a/editor/app/Rendering/Viewport/ViewportObjectIdPicker.h +++ b/editor/src/Product/Rendering/Viewport/ViewportObjectIdPicker.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/EditorViewportPicking.h" +#include "Product/Core/Viewport/EditorViewportPicking.h" #include #include diff --git a/editor/src/Product/Rendering/Viewport/ViewportObjectPickerService.h b/editor/src/Product/Rendering/Viewport/ViewportObjectPickerService.h new file mode 100644 index 00000000..9e7b8d99 --- /dev/null +++ b/editor/src/Product/Rendering/Viewport/ViewportObjectPickerService.h @@ -0,0 +1,3 @@ +#pragma once + +#include "Product/Core/Viewport/EditorViewportPicking.h" diff --git a/editor/app/Rendering/Viewport/ViewportRenderTargetUtils.cpp b/editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.cpp similarity index 97% rename from editor/app/Rendering/Viewport/ViewportRenderTargetUtils.cpp rename to editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.cpp index 3250cc45..3d347bad 100644 --- a/editor/app/Rendering/Viewport/ViewportRenderTargetUtils.cpp +++ b/editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.cpp @@ -1,4 +1,4 @@ -#include "Viewport/ViewportRenderTargetUtils.h" +#include "Product/Rendering/Viewport/ViewportRenderTargetUtils.h" #include #include diff --git a/editor/app/Rendering/Viewport/ViewportRenderTargetUtils.h b/editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.h similarity index 97% rename from editor/app/Rendering/Viewport/ViewportRenderTargetUtils.h rename to editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.h index b614beee..5077759b 100644 --- a/editor/app/Rendering/Viewport/ViewportRenderTargetUtils.h +++ b/editor/src/Product/Rendering/Viewport/ViewportRenderTargetUtils.h @@ -1,6 +1,6 @@ #pragma once -#include "ViewportTypes.h" +#include "Product/Rendering/Viewport/ViewportTypes.h" #include #include diff --git a/editor/app/Rendering/Viewport/ViewportRenderTargets.cpp b/editor/src/Product/Rendering/Viewport/ViewportRenderTargets.cpp similarity index 99% rename from editor/app/Rendering/Viewport/ViewportRenderTargets.cpp rename to editor/src/Product/Rendering/Viewport/ViewportRenderTargets.cpp index 69aa7fc1..5d10dc43 100644 --- a/editor/app/Rendering/Viewport/ViewportRenderTargets.cpp +++ b/editor/src/Product/Rendering/Viewport/ViewportRenderTargets.cpp @@ -1,4 +1,4 @@ -#include "Viewport/ViewportRenderTargets.h" +#include "Product/Rendering/Viewport/ViewportRenderTargets.h" #include "ViewportRenderHost.h" diff --git a/editor/app/Rendering/Viewport/ViewportRenderTargets.h b/editor/src/Product/Rendering/Viewport/ViewportRenderTargets.h similarity index 97% rename from editor/app/Rendering/Viewport/ViewportRenderTargets.h rename to editor/src/Product/Rendering/Viewport/ViewportRenderTargets.h index f8ab97bb..9c9f6f5f 100644 --- a/editor/app/Rendering/Viewport/ViewportRenderTargets.h +++ b/editor/src/Product/Rendering/Viewport/ViewportRenderTargets.h @@ -1,7 +1,7 @@ #pragma once #include "HostFwd.h" -#include "ViewportRenderTargetUtils.h" +#include "Product/Rendering/Viewport/ViewportRenderTargetUtils.h" #include #include diff --git a/editor/app/Rendering/Viewport/ViewportTypes.h b/editor/src/Product/Rendering/Viewport/ViewportTypes.h similarity index 79% rename from editor/app/Rendering/Viewport/ViewportTypes.h rename to editor/src/Product/Rendering/Viewport/ViewportTypes.h index c0e292bd..27d45fef 100644 --- a/editor/app/Rendering/Viewport/ViewportTypes.h +++ b/editor/src/Product/Rendering/Viewport/ViewportTypes.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/EditorViewportTypes.h" +#include "Product/Core/Viewport/EditorViewportTypes.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Composition/EditorFrameStatusController.cpp b/editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.cpp similarity index 74% rename from editor/app/Composition/EditorFrameStatusController.cpp rename to editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.cpp index 1978cebd..b7b6727c 100644 --- a/editor/app/Composition/EditorFrameStatusController.cpp +++ b/editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.cpp @@ -1,4 +1,6 @@ -#include "EditorFrameStatusController.h" +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" + +#include "Product/Commands/EditorCommand.h" #include #include @@ -10,8 +12,6 @@ namespace XCEngine::UI::Editor::App { namespace { -constexpr std::size_t kMaxConsoleEntries = 256u; - std::string ResolveViewportStatusMessage( const UIEditorShellInteractionResult& result) { if (result.viewportInputFrame.captureStarted) { @@ -45,46 +45,55 @@ std::string ResolveViewportStatusMessage( } // namespace void EditorFrameStatusController::Bind( - EditorSession& session, - std::vector& consoleEntries, + Product::EditorStore& store, SessionSyncCallback syncSessionProjection) { - m_session = &session; - m_consoleEntries = &consoleEntries; + m_store = &store; m_syncSessionProjection = std::move(syncSessionProjection); - SyncSessionConsoleProjection(); } void EditorFrameStatusController::Reset() { - m_lastStatus.clear(); - m_lastMessage.clear(); - SyncSessionConsoleProjection(); + if (m_store == nullptr) { + return; + } + + m_store->Dispatch(Product::EditorCommand::ClearStatus()); } void EditorFrameStatusController::SetStatus( std::string status, std::string message) { - if (m_lastStatus != status || m_lastMessage != message) { - AppendConsoleEntry(status, message); + if (m_store == nullptr) { + return; } - m_lastStatus = std::move(status); - m_lastMessage = std::move(message); + m_store->Dispatch(Product::EditorCommand::SetStatus( + std::move(status), + std::move(message))); } void EditorFrameStatusController::SetReadyStatus() { - SetStatus("Ready", "Application shell loaded."); + if (m_store == nullptr) { + return; + } + + m_store->Dispatch(Product::EditorCommand::SetReadyStatus()); } std::string EditorFrameStatusController::ComposeStatusText() const { - if (m_lastStatus.empty()) { - return m_lastMessage; + if (m_store == nullptr) { + return {}; } - if (m_lastMessage.empty()) { - return m_lastStatus; + const Product::EditorStatusState& statusState = m_store->GetState().status; + if (statusState.channel.empty()) { + return statusState.message; } - return m_lastStatus + ": " + m_lastMessage; + if (statusState.message.empty()) { + return statusState.channel; + } + + return statusState.channel + ": " + statusState.message; } void EditorFrameStatusController::UpdateStatusFromShellResult( @@ -160,15 +169,15 @@ std::string EditorFrameStatusController::DescribeWorkspaceState( return stream.str(); } -std::vector EditorFrameStatusController::SyncWorkspacePanelFrameEvents( - const std::vector& panelEvents) { +std::vector EditorFrameStatusController::SyncWorkspaceContentFrameEvents( + const std::vector& contentEvents) { if (m_syncSessionProjection) { m_syncSessionProjection(); } std::vector entries = {}; - entries.reserve(panelEvents.size()); - for (const EditorWorkspacePanelFrameEvent& event : panelEvents) { + entries.reserve(contentEvents.size()); + for (const EditorWorkspaceHostedContentFrameEvent& event : contentEvents) { SetStatus(event.status, event.message); entries.push_back(WorkspaceTraceEntry{ .channel = event.traceChannel, @@ -179,29 +188,4 @@ std::vector EditorFrameStatusController::SyncWorkspacePanel return entries; } -void EditorFrameStatusController::AppendConsoleEntry( - std::string channel, - std::string message) { - if (m_consoleEntries == nullptr) { - return; - } - - EditorConsoleEntry entry = {}; - entry.channel = std::move(channel); - entry.message = std::move(message); - m_consoleEntries->push_back(std::move(entry)); - if (m_consoleEntries->size() > kMaxConsoleEntries) { - m_consoleEntries->erase(m_consoleEntries->begin()); - } - SyncSessionConsoleProjection(); -} - -void EditorFrameStatusController::SyncSessionConsoleProjection() { - if (m_session == nullptr || m_consoleEntries == nullptr) { - return; - } - - m_session->consoleEntries = *m_consoleEntries; -} - } // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.h b/editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.h new file mode 100644 index 00000000..8c9fefdb --- /dev/null +++ b/editor/src/Product/Runtime/Diagnostics/EditorFrameStatusController.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Product/Runtime/Store/EditorStore.h" +#include "Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h" +#include "Product/Runtime/Diagnostics/WorkspaceTraceEntry.h" + +#include +#include + +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorFrameStatusController final { +public: + using SessionSyncCallback = std::function; + + void Bind( + Product::EditorStore& store, + SessionSyncCallback syncSessionProjection); + void Reset(); + + void SetStatus(std::string status, std::string message); + void SetReadyStatus(); + std::string ComposeStatusText() const; + void UpdateStatusFromShellResult( + const UIEditorWorkspaceController& workspaceController, + const UIEditorShellInteractionResult& result); + std::string DescribeWorkspaceState( + const UIEditorWorkspaceController& workspaceController, + const UIEditorShellInteractionState& interactionState) const; + std::vector SyncWorkspaceContentFrameEvents( + const std::vector& contentEvents); + +private: + Product::EditorStore* m_store = nullptr; + SessionSyncCallback m_syncSessionProjection = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Diagnostics/WorkspaceTraceEntry.h b/editor/src/Product/Runtime/Diagnostics/WorkspaceTraceEntry.h new file mode 100644 index 00000000..a04ed64a --- /dev/null +++ b/editor/src/Product/Runtime/Diagnostics/WorkspaceTraceEntry.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace XCEngine::UI::Editor::App { + +struct WorkspaceTraceEntry { + std::string channel = {}; + std::string message = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/EditorProductRuntime.cpp b/editor/src/Product/Runtime/EditorProductRuntime.cpp new file mode 100644 index 00000000..8cda7d0a --- /dev/null +++ b/editor/src/Product/Runtime/EditorProductRuntime.cpp @@ -0,0 +1,244 @@ +#include "Product/Runtime/EditorProductRuntime.h" +#include "Product/Core/Engine/EditorSceneBackendFactory.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" + +#include "Product/Commands/EditorCommand.h" + +#include + +#include + +namespace XCEngine::UI::Editor::App { + +using ::XCEngine::UI::Editor::AppendUIEditorRuntimeTrace; + +namespace { + +UIEditorWindowWorkspaceSet BuildInitialWindowWorkspaceSet( + const EditorShellAsset& shellAsset, + std::string_view primaryWindowId) { + UIEditorWindowWorkspaceSet windowWorkspace = {}; + windowWorkspace.primaryWindowId = std::string(primaryWindowId); + windowWorkspace.activeWindowId = windowWorkspace.primaryWindowId; + + UIEditorWindowWorkspaceState primaryWindow = {}; + primaryWindow.windowId = windowWorkspace.primaryWindowId; + primaryWindow.workspace = CanonicalizeUIEditorWorkspaceModel(shellAsset.workspace); + primaryWindow.session = shellAsset.workspaceSession; + windowWorkspace.windows.push_back(std::move(primaryWindow)); + return windowWorkspace; +} + +} // namespace + +bool EditorProductRuntime::Initialize( + const EditorRuntimePaths& runtimePaths, + EditorSceneBackendFactory& sceneBackendFactory) { + m_valid = false; + m_validationMessage.clear(); + AppendUIEditorRuntimeTrace("startup", "EditorProductRuntime::Initialize begin"); + if (!m_shellDefinitionService.Initialize(runtimePaths)) { + m_validationMessage = m_shellDefinitionService.GetValidationMessage(); + AppendUIEditorRuntimeTrace("startup", m_validationMessage); + return false; + } + + Product::EditorState initialState = {}; + initialState.session.workspaceRoot = runtimePaths.workspaceRoot; + initialState.session.projectRoot = runtimePaths.projectRoot; + initialState.windowWorkspace = + BuildInitialWindowWorkspaceSet(m_shellDefinitionService.GetShellAsset(), "main"); + m_store.Reset(std::move(initialState)); + m_selectionService = {}; + m_projectRuntime.Reset(); + AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize begin"); + m_projectRuntime.Initialize(runtimePaths.projectRoot); + AppendUIEditorRuntimeTrace("startup", "EditorProjectRuntime::Initialize end"); + m_projectRuntime.BindSelectionService(&m_selectionService); + m_sceneRuntime.SetBackend(sceneBackendFactory.CreateSceneBackend()); + AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize begin"); + const EditorStartupSceneResult startupScene = + m_sceneRuntime.Initialize(m_store.GetState().session.projectRoot); + if (!startupScene.ready) { + m_validationMessage = "Editor scene runtime failed to initialize."; + AppendUIEditorRuntimeTrace("startup", m_validationMessage); + return false; + } + AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize end"); + m_sceneRuntime.BindSelectionService(&m_selectionService); + m_runtimeCommandService.Initialize( + m_store.GetMutableSession(), + m_sceneRuntime, + runtimePaths, + startupScene); + ResetEditorColorPickerToolState(m_colorPickerToolState); + SyncSessionFromSelectionService(); + m_frameStatusController.Bind( + m_store, + [this]() { + SyncSessionFromSelectionService(); + }); + m_frameStatusController.Reset(); + m_frameStatusController.SetReadyStatus(); + m_valid = true; + AppendUIEditorRuntimeTrace("startup", "EditorProductRuntime::Initialize end"); + return true; +} + +void EditorProductRuntime::TickEditorRuntime() { + m_runtimeCommandService.TickFrame(); +} + +bool EditorProductRuntime::IsValid() const { + return m_valid; +} + +const std::string& EditorProductRuntime::GetValidationMessage() const { + return m_validationMessage; +} + +const EditorShellAsset& EditorProductRuntime::GetShellAsset() const { + return m_shellDefinitionService.GetShellAsset(); +} + +const EditorSession& EditorProductRuntime::GetSession() const { + return m_store.GetState().session; +} + +const std::vector& EditorProductRuntime::GetConsoleEntries() const { + return m_store.GetState().consoleEntries; +} + +EditorProjectRuntime& EditorProductRuntime::GetProjectRuntime() { + return m_projectRuntime; +} + +const EditorProjectRuntime& EditorProductRuntime::GetProjectRuntime() const { + return m_projectRuntime; +} + +EditorSceneRuntime& EditorProductRuntime::GetSceneRuntime() { + return m_sceneRuntime; +} + +const EditorSceneRuntime& EditorProductRuntime::GetSceneRuntime() const { + return m_sceneRuntime; +} + +const UIEditorPanelRegistry& EditorProductRuntime::GetPanelRegistry() const { + return m_shellDefinitionService.GetShellAsset().panelRegistry; +} + +const UIEditorWindowWorkspaceSet& EditorProductRuntime::GetWindowWorkspace() const { + return m_store.GetWindowWorkspace(); +} + +const UIEditorWindowWorkspaceState* EditorProductRuntime::FindWindowWorkspaceState( + std::string_view windowId) const { + return m_store.FindWindowWorkspaceState(windowId); +} + +bool EditorProductRuntime::IsPrimaryWorkspaceWindow(std::string_view windowId) const { + return !windowId.empty() && + m_store.GetWindowWorkspace().primaryWindowId == windowId; +} + +void EditorProductRuntime::CommitWindowWorkspaceSet( + UIEditorWindowWorkspaceSet windowWorkspace) { + m_store.ReplaceWindowWorkspace(std::move(windowWorkspace)); +} + +bool EditorProductRuntime::MutateWindowWorkspaceState( + std::string_view windowId, + UIEditorWorkspaceController& workspaceController, + const std::function& mutator, + std::string& outError) { + if (windowId.empty()) { + outError = "workspace mutation missing window id"; + return false; + } + + if (!mutator) { + outError = "workspace mutation handler is unavailable"; + return false; + } + + const bool mutated = m_store.MutateWindowWorkspaceState( + windowId, + [&workspaceController, &mutator](UIEditorWindowWorkspaceState& windowState) { + workspaceController.Rebind( + windowState.workspace, + windowState.session); + mutator(workspaceController); + }); + if (!mutated) { + outError = + "workspace mutation references unknown authoritative window '" + + std::string(windowId) + "'"; + return false; + } + + outError.clear(); + return true; +} + +EditorRuntimeCommandService& EditorProductRuntime::GetRuntimeCommandService() { + return m_runtimeCommandService; +} + +const EditorRuntimeCommandService& EditorProductRuntime::GetRuntimeCommandService() const { + return m_runtimeCommandService; +} + +EditorColorPickerToolState& EditorProductRuntime::GetColorPickerToolState() { + return m_colorPickerToolState; +} + +const EditorColorPickerToolState& EditorProductRuntime::GetColorPickerToolState() const { + return m_colorPickerToolState; +} + +void EditorProductRuntime::RequestOpenUtilityWindow(EditorUtilityWindowKind kind) { + m_store.Dispatch(Product::EditorCommand::RequestOpenUtilityWindow(kind)); +} + +std::optional EditorProductRuntime::ConsumeOpenUtilityWindowRequest() { + return m_store.ConsumePendingUtilityWindowRequest(); +} + +void EditorProductRuntime::SyncSessionFromSelectionService() { + m_store.Dispatch( + Product::EditorCommand::SetSelection(m_selectionService.GetSelection())); +} + +bool EditorProductRuntime::RequestOpenSceneAsset(const std::filesystem::path& scenePath) { + const bool opened = m_runtimeCommandService.RequestOpenSceneAsset(scenePath); + m_frameStatusController.SetStatus( + "Scene", + opened + ? m_runtimeCommandService.GetLastMessage() + : m_runtimeCommandService.GetLastMessage().empty() + ? std::string("Failed to open scene asset.") + : m_runtimeCommandService.GetLastMessage()); + SyncSessionFromSelectionService(); + return opened; +} + +EditorShellDefinitionService& EditorProductRuntime::GetShellDefinitionService() { + return m_shellDefinitionService; +} + +const EditorShellDefinitionService& EditorProductRuntime::GetShellDefinitionService() const { + return m_shellDefinitionService; +} + +EditorFrameStatusController& EditorProductRuntime::GetFrameStatusController() { + return m_frameStatusController; +} + +const EditorFrameStatusController& EditorProductRuntime::GetFrameStatusController() const { + return m_frameStatusController; +} + +} // namespace XCEngine::UI::Editor::App + diff --git a/editor/src/Product/Runtime/EditorProductRuntime.h b/editor/src/Product/Runtime/EditorProductRuntime.h new file mode 100644 index 00000000..5704ef83 --- /dev/null +++ b/editor/src/Product/Runtime/EditorProductRuntime.h @@ -0,0 +1,84 @@ +#pragma once + +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/Services/Project/EditorProjectRuntime.h" +#include "Product/Services/Runtime/EditorRuntimeCommandService.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" +#include "Product/Runtime/Shell/EditorShellDefinitionService.h" +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" +#include "Product/Runtime/Store/EditorStore.h" +#include "Product/State/EditorColorPickerToolState.h" +#include "Product/State/EditorSelectionService.h" +#include "Product/State/EditorSession.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorSceneBackendFactory; + +class EditorProductRuntime { +public: + bool Initialize( + const EditorRuntimePaths& runtimePaths, + EditorSceneBackendFactory& sceneBackendFactory); + + bool IsValid() const; + const std::string& GetValidationMessage() const; + + const EditorSession& GetSession() const; + const std::vector& GetConsoleEntries() const; + EditorProjectRuntime& GetProjectRuntime(); + const EditorProjectRuntime& GetProjectRuntime() const; + EditorSceneRuntime& GetSceneRuntime(); + const EditorSceneRuntime& GetSceneRuntime() const; + const UIEditorPanelRegistry& GetPanelRegistry() const; + const UIEditorWindowWorkspaceSet& GetWindowWorkspace() const; + const UIEditorWindowWorkspaceState* FindWindowWorkspaceState( + std::string_view windowId) const; + bool IsPrimaryWorkspaceWindow(std::string_view windowId) const; + void CommitWindowWorkspaceSet(UIEditorWindowWorkspaceSet windowWorkspace); + bool MutateWindowWorkspaceState( + std::string_view windowId, + UIEditorWorkspaceController& workspaceController, + const std::function& mutator, + std::string& outError); + EditorColorPickerToolState& GetColorPickerToolState(); + const EditorColorPickerToolState& GetColorPickerToolState() const; + const EditorShellAsset& GetShellAsset() const; + EditorRuntimeCommandService& GetRuntimeCommandService(); + const EditorRuntimeCommandService& GetRuntimeCommandService() const; + EditorShellDefinitionService& GetShellDefinitionService(); + const EditorShellDefinitionService& GetShellDefinitionService() const; + EditorFrameStatusController& GetFrameStatusController(); + const EditorFrameStatusController& GetFrameStatusController() const; + + void RequestOpenUtilityWindow(EditorUtilityWindowKind kind); + std::optional ConsumeOpenUtilityWindowRequest(); + bool RequestOpenSceneAsset(const std::filesystem::path& scenePath); + void TickEditorRuntime(); + +private: + void SyncSessionFromSelectionService(); + + EditorShellDefinitionService m_shellDefinitionService = {}; + EditorFrameStatusController m_frameStatusController = {}; + Product::EditorStore m_store = {}; + EditorSelectionService m_selectionService = {}; + EditorProjectRuntime m_projectRuntime = {}; + EditorSceneRuntime m_sceneRuntime = {}; + EditorRuntimeCommandService m_runtimeCommandService = {}; + EditorColorPickerToolState m_colorPickerToolState = {}; + bool m_valid = false; + std::string m_validationMessage = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Features/EditorFeatureComposition.cpp b/editor/src/Product/Runtime/Features/EditorFeatureComposition.cpp new file mode 100644 index 00000000..c0d3997d --- /dev/null +++ b/editor/src/Product/Runtime/Features/EditorFeatureComposition.cpp @@ -0,0 +1,89 @@ +#include "Product/Runtime/Features/EditorFeatureComposition.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +UIEditorWorkspacePanelPresentationModel* FindMutablePresentation( + std::vector& presentations, + std::string_view panelId) { + for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) { + if (presentation.panelId == panelId) { + return &presentation; + } + } + + return nullptr; +} + +} // namespace + +void ApplyEditorWorkspaceFeaturePresentationCustomizations( + std::vector& presentations) { + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + if (feature.customizePresentation == nullptr) { + continue; + } + + if (UIEditorWorkspacePanelPresentationModel* presentation = + FindMutablePresentation(presentations, feature.panelId); + presentation != nullptr) { + feature.customizePresentation(*presentation); + } + } +} + +EditorWorkspaceHostedContentSet CreateEditorWorkspaceFeatureContentSet( + const std::vector& consoleEntries, + EditorProjectRuntime& projectRuntime, + EditorSceneRuntime& sceneRuntime, + EditorColorPickerToolState& colorPickerToolState, + ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, + RequestOpenUtilityWindowCallback requestOpenUtilityWindow, + RequestOpenSceneAssetCallback requestOpenSceneAsset) { + EditorWorkspaceHostedContentSet contentSet = {}; + EditorCommandFocusService& commandFocusService = + contentSet.GetCommandFocusService(); + const EditorWorkspaceFeatureContext context = { + .consoleEntries = consoleEntries, + .commandFocusService = commandFocusService, + .projectRuntime = projectRuntime, + .sceneRuntime = sceneRuntime, + .colorPickerToolState = colorPickerToolState, + .systemInteractionHost = systemInteractionHost, + .requestOpenUtilityWindow = requestOpenUtilityWindow, + .requestOpenSceneAsset = requestOpenSceneAsset, + }; + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + if (feature.createContent == nullptr) { + continue; + } + + if (std::unique_ptr content = + feature.createContent(feature, context); + content != nullptr) { + contentSet.AddContent(std::move(content)); + } + } + return contentSet; +} + +std::unique_ptr CreateEditorUtilityFeatureContent( + EditorUtilityWindowKind kind, + EditorColorPickerToolState& colorPickerToolState, + EditorSceneRuntime& sceneRuntime) { + const EditorUtilityFeatureDescriptor* feature = FindEditorUtilityFeature(kind); + if (feature == nullptr || feature->createContent == nullptr) { + return nullptr; + } + + const EditorUtilityFeatureContext context = { + .colorPickerToolState = colorPickerToolState, + .sceneRuntime = sceneRuntime, + }; + return feature->createContent(*feature, context); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Features/EditorFeatureComposition.h b/editor/src/Product/Runtime/Features/EditorFeatureComposition.h new file mode 100644 index 00000000..3ae1d77b --- /dev/null +++ b/editor/src/Product/Runtime/Features/EditorFeatureComposition.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h" +#include "Product/Registry/EditorProductRegistry.h" + +#include +#include + +namespace XCEngine::UI::Editor::App { + +struct EditorWorkspaceFeatureContext { + const std::vector& consoleEntries; + EditorCommandFocusService& commandFocusService; + EditorProjectRuntime& projectRuntime; + EditorSceneRuntime& sceneRuntime; + EditorColorPickerToolState& colorPickerToolState; + ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost = + nullptr; + const RequestOpenUtilityWindowCallback& requestOpenUtilityWindow; + const RequestOpenSceneAssetCallback& requestOpenSceneAsset; +}; + +struct EditorUtilityFeatureContext { + EditorColorPickerToolState& colorPickerToolState; + EditorSceneRuntime& sceneRuntime; +}; + +void ApplyEditorWorkspaceFeaturePresentationCustomizations( + std::vector& presentations); + +EditorWorkspaceHostedContentSet CreateEditorWorkspaceFeatureContentSet( + const std::vector& consoleEntries, + EditorProjectRuntime& projectRuntime, + EditorSceneRuntime& sceneRuntime, + EditorColorPickerToolState& colorPickerToolState, + ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, + RequestOpenUtilityWindowCallback requestOpenUtilityWindow, + RequestOpenSceneAssetCallback requestOpenSceneAsset); + +std::unique_ptr CreateEditorUtilityFeatureContent( + EditorUtilityWindowKind kind, + EditorColorPickerToolState& colorPickerToolState, + EditorSceneRuntime& sceneRuntime); + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellAssetBuilder.cpp b/editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.cpp similarity index 88% rename from editor/app/Composition/EditorShellAssetBuilder.cpp rename to editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.cpp index 17e58042..9053573c 100644 --- a/editor/app/Composition/EditorShellAssetBuilder.cpp +++ b/editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.cpp @@ -1,13 +1,18 @@ -#include "EditorShellAssetBuilder.h" +#include "Product/Runtime/Shell/EditorShellAssetBuilder.h" +#include "Product/Features/Workspace/Console/ConsoleWorkspaceFeature.h" +#include "Product/Features/Workspace/Game/GameWorkspaceFeature.h" +#include "Product/Features/Workspace/Hierarchy/HierarchyWorkspaceFeature.h" +#include "Product/Features/Workspace/Inspector/InspectorWorkspaceFeature.h" +#include "Product/Features/Workspace/Project/ProjectWorkspaceFeature.h" +#include "Product/Features/Workspace/Scene/SceneWorkspaceFeature.h" +#include "Product/Registry/EditorProductRegistry.h" #include #include #include #include #include -#include "Panels/EditorPanelIds.h" -#include "Product/EditorProductManifest.h" #include -#include "Assets/EditorIconService.h" +#include "Product/Core/Assets/EditorIconService.h" namespace XCEngine::UI::Editor::App { @@ -194,13 +199,13 @@ UIEditorCommandRegistry BuildEditorCommandRegistry() { "Reset Layout", UIEditorWorkspaceCommandKind::ResetWorkspace) }; - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { registry.commands.push_back( BuildWorkspaceCommand( - BuildPanelActivationCommandId(panel.panelId), - std::string(panel.defaultTitle), + BuildPanelActivationCommandId(feature.panelId), + std::string(feature.defaultTitle), UIEditorWorkspaceCommandKind::ActivatePanel, - std::string(panel.panelId))); + std::string(feature.panelId))); } return registry; } @@ -233,21 +238,21 @@ namespace XCEngine::UI::Editor::App { namespace { -UIEditorPanelDescriptor BuildProductPanelDescriptor( - const EditorProductPanelDescriptor& productPanel) { +UIEditorPanelDescriptor BuildWorkspaceFeaturePanelDescriptor( + const EditorWorkspaceFeatureDescriptor& feature) { UIEditorPanelDescriptor descriptor = {}; - descriptor.panelId = std::string(productPanel.panelId); - descriptor.defaultTitle = std::string(productPanel.defaultTitle); - descriptor.presentationKind = productPanel.presentationKind; - descriptor.placeholder = productPanel.placeholder; - descriptor.canHide = productPanel.canHide; - descriptor.canClose = productPanel.canClose; + descriptor.panelId = std::string(feature.panelId); + descriptor.defaultTitle = std::string(feature.defaultTitle); + descriptor.presentationKind = feature.presentationKind; + descriptor.placeholder = feature.placeholder; + descriptor.canHide = feature.canHide; + descriptor.canClose = feature.canClose; if (descriptor.presentationKind == UIEditorPanelPresentationKind::ViewportShell) { descriptor.viewportShellSpec.chrome.title = descriptor.defaultTitle; descriptor.viewportShellSpec.chrome.showTopBar = - productPanel.showViewportTopBar; + feature.showViewportTopBar; descriptor.viewportShellSpec.chrome.showBottomBar = - productPanel.showViewportBottomBar; + feature.showViewportBottomBar; } return descriptor; } @@ -269,8 +274,8 @@ const UIEditorPanelDescriptor& RequirePanelDescriptor( UIEditorPanelRegistry BuildEditorPanelRegistry() { UIEditorPanelRegistry registry = {}; - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { - registry.panels.push_back(BuildProductPanelDescriptor(panel)); + for (const EditorWorkspaceFeatureDescriptor& feature : GetEditorWorkspaceFeatures()) { + registry.panels.push_back(BuildWorkspaceFeaturePanelDescriptor(feature)); } return registry; } @@ -278,17 +283,17 @@ UIEditorPanelRegistry BuildEditorPanelRegistry() { UIEditorWorkspaceModel BuildEditorWorkspaceModel( const UIEditorPanelRegistry& panelRegistry) { const UIEditorPanelDescriptor& hierarchy = - RequirePanelDescriptor(panelRegistry, kHierarchyPanelId); + RequirePanelDescriptor(panelRegistry, kHierarchyWorkspaceFeaturePanelId); const UIEditorPanelDescriptor& scene = - RequirePanelDescriptor(panelRegistry, kScenePanelId); + RequirePanelDescriptor(panelRegistry, kSceneWorkspaceFeaturePanelId); const UIEditorPanelDescriptor& game = - RequirePanelDescriptor(panelRegistry, kGamePanelId); + RequirePanelDescriptor(panelRegistry, kGameWorkspaceFeaturePanelId); const UIEditorPanelDescriptor& inspector = - RequirePanelDescriptor(panelRegistry, kInspectorPanelId); + RequirePanelDescriptor(panelRegistry, kInspectorWorkspaceFeaturePanelId); const UIEditorPanelDescriptor& console = - RequirePanelDescriptor(panelRegistry, kConsolePanelId); + RequirePanelDescriptor(panelRegistry, kConsoleWorkspaceFeaturePanelId); const UIEditorPanelDescriptor& project = - RequirePanelDescriptor(panelRegistry, kProjectPanelId); + RequirePanelDescriptor(panelRegistry, kProjectWorkspaceFeaturePanelId); UIEditorWorkspaceModel workspace = {}; workspace.root = BuildUIEditorWorkspaceSplit( @@ -305,7 +310,7 @@ UIEditorWorkspaceModel BuildEditorWorkspaceModel( 0.19047619f, BuildUIEditorWorkspaceSingleTabStack( "hierarchy-panel", - std::string(kHierarchyPanelId), + std::string(kHierarchyWorkspaceFeaturePanelId), hierarchy.defaultTitle, hierarchy.placeholder), BuildUIEditorWorkspaceTabStack( @@ -313,19 +318,19 @@ UIEditorWorkspaceModel BuildEditorWorkspaceModel( { BuildUIEditorWorkspacePanel( "scene-panel", - std::string(kScenePanelId), + std::string(kSceneWorkspaceFeaturePanelId), scene.defaultTitle, scene.placeholder), BuildUIEditorWorkspacePanel( "game-panel", - std::string(kGamePanelId), + std::string(kGameWorkspaceFeaturePanelId), game.defaultTitle, game.placeholder) }, 0u)), BuildUIEditorWorkspaceSingleTabStack( "inspector-panel", - std::string(kInspectorPanelId), + std::string(kInspectorWorkspaceFeaturePanelId), inspector.defaultTitle, inspector.placeholder)), BuildUIEditorWorkspaceTabStack( @@ -333,17 +338,17 @@ UIEditorWorkspaceModel BuildEditorWorkspaceModel( { BuildUIEditorWorkspacePanel( "console-panel", - std::string(kConsolePanelId), + std::string(kConsoleWorkspaceFeaturePanelId), console.defaultTitle, console.placeholder), BuildUIEditorWorkspacePanel( "project-panel", - std::string(kProjectPanelId), + std::string(kProjectWorkspaceFeaturePanelId), project.defaultTitle, project.placeholder) }, 1u)); - workspace.activePanelId = std::string(kScenePanelId); + workspace.activePanelId = std::string(kSceneWorkspaceFeaturePanelId); return workspace; } @@ -460,8 +465,8 @@ UIEditorMenuModel BuildEditorMenuModel() { }; std::vector panelMenuItems = {}; - panelMenuItems.reserve(GetEditorProductPanels().size()); - for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { + panelMenuItems.reserve(GetEditorWorkspaceFeatures().size()); + for (const EditorWorkspaceFeatureDescriptor& panel : GetEditorWorkspaceFeatures()) { UIEditorMenuCheckedStateBinding activeBinding = { UIEditorMenuCheckedStateSource::PanelActive, std::string(panel.panelId) diff --git a/editor/app/Composition/EditorShellAssetBuilder.h b/editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.h similarity index 84% rename from editor/app/Composition/EditorShellAssetBuilder.h rename to editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.h index 83b52436..f739e5ef 100644 --- a/editor/app/Composition/EditorShellAssetBuilder.h +++ b/editor/src/Product/Runtime/Shell/EditorShellAssetBuilder.h @@ -1,7 +1,7 @@ #pragma once -#include "Environment/EditorRuntimePaths.h" -#include "Windowing/EditorShellVariant.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Windowing/EditorShellVariant.h" #include #include diff --git a/editor/src/Product/Runtime/Shell/EditorShellDefinitionService.cpp b/editor/src/Product/Runtime/Shell/EditorShellDefinitionService.cpp new file mode 100644 index 00000000..e0b230b4 --- /dev/null +++ b/editor/src/Product/Runtime/Shell/EditorShellDefinitionService.cpp @@ -0,0 +1,60 @@ +#include "Product/Runtime/Shell/EditorShellDefinitionService.h" + +#include "Product/Registry/EditorProductRegistry.h" +#include "Product/Runtime/Features/EditorFeatureComposition.h" +#include "Product/Runtime/Shell/EditorShellAssetBuilder.h" + +namespace XCEngine::UI::Editor::App { + +bool EditorShellDefinitionService::Initialize(const EditorRuntimePaths& runtimePaths) { + m_valid = false; + m_validationMessage.clear(); + + const EditorProductRegistryValidationResult registryValidation = + ValidateEditorProductRegistry(); + if (!registryValidation.IsValid()) { + m_validationMessage = registryValidation.message; + return false; + } + + m_shellAsset = BuildEditorApplicationShellAsset(runtimePaths); + m_shellValidation = ValidateEditorShellAsset(m_shellAsset); + if (!m_shellValidation.IsValid()) { + m_validationMessage = m_shellValidation.message; + return false; + } + + m_valid = true; + return true; +} + +bool EditorShellDefinitionService::IsValid() const { + return m_valid; +} + +const std::string& EditorShellDefinitionService::GetValidationMessage() const { + return m_validationMessage; +} + +const EditorShellAsset& EditorShellDefinitionService::GetShellAsset() const { + return m_shellAsset; +} + +UIEditorShellInteractionDefinition EditorShellDefinitionService::BuildShellDefinition( + const UIEditorWorkspaceController& workspaceController, + std::string_view statusText, + std::string_view captureText, + EditorShellVariant variant) const { + UIEditorShellInteractionDefinition definition = + BuildEditorApplicationShellInteractionDefinition( + m_shellAsset, + workspaceController, + statusText, + captureText, + variant); + ApplyEditorWorkspaceFeaturePresentationCustomizations( + definition.workspacePresentations); + return definition; +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellDefinitionService.h b/editor/src/Product/Runtime/Shell/EditorShellDefinitionService.h similarity index 63% rename from editor/app/Composition/EditorShellDefinitionService.h rename to editor/src/Product/Runtime/Shell/EditorShellDefinitionService.h index 064a9b2c..52226273 100644 --- a/editor/app/Composition/EditorShellDefinitionService.h +++ b/editor/src/Product/Runtime/Shell/EditorShellDefinitionService.h @@ -1,7 +1,7 @@ #pragma once -#include "Environment/EditorRuntimePaths.h" -#include "Windowing/EditorFrameContracts.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Windowing/EditorShellVariant.h" #include #include @@ -11,22 +11,19 @@ namespace XCEngine::UI::Editor::App { -class EditorShellDefinitionService final - : public EditorFrameValidation - , public EditorShellDefinitionProvider { +class EditorShellDefinitionService final { public: bool Initialize(const EditorRuntimePaths& runtimePaths); - bool IsValid() const override; - const std::string& GetValidationMessage() const override; + bool IsValid() const; + const std::string& GetValidationMessage() const; const EditorShellAsset& GetShellAsset() const; - UIEditorWorkspaceController BuildWorkspaceController() const; UIEditorShellInteractionDefinition BuildShellDefinition( const UIEditorWorkspaceController& workspaceController, std::string_view statusText, std::string_view captureText, - EditorShellVariant variant) const override; + EditorShellVariant variant) const; private: EditorShellAsset m_shellAsset = {}; diff --git a/editor/app/Composition/EditorShellDrawComposer.cpp b/editor/src/Product/Runtime/Shell/EditorShellDrawComposer.cpp similarity index 94% rename from editor/app/Composition/EditorShellDrawComposer.cpp rename to editor/src/Product/Runtime/Shell/EditorShellDrawComposer.cpp index 9313def4..d01f7140 100644 --- a/editor/app/Composition/EditorShellDrawComposer.cpp +++ b/editor/src/Product/Runtime/Shell/EditorShellDrawComposer.cpp @@ -1,7 +1,7 @@ -#include "EditorShellDrawComposer.h" +#include "Product/Runtime/Shell/EditorShellDrawComposer.h" -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" -#include "Assets/EditorIconService.h" +#include "Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h" +#include "Product/Core/Assets/EditorIconService.h" #include #include @@ -142,7 +142,7 @@ void EditorShellDrawComposer::Append( palette.shellPalette, metrics.shellMetrics); }); - context.workspacePanels.AppendDrawPackets(drawData); + context.workspaceContent.AppendDrawPackets(drawData); AppendDrawPacket( drawData, "XCEditorShell.Compose.Overlay", @@ -153,7 +153,7 @@ void EditorShellDrawComposer::Append( palette.shellPalette, metrics.shellMetrics); }); - context.workspacePanels.AppendOverlayDrawPackets(drawData); + context.workspaceContent.AppendOverlayDrawPackets(drawData); AppendDrawPacket( drawData, "XCEditorShell.Popups", diff --git a/editor/app/Composition/EditorShellDrawComposer.h b/editor/src/Product/Runtime/Shell/EditorShellDrawComposer.h similarity index 87% rename from editor/app/Composition/EditorShellDrawComposer.h rename to editor/src/Product/Runtime/Shell/EditorShellDrawComposer.h index 0999afef..256fda97 100644 --- a/editor/app/Composition/EditorShellDrawComposer.h +++ b/editor/src/Product/Runtime/Shell/EditorShellDrawComposer.h @@ -12,13 +12,13 @@ struct UIEditorShellInteractionState; namespace XCEngine::UI::Editor::App { class EditorIconService; -class EditorWorkspacePanelRuntimeSet; +class EditorWorkspaceHostedContentSet; struct EditorShellDrawComposerContext { const UIEditorShellInteractionFrame& shellFrame; const UIEditorShellInteractionState& shellInteractionState; const EditorIconService& iconService; - const EditorWorkspacePanelRuntimeSet& workspacePanels; + const EditorWorkspaceHostedContentSet& workspaceContent; }; class EditorShellDrawComposer final { diff --git a/editor/app/Composition/EditorShellInteractionEngine.cpp b/editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.cpp similarity index 97% rename from editor/app/Composition/EditorShellInteractionEngine.cpp rename to editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.cpp index 46d2a65b..57287b71 100644 --- a/editor/app/Composition/EditorShellInteractionEngine.cpp +++ b/editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.cpp @@ -1,6 +1,6 @@ -#include "EditorShellInteractionEngine.h" +#include "Product/Runtime/Shell/EditorShellInteractionEngine.h" -#include "Panels/EditorPanelIds.h" +#include "Product/Registry/EditorProductRegistry.h" #include @@ -14,7 +14,7 @@ using ::XCEngine::UI::UIInputEvent; using ::XCEngine::UI::UIInputEventType; bool IsViewportPanel(std::string_view panelId) { - return IsEditorViewportPanelId(panelId); + return IsEditorWorkspaceViewportFeature(panelId); } void ApplyViewportFrameToPresentation( diff --git a/editor/app/Composition/EditorShellInteractionEngine.h b/editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.h similarity index 95% rename from editor/app/Composition/EditorShellInteractionEngine.h rename to editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.h index f627f7a9..06d1f757 100644 --- a/editor/app/Composition/EditorShellInteractionEngine.h +++ b/editor/src/Product/Runtime/Shell/EditorShellInteractionEngine.h @@ -1,6 +1,6 @@ #pragma once -#include "Viewport/EditorViewportRuntimeServices.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" #include #include diff --git a/editor/app/Composition/EditorShellRuntime.cpp b/editor/src/Product/Runtime/Shell/EditorShellRuntime.cpp similarity index 71% rename from editor/app/Composition/EditorShellRuntime.cpp rename to editor/src/Product/Runtime/Shell/EditorShellRuntime.cpp index fe3a6ac7..82aed1dc 100644 --- a/editor/app/Composition/EditorShellRuntime.cpp +++ b/editor/src/Product/Runtime/Shell/EditorShellRuntime.cpp @@ -1,6 +1,7 @@ -#include "EditorShellRuntime.h" +#include "Product/Runtime/Shell/EditorShellRuntime.h" #include "UiTextureHost.h" #include "ViewportRenderHost.h" +#include #include #include @@ -10,16 +11,20 @@ namespace XCEngine::UI::Editor::App { EditorShellRuntime::EditorShellRuntime( - EditorWorkspacePanelRuntimeSet workspacePanels, + EditorWorkspaceHostedContentSet workspaceContent, std::unique_ptr iconService, std::unique_ptr viewportRuntimeServices, UIEditorShortcutManager shortcutManager, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, EditorHostCommandBridge::RuntimeCommandOwner& runtimeCommandOwner, std::function requestExit) : m_iconService(std::move(iconService)) , m_viewportRuntimeServices(std::move(viewportRuntimeServices)) - , m_workspacePanels(std::move(workspacePanels)) - , m_shortcutManager(std::move(shortcutManager)) { + , m_workspaceContent(std::move(workspaceContent)) + , m_shortcutManager(std::move(shortcutManager)) + , m_shellDefinitionService(shellDefinitionService) + , m_frameStatusController(frameStatusController) { m_hostCommandBridge.BindRuntimeCommandOwner(&runtimeCommandOwner); m_hostCommandBridge.SetExitRequestHandler(std::move(requestExit)); m_shortcutManager.SetHostCommandHandler(&m_hostCommandBridge); @@ -48,8 +53,8 @@ void EditorShellRuntime::Initialize( gameViewportEngineBridge, shaderProvider); m_shellServices.textMeasurer = &textMeasurer; - m_workspacePanels.Initialize( - EditorWorkspacePanelInitializationContext{ + m_workspaceContent.Initialize( + EditorWorkspaceHostedContentInitializationContext{ .runtimePaths = runtimePaths, .textMeasurer = textMeasurer, .iconService = *m_iconService, @@ -80,8 +85,8 @@ void EditorShellRuntime::Shutdown() { m_shellInteractionState = {}; m_splitterDragCorrectionState = {}; m_traceEntries.clear(); - m_workspacePanels.Shutdown(EditorWorkspacePanelShutdownContext{}); - m_workspacePanels = {}; + m_workspaceContent.Shutdown(EditorWorkspaceHostedContentShutdownContext{}); + m_workspaceContent = {}; m_shellServices.textMeasurer = nullptr; if (m_iconService != nullptr) { m_iconService->Shutdown(); @@ -96,7 +101,7 @@ void EditorShellRuntime::ResetInteractionState() { m_shellInteractionState = {}; m_splitterDragCorrectionState = {}; m_traceEntries.clear(); - m_workspacePanels.ResetInteractionState(); + m_workspaceContent.ResetInteractionState(); } const UIEditorShellInteractionFrame& EditorShellRuntime::GetShellFrame() const { @@ -128,10 +133,10 @@ void EditorShellRuntime::ClearExternalDockHostDropPreview() { } EditorWorkspaceShellCursorKind EditorShellRuntime::GetHostedContentCursorKind() const { - switch (m_workspacePanels.GetHostedContentCursorKind()) { - case EditorWorkspacePanelCursorKind::ResizeEW: + switch (m_workspaceContent.GetHostedContentCursorKind()) { + case EditorWorkspaceHostedContentCursorKind::ResizeEW: return EditorWorkspaceShellCursorKind::ResizeEW; - case EditorWorkspacePanelCursorKind::Arrow: + case EditorWorkspaceHostedContentCursorKind::Arrow: default: return EditorWorkspaceShellCursorKind::Arrow; } @@ -163,7 +168,7 @@ UIEditorDockHostTabDropTarget EditorShellRuntime::ResolveDockTabDropTarget( } bool EditorShellRuntime::HasHostedContentCapture() const { - return m_workspacePanels.HasActivePointerCapture(); + return m_workspaceContent.HasActivePointerCapture(); } bool EditorShellRuntime::HasShellInteractiveCapture() const { @@ -174,18 +179,63 @@ bool EditorShellRuntime::HasInteractiveCapture() const { return HasHostedContentCapture() || HasShellInteractiveCapture(); } +UIEditorShellInteractionDefinition EditorShellRuntime::PrepareShellDefinition( + UIEditorWorkspaceController& workspaceController, + std::string_view captureText, + EditorShellVariant shellVariant) { + m_workspaceContent.PrepareForShellDefinition(workspaceController); + UIEditorShellInteractionDefinition definition = + m_shellDefinitionService.BuildShellDefinition( + workspaceController, + m_frameStatusController.ComposeStatusText(), + captureText, + shellVariant); + definition.panelOverlayRegions = + m_workspaceContent.CollectPanelOverlayRegions(); + return definition; +} + +void EditorShellRuntime::UpdateHostedPanels( + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents) { + const bool shellOwnsHostedContentPointerStream = + ShouldYieldUIEditorHostedContentPointerStream( + m_shellFrame, + HasShellInteractiveCapture()); + const std::vector<::XCEngine::UI::UIInputEvent> hostedContentEvents = + FilterUIEditorHostedContentInputEvents( + inputEvents, + shellOwnsHostedContentPointerStream); + + const EditorWorkspaceHostedContentUpdateContext updateContext{ + .shellFrame = m_shellFrame, + .shellInteractionState = m_shellInteractionState, + .hostedContentEvents = hostedContentEvents, + }; + + m_workspaceContent.UpdatePhase( + updateContext, + EditorWorkspaceHostedContentUpdatePhase::Main); + m_workspaceContent.UpdatePhase( + updateContext, + EditorWorkspaceHostedContentUpdatePhase::AfterCommandFocusSync); +} + std::unique_ptr CreateEditorWorkspaceShellRuntime( - EditorWorkspacePanelRuntimeSet workspacePanels, + EditorWorkspaceHostedContentSet workspaceContent, std::unique_ptr iconService, std::unique_ptr viewportRuntimeServices, UIEditorShortcutManager shortcutManager, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, EditorHostCommandBridge::RuntimeCommandOwner& runtimeCommandOwner, std::function requestExit) { return std::make_unique( - std::move(workspacePanels), + std::move(workspaceContent), std::move(iconService), std::move(viewportRuntimeServices), std::move(shortcutManager), + shellDefinitionService, + frameStatusController, runtimeCommandOwner, std::move(requestExit)); } @@ -211,7 +261,7 @@ void EditorShellRuntime::Append(::XCEngine::UI::UIDrawData& drawData) const { .shellFrame = m_shellFrame, .shellInteractionState = m_shellInteractionState, .iconService = *m_iconService, - .workspacePanels = m_workspacePanels, + .workspaceContent = m_workspaceContent, }, drawData); } @@ -221,8 +271,6 @@ void EditorShellRuntime::Append(::XCEngine::UI::UIDrawData& drawData) const { namespace XCEngine::UI::Editor::App { void EditorShellRuntime::Update( - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, UIEditorWorkspaceController& workspaceController, const ::XCEngine::UI::UIRect& bounds, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, @@ -236,25 +284,20 @@ void EditorShellRuntime::Update( } m_hostCommandBridge.BindCommandFocusService( - m_workspacePanels.GetCommandFocusService()); + m_workspaceContent.GetCommandFocusService()); m_hostCommandBridge.ClearEditCommandRoutes(); - for (const EditorWorkspacePanelCommandRouteBinding& binding : - m_workspacePanels.CollectCommandRoutes()) { + for (const EditorWorkspaceHostedContentCommandRouteBinding& binding : + m_workspaceContent.CollectCommandRoutes()) { m_hostCommandBridge.BindEditCommandRoute( binding.route, binding.editCommandRoute); } const auto buildDefinition = [&]() { - return m_sessionCoordinator.PrepareShellDefinition( - EditorShellSessionCoordinatorContext{ - .shellDefinitionProvider = shellDefinitionProvider, - .frameStatusService = frameStatusService, - .workspaceController = workspaceController, - .captureText = captureText, - .shellVariant = shellVariant, - .workspacePanels = m_workspacePanels, - }); + return PrepareShellDefinition( + workspaceController, + captureText, + shellVariant); }; m_interactionEngine.Update( EditorShellInteractionEngineContext{ @@ -272,21 +315,12 @@ void EditorShellRuntime::Update( .detachedWindowChromeHeight = detachedWindowChromeHeight, .viewportRuntimeServices = *m_viewportRuntimeServices, }); - m_sessionCoordinator.FinalizeFrame( - frameStatusService, + m_frameStatusController.UpdateStatusFromShellResult( workspaceController, m_shellFrame.result); - - m_hostedPanelCoordinator.Update( - EditorShellHostedPanelCoordinatorContext{ - .shellFrame = m_shellFrame, - .shellInteractionState = m_shellInteractionState, - .inputEvents = inputEvents, - .shellInteractiveCaptureActive = HasShellInteractiveCapture(), - .workspacePanels = m_workspacePanels, - }); - m_traceEntries = frameStatusService.SyncWorkspacePanelFrameEvents( - m_workspacePanels.CollectFrameEvents()); + UpdateHostedPanels(inputEvents); + m_traceEntries = m_frameStatusController.SyncWorkspaceContentFrameEvents( + m_workspaceContent.CollectFrameEvents()); } } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Composition/EditorShellRuntime.h b/editor/src/Product/Runtime/Shell/EditorShellRuntime.h similarity index 77% rename from editor/app/Composition/EditorShellRuntime.h rename to editor/src/Product/Runtime/Shell/EditorShellRuntime.h index 201856ad..c425db1c 100644 --- a/editor/app/Composition/EditorShellRuntime.h +++ b/editor/src/Product/Runtime/Shell/EditorShellRuntime.h @@ -1,14 +1,14 @@ #pragma once -#include "EditorShellDrawComposer.h" -#include "EditorShellHostedPanelCoordinator.h" -#include "EditorShellInteractionEngine.h" -#include "EditorShellSessionCoordinator.h" -#include "Assets/EditorIconService.h" -#include "Commands/EditorHostCommandBridge.h" -#include "Viewport/EditorViewportRuntimeServices.h" -#include "Windowing/EditorWorkspaceShellRuntime.h" -#include "WorkspacePanels/EditorWorkspacePanelRuntime.h" +#include "Product/Runtime/Shell/EditorShellDrawComposer.h" +#include "Product/Runtime/Shell/EditorShellInteractionEngine.h" +#include "Product/Runtime/Shell/EditorShellDefinitionService.h" +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" +#include "Product/Runtime/Shell/EditorWorkspaceShellRuntime.h" +#include "Product/Core/Assets/EditorIconService.h" +#include "Product/Commands/EditorHostCommandBridge.h" +#include "Product/Core/Viewport/EditorViewportRuntimeServices.h" +#include "Product/Framework/Workspace/EditorWorkspaceHostedContentSet.h" #include #include @@ -49,10 +49,12 @@ namespace XCEngine::UI::Editor::App { class EditorShellRuntime final : public EditorWorkspaceShellRuntime { public: EditorShellRuntime( - EditorWorkspacePanelRuntimeSet workspacePanels, + EditorWorkspaceHostedContentSet workspaceContent, std::unique_ptr iconService, std::unique_ptr viewportRuntimeServices, UIEditorShortcutManager shortcutManager, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, EditorHostCommandBridge::RuntimeCommandOwner& runtimeCommandOwner, std::function requestExit); @@ -71,8 +73,6 @@ public: void SetViewportSurfacePresentationEnabled(bool enabled) override; void Update( - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, UIEditorWorkspaceController& workspaceController, const ::XCEngine::UI::UIRect& bounds, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, @@ -107,10 +107,19 @@ public: bool HasInteractiveCapture() const override; private: + UIEditorShellInteractionDefinition PrepareShellDefinition( + UIEditorWorkspaceController& workspaceController, + std::string_view captureText, + EditorShellVariant shellVariant); + void UpdateHostedPanels( + const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents); + std::unique_ptr m_iconService = {}; std::unique_ptr m_viewportRuntimeServices = {}; - EditorWorkspacePanelRuntimeSet m_workspacePanels = {}; + EditorWorkspaceHostedContentSet m_workspaceContent = {}; UIEditorShortcutManager m_shortcutManager = {}; + const EditorShellDefinitionService& m_shellDefinitionService; + EditorFrameStatusController& m_frameStatusController; EditorHostCommandBridge m_hostCommandBridge = {}; UIEditorShellInteractionServices m_shellServices = {}; UIEditorShellInteractionState m_shellInteractionState = {}; @@ -118,16 +127,16 @@ private: std::vector m_traceEntries = {}; UIEditorWorkspaceSplitterDragCorrectionState m_splitterDragCorrectionState = {}; EditorShellDrawComposer m_drawComposer = {}; - EditorShellHostedPanelCoordinator m_hostedPanelCoordinator = {}; EditorShellInteractionEngine m_interactionEngine = {}; - EditorShellSessionCoordinator m_sessionCoordinator = {}; }; std::unique_ptr CreateEditorWorkspaceShellRuntime( - EditorWorkspacePanelRuntimeSet workspacePanels, + EditorWorkspaceHostedContentSet workspaceContent, std::unique_ptr iconService, std::unique_ptr viewportRuntimeServices, UIEditorShortcutManager shortcutManager, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, EditorHostCommandBridge::RuntimeCommandOwner& runtimeCommandOwner, std::function requestExit); diff --git a/editor/app/Core/Windowing/EditorWorkspaceShellRuntime.h b/editor/src/Product/Runtime/Shell/EditorWorkspaceShellRuntime.h similarity index 90% rename from editor/app/Core/Windowing/EditorWorkspaceShellRuntime.h rename to editor/src/Product/Runtime/Shell/EditorWorkspaceShellRuntime.h index e107fd49..20099f52 100644 --- a/editor/app/Core/Windowing/EditorWorkspaceShellRuntime.h +++ b/editor/src/Product/Runtime/Shell/EditorWorkspaceShellRuntime.h @@ -1,7 +1,8 @@ #pragma once -#include "Environment/EditorRuntimePaths.h" -#include "Windowing/EditorFrameContracts.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Windowing/EditorShellVariant.h" +#include "Product/Runtime/Diagnostics/WorkspaceTraceEntry.h" #include #include @@ -14,6 +15,15 @@ #include #include +namespace XCEngine::UI::Editor { + +class UIEditorWorkspaceController; + +struct UIEditorShellInteractionFrame; +struct UIEditorShellInteractionState; + +} // namespace XCEngine::UI::Editor + namespace XCEngine::Rendering { class RenderContext; @@ -70,8 +80,6 @@ public: virtual void SetViewportSurfacePresentationEnabled(bool enabled) = 0; virtual void Update( - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, UIEditorWorkspaceController& workspaceController, const ::XCEngine::UI::UIRect& bounds, const std::vector<::XCEngine::UI::UIInputEvent>& inputEvents, diff --git a/editor/src/Product/Runtime/Store/EditorStore.cpp b/editor/src/Product/Runtime/Store/EditorStore.cpp new file mode 100644 index 00000000..674bf075 --- /dev/null +++ b/editor/src/Product/Runtime/Store/EditorStore.cpp @@ -0,0 +1,245 @@ +#include "Product/Runtime/Store/EditorStore.h" + +#include +#include + +namespace XCEngine::UI::Editor::Product { + +namespace { + +constexpr std::size_t kMaxConsoleEntries = 256u; + +bool AreSelectionStatesEqual( + const App::EditorSelectionState& left, + const App::EditorSelectionState& right) { + return left.kind == right.kind && + left.itemId == right.itemId && + left.displayName == right.displayName && + left.absolutePath == right.absolutePath && + left.directory == right.directory && + left.stamp == right.stamp; +} + +bool AreWindowWorkspaceStatesEqual( + const UIEditorWindowWorkspaceState& left, + const UIEditorWindowWorkspaceState& right) { + return left.windowId == right.windowId && + AreUIEditorWorkspaceModelsEquivalent(left.workspace, right.workspace) && + AreUIEditorWorkspaceSessionsEquivalent(left.session, right.session); +} + +bool AreWindowWorkspaceSetsEqual( + const UIEditorWindowWorkspaceSet& left, + const UIEditorWindowWorkspaceSet& right) { + if (left.primaryWindowId != right.primaryWindowId || + left.activeWindowId != right.activeWindowId || + left.windows.size() != right.windows.size()) { + return false; + } + + for (std::size_t index = 0; index < left.windows.size(); ++index) { + if (!AreWindowWorkspaceStatesEqual(left.windows[index], right.windows[index])) { + return false; + } + } + + return true; +} + +void AppendConsoleEntry( + EditorState& state, + App::EditorConsoleEntry entry) { + state.consoleEntries.push_back(std::move(entry)); + if (state.consoleEntries.size() > kMaxConsoleEntries) { + state.consoleEntries.erase(state.consoleEntries.begin()); + } +} + +} // namespace + +const EditorState& EditorStore::GetState() const { + return m_state; +} + +const UIEditorWindowWorkspaceSet& EditorStore::GetWindowWorkspace() const { + return m_state.windowWorkspace; +} + +const UIEditorWindowWorkspaceState* EditorStore::FindWindowWorkspaceState( + std::string_view windowId) const { + return FindUIEditorWindowWorkspaceState(m_state.windowWorkspace, windowId); +} + +bool EditorStore::ReplaceWindowWorkspace(UIEditorWindowWorkspaceSet windowWorkspace) { + if (AreWindowWorkspaceSetsEqual(m_state.windowWorkspace, windowWorkspace)) { + return false; + } + + m_state.windowWorkspace = std::move(windowWorkspace); + SyncSessionProjections(m_state); + NotifyObservers(); + return true; +} + +bool EditorStore::MutateWindowWorkspaceState( + std::string_view windowId, + const std::function& mutator) { + if (!mutator) { + return false; + } + + UIEditorWindowWorkspaceState* const windowState = + FindMutableUIEditorWindowWorkspaceState(m_state.windowWorkspace, windowId); + if (windowState == nullptr) { + return false; + } + + const UIEditorWindowWorkspaceState previousState = *windowState; + mutator(*windowState); + if (!AreWindowWorkspaceStatesEqual(previousState, *windowState)) { + SyncSessionProjections(m_state); + NotifyObservers(); + } + + return true; +} + +App::EditorSession& EditorStore::GetMutableSession() { + return m_state.session; +} + +void EditorStore::Reset(EditorState initialState) { + SyncSessionProjections(initialState); + m_state = std::move(initialState); + NotifyObservers(); +} + +EditorStore::DispatchResult EditorStore::Dispatch(const EditorCommand& command) { + DispatchResult result = {}; + result.handled = true; + + switch (command.kind) { + case EditorCommandKind::ClearStatus: + if (!m_state.status.channel.empty() || !m_state.status.message.empty()) { + m_state.status = {}; + result.stateChanged = true; + } + break; + + case EditorCommandKind::SetStatus: + if (m_state.status.channel != command.status.channel || + m_state.status.message != command.status.message) { + m_state.status = command.status; + AppendConsoleEntry( + m_state, + App::EditorConsoleEntry{ + .channel = command.status.channel, + .message = command.status.message, + }); + result.stateChanged = true; + } + break; + + case EditorCommandKind::AppendConsoleEntry: + AppendConsoleEntry(m_state, command.consoleEntry); + result.stateChanged = true; + break; + + case EditorCommandKind::SetSelection: + if (!AreSelectionStatesEqual(m_state.session.selection, command.selection)) { + m_state.session.selection = command.selection; + result.stateChanged = true; + } + break; + + case EditorCommandKind::RequestOpenUtilityWindow: + if (command.utilityWindowKind == App::EditorUtilityWindowKind::None) { + break; + } + + if (m_state.pendingUtilityWindowRequest != command.utilityWindowKind) { + m_state.pendingUtilityWindowRequest = command.utilityWindowKind; + result.effects.push_back(EditorEffect{ + .kind = EditorEffectKind::UtilityWindowRequestQueued, + .utilityWindowKind = command.utilityWindowKind, + }); + result.stateChanged = true; + } + break; + + case EditorCommandKind::ConsumePendingUtilityWindowRequest: + if (m_state.pendingUtilityWindowRequest.has_value()) { + result.effects.push_back(EditorEffect{ + .kind = EditorEffectKind::UtilityWindowRequestConsumed, + .utilityWindowKind = *m_state.pendingUtilityWindowRequest, + }); + m_state.pendingUtilityWindowRequest.reset(); + result.stateChanged = true; + } + break; + + case EditorCommandKind::None: + default: + result.handled = false; + break; + } + + if (result.stateChanged) { + SyncSessionProjections(m_state); + NotifyObservers(); + } + + return result; +} + +std::optional +EditorStore::ConsumePendingUtilityWindowRequest() { + const std::optional requestedKind = + m_state.pendingUtilityWindowRequest; + if (!requestedKind.has_value()) { + return std::nullopt; + } + + Dispatch(EditorCommand::ConsumePendingUtilityWindowRequest()); + return requestedKind; +} + +EditorStore::SubscriptionToken EditorStore::Subscribe(EditorStateObserver observer) { + if (!observer) { + return 0u; + } + + const SubscriptionToken token = m_nextSubscriptionToken++; + m_observers.emplace_back(token, std::move(observer)); + return token; +} + +bool EditorStore::Unsubscribe(SubscriptionToken token) { + const auto iterator = std::find_if( + m_observers.begin(), + m_observers.end(), + [token](const auto& entry) { + return entry.first == token; + }); + if (iterator == m_observers.end()) { + return false; + } + + m_observers.erase(iterator); + return true; +} + +void EditorStore::SyncSessionProjections(EditorState& state) { + state.session.consoleEntries = state.consoleEntries; +} + +void EditorStore::NotifyObservers() const { + for (const auto& [token, observer] : m_observers) { + (void)token; + if (observer) { + observer(m_state); + } + } +} + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/src/Product/Runtime/Store/EditorStore.h b/editor/src/Product/Runtime/Store/EditorStore.h new file mode 100644 index 00000000..c91b874d --- /dev/null +++ b/editor/src/Product/Runtime/Store/EditorStore.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Product/Commands/EditorCommand.h" +#include "Product/Effects/EditorEffect.h" + +#include +#include +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::Product { + +class EditorStore final { +public: + using EditorStateObserver = std::function; + using SubscriptionToken = std::uint64_t; + + struct DispatchResult { + bool handled = false; + bool stateChanged = false; + std::vector effects = {}; + }; + + EditorStore() = default; + + const EditorState& GetState() const; + const UIEditorWindowWorkspaceSet& GetWindowWorkspace() const; + const UIEditorWindowWorkspaceState* FindWindowWorkspaceState( + std::string_view windowId) const; + bool ReplaceWindowWorkspace(UIEditorWindowWorkspaceSet windowWorkspace); + bool MutateWindowWorkspaceState( + std::string_view windowId, + const std::function& mutator); + App::EditorSession& GetMutableSession(); + + void Reset(EditorState initialState = {}); + DispatchResult Dispatch(const EditorCommand& command); + std::optional ConsumePendingUtilityWindowRequest(); + + SubscriptionToken Subscribe(EditorStateObserver observer); + bool Unsubscribe(SubscriptionToken token); + +private: + static void SyncSessionProjections(EditorState& state); + void NotifyObservers() const; + + EditorState m_state = {}; + SubscriptionToken m_nextSubscriptionToken = 1u; + std::vector> m_observers = {}; +}; + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/app/Windowing/Content/EditorUtilityWindowContentController.cpp b/editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.cpp similarity index 79% rename from editor/app/Windowing/Content/EditorUtilityWindowContentController.cpp rename to editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.cpp index 7bc39e19..ef78b352 100644 --- a/editor/app/Windowing/Content/EditorUtilityWindowContentController.cpp +++ b/editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.cpp @@ -1,6 +1,6 @@ -#include "Content/EditorUtilityWindowContentController.h" +#include "Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.h" -#include "UtilityWindows/EditorUtilityWindowRuntime.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" #include @@ -10,9 +10,9 @@ using ::XCEngine::UI::UIInputEvent; using ::XCEngine::UI::UIInputEventType; EditorUtilityWindowContentController::EditorUtilityWindowContentController( - std::unique_ptr panel, + std::unique_ptr content, const ::XCEngine::UI::UISize& minimumOuterSize) - : m_panel(std::move(panel)), + : m_content(std::move(content)), m_minimumOuterSize(minimumOuterSize) {} EditorUtilityWindowContentController::~EditorUtilityWindowContentController() = default; @@ -25,13 +25,13 @@ EditorUtilityWindowContentController::GetCapabilities() const { .inputFeedback = false, .titleBar = false, .viewportRendering = false, - .utilityPanel = true, + .utilityWindowContent = true, }; } void EditorUtilityWindowContentController::Shutdown() { - if (m_panel != nullptr) { - m_panel->ResetInteractionState(); + if (m_content != nullptr) { + m_content->ResetInteractionState(); } m_shellInteractionState = {}; m_shellFrame = {}; @@ -39,8 +39,8 @@ void EditorUtilityWindowContentController::Shutdown() { } void EditorUtilityWindowContentController::ResetInteractionState() { - if (m_panel != nullptr) { - m_panel->ResetInteractionState(); + if (m_content != nullptr) { + m_content->ResetInteractionState(); } } @@ -63,11 +63,11 @@ EditorUtilityWindowContentController::UpdateAndAppend( m_shellInteractionState.focused = m_windowFocused; m_shellFrame.focused = m_windowFocused; - if (m_panel == nullptr) { + if (m_content == nullptr) { return {}; } - m_panel->Update( + m_content->Update( EditorUtilityWindowHostContext{ .mounted = true, .bounds = context.bounds, @@ -79,8 +79,8 @@ EditorUtilityWindowContentController::UpdateAndAppend( context.inputEvents); ::XCEngine::UI::UIDrawList& drawList = - drawData.EmplaceDrawList(std::string(m_panel->GetDrawListId())); - m_panel->Append(drawList); + drawData.EmplaceDrawList(std::string(m_content->GetDrawListId())); + m_content->Append(drawList); return {}; } @@ -99,13 +99,13 @@ EditorUtilityWindowContentController::GetShellInteractionState() const { std::unique_ptr CreateEditorUtilityWindowContentController( const EditorUtilityWindowDescriptor& descriptor, - std::unique_ptr panel) { - if (panel == nullptr) { + std::unique_ptr content) { + if (content == nullptr) { return nullptr; } return std::make_unique( - std::move(panel), + std::move(content), descriptor.minimumOuterSize); } diff --git a/editor/app/Windowing/Content/EditorUtilityWindowContentController.h b/editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.h similarity index 82% rename from editor/app/Windowing/Content/EditorUtilityWindowContentController.h rename to editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.h index ab0edc6f..e5951bac 100644 --- a/editor/app/Windowing/Content/EditorUtilityWindowContentController.h +++ b/editor/src/Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.h @@ -1,6 +1,6 @@ #pragma once -#include "Content/EditorWindowContentController.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentController.h" #include @@ -10,14 +10,14 @@ namespace XCEngine::UI::Editor::App { -class EditorUtilityWindowPanel; +class EditorUtilityWindowContent; struct EditorUtilityWindowDescriptor; class EditorUtilityWindowContentController final : public EditorWindowContentController { public: EditorUtilityWindowContentController( - std::unique_ptr panel, + std::unique_ptr content, const ::XCEngine::UI::UISize& minimumOuterSize); ~EditorUtilityWindowContentController() override; @@ -32,7 +32,7 @@ public: ::XCEngine::UI::UISize ResolveMinimumOuterSize() const override; private: - std::unique_ptr m_panel = {}; + std::unique_ptr m_content = {}; ::XCEngine::UI::UISize m_minimumOuterSize = {}; UIEditorShellInteractionState m_shellInteractionState = {}; UIEditorShellInteractionFrame m_shellFrame = {}; @@ -41,6 +41,6 @@ private: std::unique_ptr CreateEditorUtilityWindowContentController( const EditorUtilityWindowDescriptor& descriptor, - std::unique_ptr panel); + std::unique_ptr content); } // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.cpp b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.cpp new file mode 100644 index 00000000..dddebb6b --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.cpp @@ -0,0 +1,38 @@ +#include "Product/Runtime/Windowing/Content/EditorWindowContentController.h" + +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" +#include "Product/Runtime/Shell/EditorShellDefinitionService.h" +#include "Product/Runtime/Windowing/EditorWindowVisuals.h" + +#include + +namespace XCEngine::UI::Editor::App { + +bool EditorWindowContentController::IsFrameValid( + const EditorShellDefinitionService& shellDefinitionService) const { + return shellDefinitionService.IsValid(); +} + +void EditorWindowContentController::AppendInvalidFrame( + const EditorShellDefinitionService& shellDefinitionService, + ::XCEngine::UI::UIDrawList& drawList) const { + drawList.AddText( + ::XCEngine::UI::UIPoint(28.0f, 28.0f), + "Editor shell asset invalid.", + kShellTextColor, + 16.0f); + drawList.AddText( + ::XCEngine::UI::UIPoint(28.0f, 54.0f), + shellDefinitionService.GetValidationMessage().empty() + ? std::string("Unknown validation error.") + : shellDefinitionService.GetValidationMessage(), + kShellMutedTextColor, + 12.0f); +} + +void EditorWindowContentController::NotifyStartupCaptureRequested( + EditorFrameStatusController& frameStatusController) { + frameStatusController.SetStatus("Capture", "Startup capture requested."); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWindowContentController.h b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.h similarity index 88% rename from editor/app/Windowing/Content/EditorWindowContentController.h rename to editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.h index 384ab1af..18e6b7f9 100644 --- a/editor/app/Windowing/Content/EditorWindowContentController.h +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentController.h @@ -1,8 +1,7 @@ #pragma once -#include "Environment/EditorRuntimePaths.h" -#include "Windowing/EditorWindowTransferRequests.h" -#include "Windowing/EditorFrameContracts.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" #include "EditorWindowContentBindings.h" #include @@ -52,7 +51,9 @@ class EditorHostResourceService; namespace XCEngine::UI::Editor::App { +class EditorFrameStatusController; class EditorShaderProvider; +class EditorShellDefinitionService; class GameViewportEngineBridge; class SceneViewportEngineBridge; @@ -62,7 +63,7 @@ struct EditorWindowContentCapabilities { bool inputFeedback = false; bool titleBar = false; bool viewportRendering = false; - bool utilityPanel = false; + bool utilityWindowContent = false; }; struct EditorWindowContentInitializationContext { @@ -77,8 +78,6 @@ struct EditorWindowContentInitializationContext { }; struct EditorWindowContentFrameContext { - const EditorShellDefinitionProvider& shellDefinitionProvider; - EditorFrameStatusService& frameStatusService; const std::function()>* consumeOpenUtilityWindowRequest = nullptr; const ::XCEngine::UI::UIRect& bounds; @@ -118,11 +117,12 @@ public: } virtual void Initialize(const EditorWindowContentInitializationContext&) {} - virtual bool IsFrameValid(const EditorFrameValidation& frameValidation) const; + virtual bool IsFrameValid(const EditorShellDefinitionService& shellDefinitionService) const; virtual void AppendInvalidFrame( - const EditorFrameValidation& frameValidation, + const EditorShellDefinitionService& shellDefinitionService, ::XCEngine::UI::UIDrawList& drawList) const; - virtual void NotifyStartupCaptureRequested(EditorFrameStatusService& frameStatusService); + virtual void NotifyStartupCaptureRequested( + EditorFrameStatusController& frameStatusController); virtual void Shutdown() {} virtual void ResetInteractionState() {} virtual void SetViewportSurfacePresentationEnabled(bool) {} diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.cpp b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.cpp similarity index 61% rename from editor/app/Windowing/Content/EditorWindowContentFactory.cpp rename to editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.cpp index 133cb764..216a18d9 100644 --- a/editor/app/Windowing/Content/EditorWindowContentFactory.cpp +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.cpp @@ -1,9 +1,8 @@ -#include "Content/EditorWindowContentFactory.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentFactory.h" -#include "Content/EditorUtilityWindowContentController.h" -#include "Content/EditorWorkspaceWindowContentController.h" - -#include +#include "Product/Runtime/EditorProductRuntime.h" +#include "Product/Runtime/Windowing/Content/EditorUtilityWindowContentController.h" +#include "Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.h" #include @@ -14,12 +13,12 @@ namespace { class DefaultEditorWindowContentFactory final : public EditorWindowContentFactory { public: DefaultEditorWindowContentFactory( - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, - EditorUtilityWindowPanelFactory utilityPanelFactory) - : m_windowSystem(windowSystem) + EditorUtilityWindowContentFactory utilityContentFactory) + : m_productRuntime(productRuntime) , m_workspaceShellRuntimeFactory(std::move(workspaceShellRuntimeFactory)) - , m_utilityPanelFactory(std::move(utilityPanelFactory)) {} + , m_utilityContentFactory(std::move(utilityContentFactory)) {} std::unique_ptr CreateWorkspaceContentController( const UIEditorWindowWorkspaceState& windowState) const override { @@ -29,15 +28,15 @@ public: : nullptr; return CreateEditorWorkspaceWindowContentController( windowState, - m_windowSystem, + m_productRuntime, std::move(shellRuntime)); } std::unique_ptr CreateUtilityContentController( const EditorUtilityWindowDescriptor& descriptor) const override { - std::unique_ptr panel = - m_utilityPanelFactory - ? m_utilityPanelFactory(descriptor.kind) + std::unique_ptr panel = + m_utilityContentFactory + ? m_utilityContentFactory(descriptor.kind) : nullptr; return CreateEditorUtilityWindowContentController( descriptor, @@ -45,21 +44,21 @@ public: } private: - EditorWindowSystem& m_windowSystem; + EditorProductRuntime& m_productRuntime; EditorWorkspaceShellRuntimeFactory m_workspaceShellRuntimeFactory = {}; - EditorUtilityWindowPanelFactory m_utilityPanelFactory = {}; + EditorUtilityWindowContentFactory m_utilityContentFactory = {}; }; } // namespace std::unique_ptr CreateDefaultEditorWindowContentFactory( - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, - EditorUtilityWindowPanelFactory utilityPanelFactory) { + EditorUtilityWindowContentFactory utilityContentFactory) { return std::make_unique( - windowSystem, + productRuntime, std::move(workspaceShellRuntimeFactory), - std::move(utilityPanelFactory)); + std::move(utilityContentFactory)); } } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWindowContentFactory.h b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.h similarity index 77% rename from editor/app/Windowing/Content/EditorWindowContentFactory.h rename to editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.h index 8c453cb0..7fdc6375 100644 --- a/editor/app/Windowing/Content/EditorWindowContentFactory.h +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWindowContentFactory.h @@ -1,14 +1,12 @@ #pragma once -#include "UtilityWindows/EditorUtilityWindowRuntime.h" -#include "Windowing/EditorWorkspaceShellRuntime.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" +#include "Product/Runtime/Shell/EditorWorkspaceShellRuntime.h" #include #include namespace XCEngine::UI::Editor { - -class EditorWindowSystem; struct UIEditorWindowWorkspaceState; } // namespace XCEngine::UI::Editor @@ -16,6 +14,7 @@ struct UIEditorWindowWorkspaceState; namespace XCEngine::UI::Editor::App { class EditorWindowContentController; +class EditorProductRuntime; struct EditorUtilityWindowDescriptor; class EditorWindowContentFactory { @@ -29,8 +28,8 @@ public: }; std::unique_ptr CreateDefaultEditorWindowContentFactory( - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, - EditorUtilityWindowPanelFactory utilityPanelFactory); + EditorUtilityWindowContentFactory utilityContentFactory); } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp b/editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.cpp similarity index 83% rename from editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp rename to editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.cpp index 5d3098b0..eefcf306 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.cpp +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.cpp @@ -1,8 +1,9 @@ -#include "Content/EditorWorkspaceWindowContentController.h" +#include "Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.h" + +#include "Product/Runtime/EditorProductRuntime.h" #include #include -#include #include #include @@ -39,15 +40,19 @@ EditorWindowContentCursorKind ToContentCursorKind( EditorWorkspaceWindowContentController::EditorWorkspaceWindowContentController( const UIEditorWindowWorkspaceState& windowState, - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, std::unique_ptr shellRuntime) : m_windowId(windowState.windowId) - , m_windowSystem(windowSystem) + , m_productRuntime(productRuntime) , m_projection(BuildEditorWorkspaceWindowProjection( std::wstring_view{}, - windowSystem.GetPanelRegistry(), + productRuntime.GetPanelRegistry(), windowState, false)) + , m_workspaceController( + productRuntime.GetPanelRegistry(), + windowState.workspace, + windowState.session) , m_shellRuntime(std::move(shellRuntime)) {} EditorWorkspaceWindowContentController::~EditorWorkspaceWindowContentController() = default; @@ -60,7 +65,7 @@ EditorWorkspaceWindowContentController::GetCapabilities() const { .inputFeedback = true, .titleBar = true, .viewportRendering = true, - .utilityPanel = false, + .utilityWindowContent = false, }; } @@ -102,13 +107,6 @@ void EditorWorkspaceWindowContentController::RefreshWorkspaceProjection( m_projection = std::move(projection); } -bool EditorWorkspaceWindowContentController::TryBuildAuthoritativeWorkspaceController( - UIEditorWorkspaceController& outController) { - return m_windowSystem.TryBuildLiveWindowWorkspaceController( - m_windowId, - outController); -} - void EditorWorkspaceWindowContentController::Initialize( const EditorWindowContentInitializationContext& context) { if (m_shellRuntime == nullptr) { @@ -146,33 +144,39 @@ void EditorWorkspaceWindowContentController::SetViewportSurfacePresentationEnabl EditorWindowFrameTransferRequests EditorWorkspaceWindowContentController::UpdateAndAppend( const EditorWindowContentFrameContext& context, ::XCEngine::UI::UIDrawData& drawData) { - UIEditorWorkspaceController workspaceController = {}; - if (!TryBuildAuthoritativeWorkspaceController(workspaceController)) { - AppendUIEditorRuntimeTrace( - "window", - "workspace frame skipped: authoritative state missing for window '" + - m_windowId + "'"); - return {}; - } - if (m_shellRuntime == nullptr) { return {}; } - return m_frameOrchestrator.UpdateAndAppend( - context.shellDefinitionProvider, - context.frameStatusService, - context.consumeOpenUtilityWindowRequest, - workspaceController, - *m_shellRuntime, - context.bounds, - context.inputEvents, - context.cursorScreenPoint, - context.captureStatusText, - context.primary, - context.globalTabDragActive, - context.useDetachedTitleBarTabStrip, - drawData); + EditorWindowFrameTransferRequests transferRequests = {}; + std::string mutationError = {}; + if (!m_productRuntime.MutateWindowWorkspaceState( + m_windowId, + m_workspaceController, + [&](UIEditorWorkspaceController& workspaceController) { + transferRequests = m_frameOrchestrator.UpdateAndAppend( + m_productRuntime.GetFrameStatusController(), + context.consumeOpenUtilityWindowRequest, + workspaceController, + *m_shellRuntime, + context.bounds, + context.inputEvents, + context.cursorScreenPoint, + context.captureStatusText, + context.primary, + context.globalTabDragActive, + context.useDetachedTitleBarTabStrip, + drawData); + }, + mutationError)) { + AppendUIEditorRuntimeTrace( + "window", + "workspace frame mutation failed for window '" + m_windowId + + "': " + mutationError); + return {}; + } + + return transferRequests; } void EditorWorkspaceWindowContentController::RenderRequestedViewports( @@ -274,11 +278,11 @@ std::string EditorWorkspaceWindowContentController::ResolveDetachedWindowTitleTe std::unique_ptr CreateEditorWorkspaceWindowContentController( const UIEditorWindowWorkspaceState& windowState, - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, std::unique_ptr shellRuntime) { return std::make_unique( windowState, - windowSystem, + productRuntime, std::move(shellRuntime)); } diff --git a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h b/editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.h similarity index 88% rename from editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h rename to editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.h index 99123045..875e7cf7 100644 --- a/editor/app/Windowing/Content/EditorWorkspaceWindowContentController.h +++ b/editor/src/Product/Runtime/Windowing/Content/EditorWorkspaceWindowContentController.h @@ -1,20 +1,21 @@ #pragma once -#include "Content/EditorWindowContentController.h" -#include "Frame/EditorWindowFrameOrchestrator.h" -#include "Windowing/EditorWorkspaceShellRuntime.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentController.h" +#include "Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.h" +#include "Product/Runtime/Shell/EditorWorkspaceShellRuntime.h" #include #include namespace XCEngine::UI::Editor { -class EditorWindowSystem; struct UIEditorWindowWorkspaceState; -} +} // namespace XCEngine::UI::Editor namespace XCEngine::UI::Editor::App { +class EditorProductRuntime; + class EditorWorkspaceWindowContentController final : public EditorWindowContentController , public EditorWindowWorkspaceBinding @@ -24,7 +25,7 @@ class EditorWorkspaceWindowContentController final public: EditorWorkspaceWindowContentController( const UIEditorWindowWorkspaceState& windowState, - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, std::unique_ptr shellRuntime); ~EditorWorkspaceWindowContentController() override; @@ -77,18 +78,17 @@ public: std::string_view fallbackWindowTitle) const override; private: - bool TryBuildAuthoritativeWorkspaceController(UIEditorWorkspaceController& outController); - std::string m_windowId = {}; - EditorWindowSystem& m_windowSystem; + EditorProductRuntime& m_productRuntime; EditorWorkspaceWindowProjection m_projection = {}; + UIEditorWorkspaceController m_workspaceController = {}; std::unique_ptr m_shellRuntime = {}; EditorWindowFrameOrchestrator m_frameOrchestrator = {}; }; std::unique_ptr CreateEditorWorkspaceWindowContentController( const UIEditorWindowWorkspaceState& windowState, - EditorWindowSystem& windowSystem, + EditorProductRuntime& productRuntime, std::unique_ptr shellRuntime); } // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/EditorWindowDiagnostics.h b/editor/src/Product/Runtime/Windowing/EditorWindowDiagnostics.h similarity index 82% rename from editor/app/Windowing/EditorWindowDiagnostics.h rename to editor/src/Product/Runtime/Windowing/EditorWindowDiagnostics.h index a945825e..4f2b4bee 100644 --- a/editor/app/Windowing/EditorWindowDiagnostics.h +++ b/editor/src/Product/Runtime/Windowing/EditorWindowDiagnostics.h @@ -1,6 +1,6 @@ #pragma once -#include "EnvironmentFlags.h" +#include "Product/Support/EnvironmentFlags.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Windowing/EditorWindowInstance.cpp b/editor/src/Product/Runtime/Windowing/EditorWindowInstance.cpp similarity index 98% rename from editor/app/Windowing/EditorWindowInstance.cpp rename to editor/src/Product/Runtime/Windowing/EditorWindowInstance.cpp index d06b85e2..2190f84c 100644 --- a/editor/app/Windowing/EditorWindowInstance.cpp +++ b/editor/src/Product/Runtime/Windowing/EditorWindowInstance.cpp @@ -1,9 +1,9 @@ -#include "EditorWindowInstance.h" +#include "Product/Runtime/Windowing/EditorWindowInstance.h" -#include "StringEncoding.h" +#include "Product/Support/StringEncoding.h" #include "EditorWindowContentBindings.h" -#include "Runtime/EditorWindowRuntimeController.h" -#include "EditorWindowVisuals.h" +#include "Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h" +#include "Product/Runtime/Windowing/EditorWindowVisuals.h" #include #include diff --git a/editor/app/Windowing/EditorWindowInstance.h b/editor/src/Product/Runtime/Windowing/EditorWindowInstance.h similarity index 100% rename from editor/app/Windowing/EditorWindowInstance.h rename to editor/src/Product/Runtime/Windowing/EditorWindowInstance.h diff --git a/editor/src/Product/Runtime/Windowing/EditorWindowManager.cpp b/editor/src/Product/Runtime/Windowing/EditorWindowManager.cpp new file mode 100644 index 00000000..014ff854 --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/EditorWindowManager.cpp @@ -0,0 +1,590 @@ +#include "Product/Runtime/Windowing/EditorWindowManager.h" + +#include "Product/Registry/EditorProductRegistry.h" +#include "Product/Runtime/EditorProductRuntime.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentController.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentFactory.h" +#include "Product/Runtime/Windowing/EditorWindowInstance.h" +#include "Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h" +#include "Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h" +#include "Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h" + +#include "EditorWindowRenderRuntime.h" + +#include + +#include +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +bool IsLiveWindow(const EditorHostWindow* window) { + return window != nullptr && + window->HasLiveHostWindow() && + window->GetLifecycleState() == EditorWindowLifecycleState::Running; +} + +int ResolveOuterDimension(float value, int fallback) { + return value > 0.0f + ? static_cast(std::lround(value)) + : fallback; +} + +} // namespace + +EditorWindowManager::EditorWindowManager( + EditorProductRuntime& productRuntime, + std::function()> consumeOpenUtilityWindowRequest, + SceneViewportEngineBridge& sceneViewportEngineBridge, + GameViewportEngineBridge& gameViewportEngineBridge, + EditorShaderProvider& shaderProvider, + EditorWindowSystem& windowSystem, + Rendering::Host::EditorWindowRenderRuntimeFactory& renderRuntimeFactory, + Host::EditorHostResourceService& resourceService, + EditorWindowHostRuntimeServices& hostRuntime, + EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, + EditorUtilityWindowContentFactory utilityContentFactory) + : m_shellDefinitionService(productRuntime.GetShellDefinitionService()) + , m_frameStatusController(productRuntime.GetFrameStatusController()) + , m_consumeOpenUtilityWindowRequest(std::move(consumeOpenUtilityWindowRequest)) + , m_sceneViewportEngineBridge(sceneViewportEngineBridge) + , m_gameViewportEngineBridge(gameViewportEngineBridge) + , m_shaderProvider(shaderProvider) + , m_renderRuntimeFactory(renderRuntimeFactory) + , m_resourceService(resourceService) + , m_hostRuntime(hostRuntime) { + m_contentFactory = CreateDefaultEditorWindowContentFactory( + productRuntime, + std::move(workspaceShellRuntimeFactory), + std::move(utilityContentFactory)); + m_workspaceSynchronizer = + std::make_unique( + m_hostRuntime, + *this, + productRuntime, + windowSystem); + m_workspaceInteractionController = + std::make_unique( + m_hostRuntime, + productRuntime, + *m_workspaceSynchronizer); + m_hostRuntime.BindHostCoordinator(*this); +} + +EditorWindowManager::~EditorWindowManager() = default; + +EditorHostWindow* EditorWindowManager::CreateWorkspaceWindow( + const UIEditorWindowWorkspaceState& windowState, + const EditorWindowCreateParams& params) { + if (m_contentFactory == nullptr) { + return nullptr; + } + if (windowState.windowId.empty()) { + return nullptr; + } + if (!params.windowId.empty() && windowState.windowId != params.windowId) { + return nullptr; + } + + auto windowInstance = CreateEditorWindowInstance( + params.windowId, + params.title, + params.category, + params.chromePolicy, + params.primary, + std::make_unique( + m_shellDefinitionService, + m_frameStatusController, + m_consumeOpenUtilityWindowRequest, + m_sceneViewportEngineBridge, + m_gameViewportEngineBridge, + m_shaderProvider, + m_resourceService, + m_contentFactory->CreateWorkspaceContentController(windowState), + m_renderRuntimeFactory.CreateWindowRenderRuntime())); + EditorHostWindow* const window = windowInstance.get(); + if (!m_hostRuntime.CreateHostWindow(*window, params)) { + return nullptr; + } + m_windows.push_back(std::move(windowInstance)); + if (window != nullptr && m_workspaceSynchronizer != nullptr) { + m_workspaceSynchronizer->RegisterExistingWindow(*window); + } + return window; +} + +EditorHostWindow* EditorWindowManager::CreateUtilityWindow( + const EditorUtilityWindowDescriptor& descriptor, + const EditorWindowCreateParams& params) { + if (m_contentFactory == nullptr) { + return nullptr; + } + + auto windowInstance = CreateEditorWindowInstance( + params.windowId, + params.title, + params.category, + params.chromePolicy, + params.primary, + std::make_unique( + m_shellDefinitionService, + m_frameStatusController, + m_consumeOpenUtilityWindowRequest, + m_sceneViewportEngineBridge, + m_gameViewportEngineBridge, + m_shaderProvider, + m_resourceService, + m_contentFactory->CreateUtilityContentController(descriptor), + m_renderRuntimeFactory.CreateWindowRenderRuntime())); + EditorHostWindow* const window = windowInstance.get(); + if (!m_hostRuntime.CreateHostWindow(*window, params)) { + return nullptr; + } + m_windows.push_back(std::move(windowInstance)); + if (window != nullptr && m_workspaceSynchronizer != nullptr) { + m_workspaceSynchronizer->RegisterExistingWindow(*window); + } + return window; +} + +void EditorWindowManager::Shutdown() { + if (m_workspaceInteractionController != nullptr) { + m_workspaceInteractionController->EndGlobalTabDragSession(); + } + ShutdownAllWindows(); + ReapDestroyedWindows(); + m_windows.clear(); +} + +bool EditorWindowManager::RequestPrimaryWindowClose() { + for (const std::unique_ptr& window : m_windows) { + if (window == nullptr || !window->IsPrimary()) { + continue; + } + + RequestWindowClose(*window); + return true; + } + + return false; +} + +void EditorWindowManager::RequestWindowClose(EditorHostWindow& window) { + if (window.IsDestroyed()) { + return; + } + + if (!window.IsClosing()) { + window.MarkClosing(); + } + + if (!window.HasLiveHostWindow()) { + ShutdownRuntimeIfNeeded(window); + window.MarkDestroyed(); + return; + } + + LogRuntimeTrace( + "window-close", + "PostCloseRequest windowId='" + std::string(window.GetWindowId()) + + " primary=" + (window.IsPrimary() ? "1" : "0") + + " host=" + m_hostRuntime.DescribeWindows()); + window.PostCloseToHost(); +} + +void EditorWindowManager::AbortWindow(EditorHostWindow& window) { + AbortUnregisteredWindow(window); +} + +bool EditorWindowManager::HasWindows() const { + return !m_windows.empty(); +} + +void EditorWindowManager::DestroyClosedWindows() { + ReapDestroyedWindows(); +} + +void EditorWindowManager::RenderAllWindows() { + struct WindowFrameTransferBatch { + EditorHostWindow* sourceWindow = nullptr; + EditorWindowFrameTransferRequests requests = {}; + }; + + std::vector transferBatches = {}; + std::vector windows = {}; + windows.reserve(m_windows.size()); + for (const std::unique_ptr& window : m_windows) { + if (window != nullptr) { + windows.push_back(window.get()); + } + } + transferBatches.reserve(windows.size()); + + for (EditorHostWindow* window : windows) { + if (window == nullptr || + !window->HasLiveHostWindow() || + window->GetLifecycleState() != EditorWindowLifecycleState::Running) { + continue; + } + + if (window->ConsumeSkipNextSteadyStateFrame()) { + RefreshWindowPresentation(*window); + continue; + } + + EditorWindowFrameTransferRequests transferRequests = DriveWindowFrame(*window); + RefreshWindowPresentation(*window); + if (!transferRequests.HasPendingRequests()) { + continue; + } + + transferBatches.push_back(WindowFrameTransferBatch{ + window, + std::move(transferRequests), + }); + } + + for (WindowFrameTransferBatch& batch : transferBatches) { + if (batch.sourceWindow == nullptr || + !batch.sourceWindow->HasLiveHostWindow() || + batch.sourceWindow->GetLifecycleState() != EditorWindowLifecycleState::Running) { + continue; + } + + DispatchWindowFrameTransferRequests( + *batch.sourceWindow, + batch.requests); + } +} + +bool EditorWindowManager::InitializeHostWindow( + EditorHostWindow& window, + const EditorHostWindowRuntimeInitializationParams& params) { + return window.InitializeRuntime(params); +} + +EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrame( + EditorHostWindow& window) { + return DriveWindowFrameInternal(window, false); +} + +EditorWindowFrameTransferRequests EditorWindowManager::DriveImmediateWindowFrame( + EditorHostWindow& window) { + return DriveWindowFrameInternal(window, true); +} + +EditorWindowFrameTransferRequests EditorWindowManager::DriveWindowFrameInternal( + EditorHostWindow& window, + bool requestSkipNextSteadyStateFrame) { + if (!window.IsRenderReady() || + !window.HasLiveHostWindow() || + window.GetLifecycleState() != EditorWindowLifecycleState::Running) { + return {}; + } + + EditorWindowFrameTransferRequests transferRequests = + window.RenderHostFrame(IsGlobalTabDragActive()); + window.ValidateHostFrame(); + if (requestSkipNextSteadyStateFrame) { + window.RequestSkipNextSteadyStateFrame(); + } + + return transferRequests; +} + +bool EditorWindowManager::IsGlobalTabDragActive() const { + return m_workspaceInteractionController != nullptr && + m_workspaceInteractionController->IsGlobalTabDragActive(); +} + +bool EditorWindowManager::OwnsActiveGlobalTabDrag(std::string_view windowId) const { + return m_workspaceInteractionController != nullptr && + m_workspaceInteractionController->OwnsActiveGlobalTabDrag(windowId); +} + +bool EditorWindowManager::HandleGlobalTabDragPointerMove(EditorHostWindow& window) { + return m_workspaceInteractionController != nullptr && + m_workspaceInteractionController->HandleGlobalTabDragPointerMove(window); +} + +bool EditorWindowManager::HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window) { + return m_workspaceInteractionController != nullptr && + m_workspaceInteractionController->HandleGlobalTabDragPointerButtonUp(window); +} + +void EditorWindowManager::EndGlobalTabDragSession() { + if (m_workspaceInteractionController != nullptr) { + m_workspaceInteractionController->EndGlobalTabDragSession(); + } +} + +void EditorWindowManager::RefreshWindowPresentation(EditorHostWindow& window) { + if (m_workspaceSynchronizer != nullptr) { + m_workspaceSynchronizer->RefreshWindowPresentation(window); + } +} + +void EditorWindowManager::DispatchWindowFrameTransferRequests( + EditorHostWindow& sourceWindow, + const EditorWindowFrameTransferRequests& transferRequests) { + if (m_workspaceInteractionController != nullptr) { + m_workspaceInteractionController->HandleWindowFrameTransferRequests( + sourceWindow, + transferRequests); + } + HandleUtilityWindowFrameTransferRequests(sourceWindow, transferRequests); +} + +void EditorWindowManager::HandleUtilityWindowFrameTransferRequests( + EditorHostWindow& sourceWindow, + const EditorWindowFrameTransferRequests& transferRequests) { + if (transferRequests.utility.openUtilityWindow.has_value() && + transferRequests.utility.openUtilityWindow->IsValid()) { + TryProcessOpenUtilityWindowRequest( + sourceWindow, + *transferRequests.utility.openUtilityWindow); + } +} + +bool EditorWindowManager::TryProcessOpenUtilityWindowRequest( + EditorHostWindow& sourceWindow, + const EditorWindowOpenUtilityWindowRequest& request) { + if (!IsLiveWindow(&sourceWindow)) { + LogRuntimeTrace( + "utility", + "open utility window request rejected: source window is not running"); + return false; + } + + const EditorUtilityWindowDescriptor* descriptor = + ResolveEditorUtilityWindowDescriptor(request.kind); + if (descriptor == nullptr) { + LogRuntimeTrace("utility", "open utility window request rejected: unknown kind"); + return false; + } + + if (descriptor->reusePolicy == EditorUtilityWindowReusePolicy::SingleInstance) { + if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); + existingWindow != nullptr && existingWindow->IsDestroyed()) { + ReapDestroyedWindows(); + } + + if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); + IsLiveWindow(existingWindow)) { + if (!existingWindow->IsUtilityWindow()) { + LogRuntimeTrace( + "utility", + "open utility window request rejected: existing window id is not utility"); + return false; + } + existingWindow->FocusHostWindow(); + LogRuntimeTrace( + "utility", + "reused utility window '" + std::string(descriptor->windowId) + "'"); + return true; + } + + if (EditorHostWindow* existingWindow = m_hostRuntime.FindWindowById(descriptor->windowId); + existingWindow != nullptr) { + LogRuntimeTrace( + "utility", + "open utility window request rejected: existing utility window is not reusable"); + return false; + } + } + + EditorWindowCreateParams createParams = {}; + createParams.windowId = std::string(descriptor->windowId); + createParams.title = descriptor->title; + createParams.category = EditorWindowCategory::Utility; + createParams.chromePolicy = descriptor->chromePolicy; + createParams.nativeHostPolicy = descriptor->nativeHostPolicy; + createParams.primary = false; + createParams.initialWidth = ResolveOuterDimension( + descriptor->preferredOuterSize.width, + createParams.initialWidth); + createParams.initialHeight = ResolveOuterDimension( + descriptor->preferredOuterSize.height, + createParams.initialHeight); + if (request.useCursorPlacement) { + const EditorWindowScreenRect rect = m_hostRuntime.ResolveFloatingPlacement( + request.screenPoint, + createParams.initialWidth, + createParams.initialHeight); + createParams.initialX = rect.left; + createParams.initialY = rect.top; + createParams.initialWidth = rect.Width(); + createParams.initialHeight = rect.Height(); + } + + EditorHostWindow* utilityWindow = CreateUtilityWindow(*descriptor, createParams); + if (utilityWindow == nullptr) { + LogRuntimeTrace("utility", "failed to create utility window"); + return false; + } + + utilityWindow->FocusHostWindow(); + LogRuntimeTrace( + "utility", + "opened utility window '" + std::string(descriptor->windowId) + "'"); + return true; +} + +void EditorWindowManager::ExecuteCloseRequest(EditorHostWindow& window) { + if (window.IsDestroyed()) { + return; + } + + LogRuntimeTrace( + "window-close", + "ExecuteCloseRequest begin windowId='" + std::string(window.GetWindowId()) + + " primary=" + (window.IsPrimary() ? "1" : "0") + + " lifecycleBefore=" + + std::string(GetEditorWindowLifecycleStateName(window.GetLifecycleState())) + + " workspace=" + m_workspaceSynchronizer->DescribeWindowSet() + + " host=" + m_hostRuntime.DescribeWindows()); + + if (!window.IsClosing()) { + window.MarkClosing(); + } + + ShutdownRuntimeIfNeeded(window); + + if (window.HasLiveHostWindow()) { + window.DestroyHostWindow(); + } else { + window.MarkDestroyed(); + } + + LogRuntimeTrace( + "window-close", + "ExecuteCloseRequest end windowId='" + std::string(window.GetWindowId()) + + "' host=" + m_hostRuntime.DescribeWindows()); +} + +void EditorWindowManager::HandleNativeWindowDestroyed(EditorHostWindow& window) { + if (window.IsDestroyed()) { + return; + } + + const bool destroyedPrimary = + m_workspaceSynchronizer->IsPrimaryWindowId(window.GetWindowId()); + LogRuntimeTrace( + "window-close", + "HandleNativeWindowDestroyed begin windowId='" + std::string(window.GetWindowId()) + + " destroyedPrimary=" + (destroyedPrimary ? "1" : "0") + + " localPrimary=" + (window.IsPrimary() ? "1" : "0") + + " workspaceBefore=" + m_workspaceSynchronizer->DescribeWindowSet() + + " hostBefore=" + m_hostRuntime.DescribeWindows()); + + if (m_workspaceInteractionController->OwnsActiveGlobalTabDrag(window.GetWindowId())) { + m_workspaceInteractionController->EndGlobalTabDragSession(); + } + + ShutdownRuntimeIfNeeded(window); + window.MarkDestroyed(); + if (window.IsWorkspaceWindow()) { + m_workspaceSynchronizer->HandleNativeWindowDestroyed(window.GetWindowId()); + } + + if (destroyedPrimary) { + std::vector closeTargets = {}; + const std::vector windows = m_hostRuntime.GetWindows(); + closeTargets.reserve(windows.size()); + for (EditorHostWindow* otherWindow : windows) { + if (otherWindow == nullptr || + otherWindow == &window || + !otherWindow->HasLiveHostWindow() || + otherWindow->IsClosing()) { + continue; + } + closeTargets.push_back(otherWindow); + } + + for (EditorHostWindow* closeTarget : closeTargets) { + if (closeTarget != nullptr) { + RequestWindowClose(*closeTarget); + } + } + } + + LogRuntimeTrace( + "window-close", + "HandleNativeWindowDestroyed end windowId='" + std::string(window.GetWindowId()) + + "' workspaceAfter=" + m_workspaceSynchronizer->DescribeWindowSet() + + " hostAfter=" + m_hostRuntime.DescribeWindows()); +} + +void EditorWindowManager::AbortUnregisteredWindow(EditorHostWindow& window) { + LogRuntimeTrace( + "window-close", + "AbortUnregisteredWindow begin windowId='" + std::string(window.GetWindowId()) + + " hostBefore=" + m_hostRuntime.DescribeWindows()); + + if (!window.IsClosing()) { + window.MarkClosing(); + } + + ShutdownRuntimeIfNeeded(window); + if (window.HasLiveHostWindow()) { + window.DestroyHostWindow(); + } else { + window.MarkDestroyed(); + } + ReapDestroyedWindows(); + + LogRuntimeTrace( + "window-close", + "AbortUnregisteredWindow end windowId='" + std::string(window.GetWindowId()) + + "' hostAfter=" + m_hostRuntime.DescribeWindows()); +} + +void EditorWindowManager::ReapDestroyedWindows() { + m_hostRuntime.ReapDestroyedWindows(); + m_windows.erase( + std::remove_if( + m_windows.begin(), + m_windows.end(), + [](const std::unique_ptr& window) { + return window == nullptr || window->IsDestroyed(); + }), + m_windows.end()); +} + +void EditorWindowManager::ShutdownAllWindows() { + std::vector closeTargets = {}; + const std::vector windows = m_hostRuntime.GetWindows(); + closeTargets.reserve(windows.size()); + for (EditorHostWindow* window : windows) { + if (window != nullptr) { + closeTargets.push_back(window); + } + } + + for (EditorHostWindow* closeTarget : closeTargets) { + if (closeTarget != nullptr) { + ExecuteCloseRequest(*closeTarget); + } + } + + ReapDestroyedWindows(); +} + +void EditorWindowManager::ShutdownRuntimeIfNeeded(EditorHostWindow& window) { + if (window.IsRenderReady()) { + window.Shutdown(); + } +} + +void EditorWindowManager::LogRuntimeTrace( + std::string_view channel, + std::string_view message) const { + m_hostRuntime.LogRuntimeTrace(channel, message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Windowing/EditorWindowManager.h b/editor/src/Product/Runtime/Windowing/EditorWindowManager.h similarity index 75% rename from editor/app/Windowing/EditorWindowManager.h rename to editor/src/Product/Runtime/Windowing/EditorWindowManager.h index 8c2b5f2f..04afd0a0 100644 --- a/editor/app/Windowing/EditorWindowManager.h +++ b/editor/src/Product/Runtime/Windowing/EditorWindowManager.h @@ -4,13 +4,12 @@ #define NOMINMAX #endif -#include "UtilityWindows/EditorUtilityWindowRuntime.h" -#include "Windowing/EditorFrameContracts.h" -#include "Windowing/EditorWorkspaceShellRuntime.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" +#include "Product/Runtime/Shell/EditorWorkspaceShellRuntime.h" #include "EditorWindowHostCoordinator.h" #include "EditorWindowHostInterfaces.h" #include "EditorWindowHostTypes.h" -#include "EditorWindowInstance.h" +#include "Product/Runtime/Windowing/EditorWindowInstance.h" #include #include #include @@ -36,22 +35,22 @@ class EditorHostResourceService; namespace XCEngine::UI::Editor::App { class EditorWindowContentFactory; +class EditorFrameStatusController; +class EditorProductRuntime; class EditorShaderProvider; +class EditorShellDefinitionService; class EditorWindowHostRuntimeServices; -class EditorWindowLifecycleCoordinator; -class EditorUtilityWindowCoordinator; -class EditorWindowWorkspaceCoordinator; class GameViewportEngineBridge; class SceneViewportEngineBridge; +class EditorWorkspaceWindowInteractionController; +class EditorWorkspaceWindowSynchronizer; struct EditorUtilityWindowDescriptor; struct EditorWindowFrameTransferRequests; class EditorWindowManager final : public EditorWindowHostCoordinator { public: EditorWindowManager( - const EditorFrameValidation& frameValidation, - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, + EditorProductRuntime& productRuntime, std::function()> consumeOpenUtilityWindowRequest, SceneViewportEngineBridge& sceneViewportEngineBridge, GameViewportEngineBridge& gameViewportEngineBridge, @@ -61,7 +60,7 @@ public: Host::EditorHostResourceService& resourceService, EditorWindowHostRuntimeServices& hostRuntime, EditorWorkspaceShellRuntimeFactory workspaceShellRuntimeFactory, - EditorUtilityWindowPanelFactory utilityPanelFactory); + EditorUtilityWindowContentFactory utilityContentFactory); ~EditorWindowManager(); EditorWindowManager(const EditorWindowManager&) = delete; @@ -77,9 +76,12 @@ public: const EditorWindowCreateParams& params); void Shutdown(); bool RequestPrimaryWindowClose(); + void RequestWindowClose(EditorHostWindow& window); + void AbortWindow(EditorHostWindow& window); bool HasWindows() const; void DestroyClosedWindows(); + void ReapDestroyedWindows() override; void RenderAllWindows(); private: @@ -101,15 +103,22 @@ private: void ExecuteCloseRequest(EditorHostWindow& window) override; void HandleNativeWindowDestroyed(EditorHostWindow& window) override; void AbortUnregisteredWindow(EditorHostWindow& window) override; - void ReapDestroyedWindows() override; EditorWindowFrameTransferRequests DriveWindowFrameInternal( EditorHostWindow& window, bool requestSkipNextSteadyStateFrame); + void HandleUtilityWindowFrameTransferRequests( + EditorHostWindow& sourceWindow, + const EditorWindowFrameTransferRequests& transferRequests); + bool TryProcessOpenUtilityWindowRequest( + EditorHostWindow& sourceWindow, + const EditorWindowOpenUtilityWindowRequest& request); + void ShutdownAllWindows(); + void ShutdownRuntimeIfNeeded(EditorHostWindow& window); + void LogRuntimeTrace(std::string_view channel, std::string_view message) const; - const EditorFrameValidation& m_frameValidation; - const EditorShellDefinitionProvider& m_shellDefinitionProvider; - EditorFrameStatusService& m_frameStatusService; + const EditorShellDefinitionService& m_shellDefinitionService; + EditorFrameStatusController& m_frameStatusController; std::function()> m_consumeOpenUtilityWindowRequest = {}; SceneViewportEngineBridge& m_sceneViewportEngineBridge; @@ -119,9 +128,8 @@ private: Host::EditorHostResourceService& m_resourceService; EditorWindowHostRuntimeServices& m_hostRuntime; std::unique_ptr m_contentFactory = {}; - std::unique_ptr m_lifecycleCoordinator = {}; - std::unique_ptr m_utilityCoordinator = {}; - std::unique_ptr m_workspaceCoordinator = {}; + std::unique_ptr m_workspaceSynchronizer = {}; + std::unique_ptr m_workspaceInteractionController = {}; std::vector> m_windows = {}; }; diff --git a/editor/app/Windowing/EditorWindowVisuals.h b/editor/src/Product/Runtime/Windowing/EditorWindowVisuals.h similarity index 100% rename from editor/app/Windowing/EditorWindowVisuals.h rename to editor/src/Product/Runtime/Windowing/EditorWindowVisuals.h diff --git a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp b/editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.cpp similarity index 93% rename from editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp rename to editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.cpp index c80ea943..ff620868 100644 --- a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.cpp +++ b/editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.cpp @@ -1,8 +1,9 @@ -#include "Frame/EditorWindowFrameOrchestrator.h" +#include "Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.h" -#include "Windowing/EditorWindowMetrics.h" -#include "EditorWindowDiagnostics.h" -#include "EditorWindowVisuals.h" +#include "Product/Core/Windowing/EditorWindowMetrics.h" +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" +#include "Product/Runtime/Windowing/EditorWindowDiagnostics.h" +#include "Product/Runtime/Windowing/EditorWindowVisuals.h" #include #include @@ -43,8 +44,7 @@ std::string DescribeInputEventType(const UIInputEvent& event) { } // namespace EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::UpdateAndAppend( - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, + const EditorFrameStatusController& frameStatusController, const std::function()>* consumeOpenUtilityWindowRequest, UIEditorWorkspaceController& workspaceController, EditorWorkspaceShellRuntime& shellRuntime, @@ -57,14 +57,12 @@ EditorWindowFrameTransferRequests EditorWindowFrameOrchestrator::UpdateAndAppend bool useDetachedTitleBarTabStrip, UIDrawData& drawData) const { LogInputTrace( - frameStatusService, + frameStatusController, workspaceController, shellRuntime.GetShellInteractionState(), frameEvents); shellRuntime.Update( - shellDefinitionProvider, - frameStatusService, workspaceController, workspaceBounds, frameEvents, @@ -127,7 +125,7 @@ std::string EditorWindowFrameOrchestrator::DescribeInputEvents( } void EditorWindowFrameOrchestrator::LogInputTrace( - const EditorFrameStatusService& frameStatusService, + const EditorFrameStatusController& frameStatusController, const UIEditorWorkspaceController& workspaceController, const UIEditorShellInteractionState& shellInteractionState, const std::vector& frameEvents) const { @@ -138,7 +136,7 @@ void EditorWindowFrameOrchestrator::LogInputTrace( AppendUIEditorRuntimeTrace( "input", DescribeInputEvents(frameEvents) + " | " + - frameStatusService.DescribeWorkspaceState( + frameStatusController.DescribeWorkspaceState( workspaceController, shellInteractionState)); } diff --git a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h b/editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.h similarity index 88% rename from editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h rename to editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.h index 03584b1c..05508a89 100644 --- a/editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.h +++ b/editor/src/Product/Runtime/Windowing/Frame/EditorWindowFrameOrchestrator.h @@ -1,8 +1,7 @@ #pragma once -#include "Windowing/EditorWindowTransferRequests.h" -#include "Windowing/EditorFrameContracts.h" -#include "Windowing/EditorWorkspaceShellRuntime.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" +#include "Product/Runtime/Shell/EditorWorkspaceShellRuntime.h" #include @@ -33,6 +32,8 @@ struct UIEditorShellInteractionState; namespace XCEngine::UI::Editor::App { +class EditorFrameStatusController; + class EditorWindowFrameOrchestrator final { public: EditorWindowFrameOrchestrator() = default; @@ -44,8 +45,7 @@ public: EditorWindowFrameOrchestrator& operator=(EditorWindowFrameOrchestrator&&) = delete; EditorWindowFrameTransferRequests UpdateAndAppend( - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, + const EditorFrameStatusController& frameStatusController, const std::function()>* consumeOpenUtilityWindowRequest, UIEditorWorkspaceController& workspaceController, @@ -63,7 +63,7 @@ public: private: void LogInputTrace( - const EditorFrameStatusService& frameStatusService, + const EditorFrameStatusController& frameStatusController, const UIEditorWorkspaceController& workspaceController, const UIEditorShellInteractionState& shellInteractionState, const std::vector<::XCEngine::UI::UIInputEvent>& frameEvents) const; diff --git a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.cpp similarity index 95% rename from editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp rename to editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.cpp index 745d73a4..0f48de80 100644 --- a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.cpp +++ b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.cpp @@ -1,7 +1,9 @@ -#include "Runtime/EditorWindowRuntimeController.h" +#include "Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h" #include "EditorHostResourceService.h" -#include "TextFormat.h" +#include "Product/Runtime/Diagnostics/EditorFrameStatusController.h" +#include "Product/Runtime/Shell/EditorShellDefinitionService.h" +#include "Product/Support/TextFormat.h" #include #include @@ -44,9 +46,8 @@ bool LoadHostPngTexture( } EditorWindowRuntimeController::EditorWindowRuntimeController( - const EditorFrameValidation& frameValidation, - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, std::function()> consumeOpenUtilityWindowRequest, SceneViewportEngineBridge& sceneViewportEngineBridge, GameViewportEngineBridge& gameViewportEngineBridge, @@ -54,9 +55,8 @@ EditorWindowRuntimeController::EditorWindowRuntimeController( Host::EditorHostResourceService& resourceService, std::unique_ptr contentController, std::unique_ptr renderRuntime) - : m_frameValidation(frameValidation) - , m_shellDefinitionProvider(shellDefinitionProvider) - , m_frameStatusService(frameStatusService) + : m_shellDefinitionService(shellDefinitionService) + , m_frameStatusController(frameStatusController) , m_consumeOpenUtilityWindowRequest(std::move(consumeOpenUtilityWindowRequest)) , m_sceneViewportEngineBridge(sceneViewportEngineBridge) , m_gameViewportEngineBridge(gameViewportEngineBridge) @@ -214,7 +214,7 @@ bool EditorWindowRuntimeController::Initialize( m_resourceService.GetExecutableDirectory()); if (autoCaptureOnStartup) { m_screenshotController.RequestCapture("startup"); - m_contentController->NotifyStartupCaptureRequested(m_frameStatusService); + m_contentController->NotifyStartupCaptureRequested(m_frameStatusController); } return true; @@ -273,13 +273,13 @@ bool EditorWindowRuntimeController::ApplyResize(std::uint32_t width, std::uint32 bool EditorWindowRuntimeController::IsFrameValid() const { return m_contentController != nullptr && - m_contentController->IsFrameValid(m_frameValidation); + m_contentController->IsFrameValid(m_shellDefinitionService); } void EditorWindowRuntimeController::AppendInvalidFrame( ::XCEngine::UI::UIDrawList& drawList) const { if (m_contentController != nullptr) { - m_contentController->AppendInvalidFrame(m_frameValidation, drawList); + m_contentController->AppendInvalidFrame(m_shellDefinitionService, drawList); } } @@ -339,8 +339,6 @@ EditorWindowFrameTransferRequests EditorWindowRuntimeController::UpdateAndAppend assert(m_contentController != nullptr); return m_contentController->UpdateAndAppend( EditorWindowContentFrameContext{ - .shellDefinitionProvider = m_shellDefinitionProvider, - .frameStatusService = m_frameStatusService, .consumeOpenUtilityWindowRequest = &m_consumeOpenUtilityWindowRequest, .bounds = bounds, .inputEvents = inputEvents, diff --git a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h similarity index 89% rename from editor/app/Windowing/Runtime/EditorWindowRuntimeController.h rename to editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h index df2a74be..22a5e12a 100644 --- a/editor/app/Windowing/Runtime/EditorWindowRuntimeController.h +++ b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowRuntimeController.h @@ -4,10 +4,9 @@ #define NOMINMAX #endif -#include "Content/EditorWindowContentController.h" -#include "Environment/EditorRuntimePaths.h" -#include "Runtime/EditorWindowScreenshotController.h" -#include "Windowing/EditorFrameContracts.h" +#include "Product/Runtime/Windowing/Content/EditorWindowContentController.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.h" #include "EditorWindowRenderRuntime.h" @@ -36,15 +35,16 @@ class EditorHostResourceService; namespace XCEngine::UI::Editor::App { class EditorShaderProvider; +class EditorFrameStatusController; +class EditorShellDefinitionService; class GameViewportEngineBridge; class SceneViewportEngineBridge; class EditorWindowRuntimeController final { public: EditorWindowRuntimeController( - const EditorFrameValidation& frameValidation, - const EditorShellDefinitionProvider& shellDefinitionProvider, - EditorFrameStatusService& frameStatusService, + const EditorShellDefinitionService& shellDefinitionService, + EditorFrameStatusController& frameStatusController, std::function()> consumeOpenUtilityWindowRequest, SceneViewportEngineBridge& sceneViewportEngineBridge, GameViewportEngineBridge& gameViewportEngineBridge, @@ -111,9 +111,8 @@ private: void UpdateFrameTiming(); void RefreshDisplayedFrameStats(); - const EditorFrameValidation& m_frameValidation; - const EditorShellDefinitionProvider& m_shellDefinitionProvider; - EditorFrameStatusService& m_frameStatusService; + const EditorShellDefinitionService& m_shellDefinitionService; + EditorFrameStatusController& m_frameStatusController; std::function()> m_consumeOpenUtilityWindowRequest = {}; SceneViewportEngineBridge& m_sceneViewportEngineBridge; diff --git a/editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.cpp similarity index 98% rename from editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp rename to editor/src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.cpp index 0cddf9c3..c1d45cc3 100644 --- a/editor/app/Windowing/Runtime/EditorWindowScreenshotController.cpp +++ b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.cpp @@ -1,4 +1,4 @@ -#include "Runtime/EditorWindowScreenshotController.h" +#include "Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.h" #include #include diff --git a/editor/app/Windowing/Runtime/EditorWindowScreenshotController.h b/editor/src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.h similarity index 100% rename from editor/app/Windowing/Runtime/EditorWindowScreenshotController.h rename to editor/src/Product/Runtime/Windowing/Runtime/EditorWindowScreenshotController.h diff --git a/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.cpp b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.cpp new file mode 100644 index 00000000..ff1c1b11 --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.cpp @@ -0,0 +1,542 @@ +#include "Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h" + +#include "Product/Runtime/EditorProductRuntime.h" +#include "EditorWindowContentBindings.h" + +#include +#include +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +using ::XCEngine::UI::UIPoint; + +constexpr std::int32_t kFallbackDragHotspotX = 40; +constexpr std::int32_t kFallbackDragHotspotY = 12; + +EditorWindowScreenPoint BuildFallbackGlobalTabDragHotspot() { + EditorWindowScreenPoint hotspot = {}; + hotspot.x = kFallbackDragHotspotX; + hotspot.y = kFallbackDragHotspotY; + return hotspot; +} + +bool CanStartGlobalTabDragFromWindow( + const EditorProductRuntime& productRuntime, + const EditorHostWindow& sourceWindow, + std::string_view sourceNodeId, + std::string_view panelId) { + const UIEditorWindowWorkspaceState* windowState = + productRuntime.FindWindowWorkspaceState(sourceWindow.GetWindowId()); + if (windowState == nullptr) { + return false; + } + + UIEditorWorkspaceModel workspace = windowState->workspace; + UIEditorWorkspaceSession session = windowState->session; + UIEditorWorkspaceExtractedPanel extractedPanel = {}; + return TryExtractUIEditorWorkspaceVisiblePanel( + workspace, + session, + sourceNodeId, + panelId, + extractedPanel); +} + +bool IsLiveInteractiveWindow(const EditorHostWindow* window) { + return window != nullptr && + window->HasLiveHostWindow() && + window->GetLifecycleState() == EditorWindowLifecycleState::Running; +} + +} // namespace + +EditorWorkspaceWindowInteractionController::EditorWorkspaceWindowInteractionController( + EditorWindowHost& hostRuntime, + EditorProductRuntime& productRuntime, + EditorWorkspaceWindowSynchronizer& workspaceSynchronizer) + : m_hostRuntime(hostRuntime), + m_productRuntime(productRuntime), + m_workspaceSynchronizer(workspaceSynchronizer) {} + +EditorWorkspaceWindowInteractionController::~EditorWorkspaceWindowInteractionController() = default; + +bool EditorWorkspaceWindowInteractionController::IsGlobalTabDragActive() const { + return m_globalTabDragSession.active; +} + +bool EditorWorkspaceWindowInteractionController::OwnsActiveGlobalTabDrag( + std::string_view windowId) const { + return m_globalTabDragSession.active && + m_globalTabDragSession.panelWindowId == windowId; +} + +void EditorWorkspaceWindowInteractionController::BeginGlobalTabDragSession( + std::string_view panelWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + const EditorWindowScreenPoint& screenPoint, + const EditorWindowScreenPoint& dragHotspot) { + m_globalTabDragSession.active = true; + m_globalTabDragSession.panelWindowId = std::string(panelWindowId); + m_globalTabDragSession.sourceNodeId = std::string(sourceNodeId); + m_globalTabDragSession.panelId = std::string(panelId); + m_globalTabDragSession.screenPoint = screenPoint; + m_globalTabDragSession.dragHotspot = dragHotspot; +} + +bool EditorWorkspaceWindowInteractionController::TryResolveGlobalTabDragHotspot( + const EditorHostWindow& sourceWindow, + std::string_view nodeId, + std::string_view panelId, + const EditorWindowScreenPoint& screenPoint, + EditorWindowScreenPoint& outDragHotspot) const { + return sourceWindow.TryResolveDockTabDragHotspot( + nodeId, + panelId, + screenPoint, + outDragHotspot); +} + +void EditorWorkspaceWindowInteractionController::UpdateGlobalTabDragOwnerWindowPosition() { + if (!m_globalTabDragSession.active) { + return; + } + + EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); + if (!IsLiveInteractiveWindow(ownerWindow)) { + return; + } + + EditorWindowScreenRect windowRect = {}; + if (!ownerWindow->TryGetHostScreenRect(windowRect)) { + return; + } + + const std::int32_t targetLeft = + m_globalTabDragSession.screenPoint.x - m_globalTabDragSession.dragHotspot.x; + const std::int32_t targetTop = + m_globalTabDragSession.screenPoint.y - m_globalTabDragSession.dragHotspot.y; + if (windowRect.left == targetLeft && windowRect.top == targetTop) { + return; + } + + ownerWindow->SetHostScreenPosition(EditorWindowScreenPoint{ + .x = targetLeft, + .y = targetTop, + }); +} + +void EditorWorkspaceWindowInteractionController::ClearGlobalTabDragDropPreview() { + if (m_globalTabDragSession.previewWindowId.empty()) { + return; + } + + if (EditorHostWindow* previewWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.previewWindowId); + IsLiveInteractiveWindow(previewWindow)) { + if (EditorWindowDockHostBinding* dockHostBinding = + previewWindow->TryGetDockHostBinding(); + dockHostBinding != nullptr) { + dockHostBinding->ClearExternalDockHostDropPreview(); + previewWindow->InvalidateHostWindow(); + } + } + m_globalTabDragSession.previewWindowId.clear(); +} + +void EditorWorkspaceWindowInteractionController::UpdateGlobalTabDragDropPreview() { + if (!m_globalTabDragSession.active) { + return; + } + + EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint( + m_globalTabDragSession.screenPoint, + m_globalTabDragSession.panelWindowId); + if (!IsLiveInteractiveWindow(targetWindow)) { + ClearGlobalTabDragDropPreview(); + return; + } + + UIEditorDockHostTabDropTarget dropTarget = {}; + if (!targetWindow->TryResolveDockTabDropTarget( + m_globalTabDragSession.screenPoint, + dropTarget)) { + ClearGlobalTabDragDropPreview(); + return; + } + + if (!m_globalTabDragSession.previewWindowId.empty() && + m_globalTabDragSession.previewWindowId != targetWindow->GetWindowId()) { + ClearGlobalTabDragDropPreview(); + } + + Widgets::UIEditorDockHostDropPreviewState preview = {}; + preview.visible = true; + preview.sourceNodeId = m_globalTabDragSession.sourceNodeId; + preview.sourcePanelId = m_globalTabDragSession.panelId; + preview.targetNodeId = dropTarget.nodeId; + preview.placement = dropTarget.placement; + preview.insertionIndex = dropTarget.insertionIndex; + if (EditorWindowDockHostBinding* dockHostBinding = + targetWindow->TryGetDockHostBinding(); + dockHostBinding != nullptr) { + dockHostBinding->SetExternalDockHostDropPreview(preview); + targetWindow->InvalidateHostWindow(); + } + m_globalTabDragSession.previewWindowId = std::string(targetWindow->GetWindowId()); +} + +void EditorWorkspaceWindowInteractionController::EndGlobalTabDragSession() { + if (!m_globalTabDragSession.active) { + return; + } + + ClearGlobalTabDragDropPreview(); + + if (EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); + ownerWindow != nullptr) { + ownerWindow->ReleasePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag); + if (IsLiveInteractiveWindow(ownerWindow)) { + ownerWindow->ResetInteractionState(); + } + } + + m_globalTabDragSession = {}; +} + +bool EditorWorkspaceWindowInteractionController::HandleGlobalTabDragPointerMove( + EditorHostWindow& window) { + if (!m_globalTabDragSession.active) { + return false; + } + + const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); + if (!IsLiveInteractiveWindow(ownerWindow)) { + EndGlobalTabDragSession(); + return false; + } + if (ownerWindow->GetWindowId() != window.GetWindowId()) { + return false; + } + + EditorWindowScreenPoint screenPoint = {}; + if (m_hostRuntime.TryGetCursorScreenPoint(screenPoint)) { + m_globalTabDragSession.screenPoint = screenPoint; + UpdateGlobalTabDragOwnerWindowPosition(); + UpdateGlobalTabDragDropPreview(); + } + return true; +} + +bool EditorWorkspaceWindowInteractionController::HandleGlobalTabDragPointerButtonUp( + EditorHostWindow& window) { + if (!m_globalTabDragSession.active) { + return false; + } + + const EditorHostWindow* ownerWindow = m_hostRuntime.FindWindowById(m_globalTabDragSession.panelWindowId); + if (!IsLiveInteractiveWindow(ownerWindow)) { + EndGlobalTabDragSession(); + return false; + } + if (ownerWindow->GetWindowId() != window.GetWindowId()) { + return false; + } + + EditorWindowScreenPoint screenPoint = m_globalTabDragSession.screenPoint; + m_hostRuntime.TryGetCursorScreenPoint(screenPoint); + + const std::string panelWindowId = m_globalTabDragSession.panelWindowId; + const std::string sourceNodeId = m_globalTabDragSession.sourceNodeId; + const std::string panelId = m_globalTabDragSession.panelId; + EndGlobalTabDragSession(); + + EditorHostWindow* targetWindow = FindTopmostWindowAtScreenPoint(screenPoint, panelWindowId); + if (!IsLiveInteractiveWindow(targetWindow)) { + return true; + } + + UIEditorDockHostTabDropTarget dropTarget = {}; + if (!targetWindow->TryResolveDockTabDropTarget(screenPoint, dropTarget)) { + return true; + } + + const EditorWorkspaceWindowMutationResult result = + dropTarget.placement == UIEditorWorkspaceDockPlacement::Center + ? m_workspaceSynchronizer.MovePanelToStack( + panelWindowId, + sourceNodeId, + panelId, + targetWindow->GetWindowId(), + dropTarget.nodeId, + dropTarget.insertionIndex) + : m_workspaceSynchronizer.DockPanelRelative( + panelWindowId, + sourceNodeId, + panelId, + targetWindow->GetWindowId(), + dropTarget.nodeId, + dropTarget.placement); + if (result.operation.status != UIEditorWindowWorkspaceOperationStatus::Changed) { + LogRuntimeTrace("drag", "cross-window drop rejected: " + result.operation.message); + return true; + } + + if (!result.committed) { + LogRuntimeTrace( + "drag", + "failed to synchronize windows after cross-window drop: " + + result.errorMessage); + return true; + } + + if (EditorHostWindow* updatedTargetWindow = m_hostRuntime.FindWindowById(targetWindow->GetWindowId()); + IsLiveInteractiveWindow(updatedTargetWindow)) { + updatedTargetWindow->FocusHostWindow(); + } + LogRuntimeTrace( + "drag", + "committed cross-window drop panel '" + panelId + + "' into window '" + std::string(targetWindow->GetWindowId()) + "'"); + return true; +} + +bool EditorWorkspaceWindowInteractionController::TryStartGlobalTabDrag( + EditorHostWindow& sourceWindow, + const EditorWindowPanelTransferRequest& request) { + if (!sourceWindow.IsWorkspaceWindow()) { + LogRuntimeTrace( + "drag", + "failed to start global tab drag: source window is not a workspace window"); + return false; + } + + if (!IsLiveInteractiveWindow(&sourceWindow)) { + LogRuntimeTrace("drag", "failed to start global tab drag: source window is closing"); + return false; + } + + EditorWindowScreenPoint dragHotspot = BuildFallbackGlobalTabDragHotspot(); + TryResolveGlobalTabDragHotspot( + sourceWindow, + request.nodeId, + request.panelId, + request.screenPoint, + dragHotspot); + + const auto tryStartDetachedPanelGlobalDrag = + [this, &request, &dragHotspot]( + const EditorWorkspaceWindowMutationResult& result) { + if (!result.committed) { + LogRuntimeTrace( + "drag", + "failed to synchronize detached drag window state: " + + result.errorMessage); + return false; + } + + EditorHostWindow* detachedWindow = + m_hostRuntime.FindWindowById(result.operation.targetWindowId); + if (!IsLiveInteractiveWindow(detachedWindow)) { + LogRuntimeTrace("drag", "detached drag window was not created."); + return false; + } + + const UIEditorWindowWorkspaceState* detachedWindowState = + m_productRuntime.FindWindowWorkspaceState(result.operation.targetWindowId); + if (detachedWindowState == nullptr) { + LogRuntimeTrace("drag", "detached drag window state was not committed."); + return false; + } + + BeginGlobalTabDragSession( + detachedWindow->GetWindowId(), + detachedWindowState->workspace.root.nodeId, + request.panelId, + request.screenPoint, + dragHotspot); + UpdateGlobalTabDragOwnerWindowPosition(); + detachedWindow->AcquirePointerCapture( + EditorWindowPointerCaptureOwner::GlobalTabDrag); + detachedWindow->FocusHostWindow(); + LogRuntimeTrace( + "drag", + "started global tab drag by detaching panel '" + request.panelId + + "' into window '" + std::string(detachedWindow->GetWindowId()) + "'"); + return true; + }; + + const EditorWorkspaceWindowMutationResult result = + m_workspaceSynchronizer.DetachPanelToNewWindow( + sourceWindow.GetWindowId(), + request.nodeId, + request.panelId, + request.screenPoint); + if (result.operation.status == UIEditorWindowWorkspaceOperationStatus::Changed) { + return tryStartDetachedPanelGlobalDrag(result); + } + + if (sourceWindow.IsPrimary()) { + LogRuntimeTrace( + "drag", + "failed to start global tab drag from primary window: " + + result.operation.message); + return false; + } + + sourceWindow.ResetInteractionState(); + if (result.operation.status != UIEditorWindowWorkspaceOperationStatus::NoOp) { + LogRuntimeTrace( + "drag", + "failed to start global tab drag from detached window: " + + result.operation.message); + return false; + } + if (!CanStartGlobalTabDragFromWindow( + m_productRuntime, + sourceWindow, + request.nodeId, + request.panelId)) { + LogRuntimeTrace( + "drag", + "failed to start global tab drag from detached window: invalid source panel request"); + return false; + } + + BeginGlobalTabDragSession( + sourceWindow.GetWindowId(), + request.nodeId, + request.panelId, + request.screenPoint, + dragHotspot); + UpdateGlobalTabDragOwnerWindowPosition(); + sourceWindow.AcquirePointerCapture(EditorWindowPointerCaptureOwner::GlobalTabDrag); + LogRuntimeTrace( + "drag", + "started global tab drag from detached window '" + + std::string(sourceWindow.GetWindowId()) + + "' panel '" + request.panelId + "'"); + return true; +} + +bool EditorWorkspaceWindowInteractionController::TryProcessDetachRequest( + EditorHostWindow& sourceWindow, + const EditorWindowPanelTransferRequest& request) { + if (!sourceWindow.IsWorkspaceWindow()) { + LogRuntimeTrace( + "detach", + "detach request rejected: source window is not a workspace window"); + return false; + } + + if (!IsLiveInteractiveWindow(&sourceWindow)) { + LogRuntimeTrace("detach", "detach request rejected: source window is closing"); + return false; + } + + const std::string sourceWindowId(sourceWindow.GetWindowId()); + const EditorWorkspaceWindowMutationResult result = + m_workspaceSynchronizer.DetachPanelToNewWindow( + sourceWindowId, + request.nodeId, + request.panelId, + request.screenPoint); + if (result.operation.status != UIEditorWindowWorkspaceOperationStatus::Changed) { + LogRuntimeTrace( + "detach", + "detach request rejected: " + result.operation.message); + return false; + } + + if (!result.committed) { + LogRuntimeTrace( + "detach", + "failed to synchronize detached window state: " + + result.errorMessage); + return false; + } + + if (EditorHostWindow* detachedWindow = + m_hostRuntime.FindWindowById(result.operation.targetWindowId); + IsLiveInteractiveWindow(detachedWindow)) { + detachedWindow->FocusHostWindow(); + } + + LogRuntimeTrace( + "detach", + "detached panel '" + request.panelId + "' from window '" + sourceWindowId + + "' to window '" + result.operation.targetWindowId + "'"); + return true; +} + +void EditorWorkspaceWindowInteractionController::HandleWindowFrameTransferRequests( + EditorHostWindow& sourceWindow, + const EditorWindowFrameTransferRequests& transferRequests) { + if (!m_globalTabDragSession.active && + transferRequests.workspace.beginGlobalTabDrag.has_value() && + transferRequests.workspace.beginGlobalTabDrag->IsValid()) { + TryStartGlobalTabDrag( + sourceWindow, + *transferRequests.workspace.beginGlobalTabDrag); + } + + if (!m_globalTabDragSession.active && + transferRequests.workspace.detachPanel.has_value() && + transferRequests.workspace.detachPanel->IsValid()) { + TryProcessDetachRequest( + sourceWindow, + *transferRequests.workspace.detachPanel); + } +} + +EditorHostWindow* EditorWorkspaceWindowInteractionController::FindTopmostWindowAtScreenPoint( + const EditorWindowScreenPoint& screenPoint, + std::string_view excludedWindowId) { + if (EditorHostWindow* window = m_hostRuntime.FindWindowFromScreenPoint(screenPoint); + IsLiveInteractiveWindow(window) && + window->IsWorkspaceWindow() && + window->GetWindowId() != excludedWindowId) { + return window; + } + + std::vector windows = m_hostRuntime.GetWindows(); + for (auto it = windows.rbegin(); it != windows.rend(); ++it) { + EditorHostWindow* const window = *it; + if (window == nullptr || + !IsLiveInteractiveWindow(window) || + !window->IsWorkspaceWindow() || + window->GetWindowId() == excludedWindowId) { + continue; + } + + EditorWindowScreenRect windowRect = {}; + if (window->TryGetHostScreenRect(windowRect) && + windowRect.Contains(screenPoint)) { + return window; + } + } + + return nullptr; +} + +const EditorHostWindow* EditorWorkspaceWindowInteractionController::FindTopmostWindowAtScreenPoint( + const EditorWindowScreenPoint& screenPoint, + std::string_view excludedWindowId) const { + return const_cast(this)->FindTopmostWindowAtScreenPoint( + screenPoint, + excludedWindowId); +} + +void EditorWorkspaceWindowInteractionController::LogRuntimeTrace( + std::string_view channel, + std::string_view message) const { + m_hostRuntime.LogRuntimeTrace(channel, message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h new file mode 100644 index 00000000..979272ef --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowInteractionController.h @@ -0,0 +1,81 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h" +#include "Product/Core/Windowing/EditorWindowTransferRequests.h" +#include "EditorWindowHostInterfaces.h" + +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorProductRuntime; + +class EditorWorkspaceWindowInteractionController final { +public: + EditorWorkspaceWindowInteractionController( + EditorWindowHost& hostRuntime, + EditorProductRuntime& productRuntime, + EditorWorkspaceWindowSynchronizer& workspaceSynchronizer); + ~EditorWorkspaceWindowInteractionController(); + + bool IsGlobalTabDragActive() const; + bool OwnsActiveGlobalTabDrag(std::string_view windowId) const; + void EndGlobalTabDragSession(); + bool HandleGlobalTabDragPointerMove(EditorHostWindow& window); + bool HandleGlobalTabDragPointerButtonUp(EditorHostWindow& window); + void HandleWindowFrameTransferRequests( + EditorHostWindow& sourceWindow, + const EditorWindowFrameTransferRequests& transferRequests); + +private: + struct GlobalTabDragSession { + bool active = false; + std::string panelWindowId = {}; + std::string sourceNodeId = {}; + std::string panelId = {}; + std::string previewWindowId = {}; + EditorWindowScreenPoint screenPoint = {}; + EditorWindowScreenPoint dragHotspot = {}; + }; + + void BeginGlobalTabDragSession( + std::string_view panelWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + const EditorWindowScreenPoint& screenPoint, + const EditorWindowScreenPoint& dragHotspot); + bool TryResolveGlobalTabDragHotspot( + const EditorHostWindow& sourceWindow, + std::string_view nodeId, + std::string_view panelId, + const EditorWindowScreenPoint& screenPoint, + EditorWindowScreenPoint& outDragHotspot) const; + void ClearGlobalTabDragDropPreview(); + void UpdateGlobalTabDragDropPreview(); + void UpdateGlobalTabDragOwnerWindowPosition(); + EditorHostWindow* FindTopmostWindowAtScreenPoint( + const EditorWindowScreenPoint& screenPoint, + std::string_view excludedWindowId = {}); + const EditorHostWindow* FindTopmostWindowAtScreenPoint( + const EditorWindowScreenPoint& screenPoint, + std::string_view excludedWindowId = {}) const; + bool TryStartGlobalTabDrag( + EditorHostWindow& sourceWindow, + const EditorWindowPanelTransferRequest& request); + bool TryProcessDetachRequest( + EditorHostWindow& sourceWindow, + const EditorWindowPanelTransferRequest& request); + void LogRuntimeTrace(std::string_view channel, std::string_view message) const; + + EditorWindowHost& m_hostRuntime; + EditorProductRuntime& m_productRuntime; + EditorWorkspaceWindowSynchronizer& m_workspaceSynchronizer; + GlobalTabDragSession m_globalTabDragSession = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.cpp b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.cpp new file mode 100644 index 00000000..3355547e --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.cpp @@ -0,0 +1,448 @@ +#include "Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h" + +#include "Product/Runtime/EditorProductRuntime.h" +#include "Product/Runtime/Windowing/EditorWindowManager.h" + +#include +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +struct ExistingWindowSnapshot { + std::string windowId = {}; + EditorWorkspaceWindowProjection projection = {}; + bool primary = false; +}; + +std::string DescribeWindowSetState(const UIEditorWindowWorkspaceSet& windowSet) { + std::ostringstream stream = {}; + stream << "primary='" << windowSet.primaryWindowId + << "' active='" << windowSet.activeWindowId + << "' count=" << windowSet.windows.size() + << " windows=["; + bool first = true; + for (const UIEditorWindowWorkspaceState& state : windowSet.windows) { + if (!first) { + stream << ','; + } + first = false; + stream << state.windowId; + } + stream << ']'; + return stream.str(); +} + +} // namespace + +EditorWorkspaceWindowSynchronizer::EditorWorkspaceWindowSynchronizer( + EditorWindowHost& hostRuntime, + EditorWindowManager& windowManager, + EditorProductRuntime& productRuntime, + EditorWindowSystem& windowSystem) + : m_hostRuntime(hostRuntime), + m_windowManager(windowManager), + m_productRuntime(productRuntime), + m_windowSystem(windowSystem) {} + +EditorWorkspaceWindowSynchronizer::~EditorWorkspaceWindowSynchronizer() = default; + +void EditorWorkspaceWindowSynchronizer::RegisterExistingWindow(EditorHostWindow& window) const { + RefreshWorkspaceProjectionFromAuthoritativeState(window); + RefreshWindowTitle(window); +} + +void EditorWorkspaceWindowSynchronizer::RefreshWindowPresentation(EditorHostWindow& window) const { + if (!window.IsWorkspaceWindow()) { + return; + } + + RefreshWorkspaceProjectionFromAuthoritativeState(window); + RefreshWindowTitle(window); +} + +void EditorWorkspaceWindowSynchronizer::HandleNativeWindowDestroyed(std::string_view windowId) { + std::string error = {}; + EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForDestroyedWindow( + m_productRuntime.GetWindowWorkspace(), + windowId, + CaptureHostSnapshots(), + m_hostRuntime.GetPrimaryWindowTitle(), + error); + if (!plan.valid) { + LogRuntimeTrace( + "window-close", + "workspace destroy reconciliation rejected for window '" + + std::string(windowId) + "': " + error); + return; + } + + if (!ApplySynchronizationPlan(plan, error)) { + LogRuntimeTrace( + "window-close", + "workspace destroy reconciliation execution failed for window '" + + std::string(windowId) + "': " + error); + } +} + +bool EditorWorkspaceWindowSynchronizer::IsPrimaryWindowId(std::string_view windowId) const { + return m_productRuntime.IsPrimaryWorkspaceWindow(windowId); +} + +std::string EditorWorkspaceWindowSynchronizer::DescribeWindowSet() const { + return DescribeWindowSetState(m_productRuntime.GetWindowWorkspace()); +} + +bool EditorWorkspaceWindowSynchronizer::SynchronizeWindowsFromWindowSet( + const UIEditorWindowWorkspaceSet& windowSet, + std::string_view preferredNewWindowId, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth, + int preferredHeight, + std::string& outError) { + EditorWindowSynchronizationPlacement preferredPlacement = {}; + if (!preferredNewWindowId.empty()) { + const EditorWindowScreenRect detachedRect = m_hostRuntime.ResolveFloatingPlacement( + preferredScreenPoint, + preferredWidth, + preferredHeight); + preferredPlacement.enabled = true; + preferredPlacement.initialX = detachedRect.left; + preferredPlacement.initialY = detachedRect.top; + preferredPlacement.initialWidth = detachedRect.Width(); + preferredPlacement.initialHeight = detachedRect.Height(); + } + + std::string error = {}; + EditorWindowSynchronizationPlan plan = m_windowSystem.BuildPlanForWindowSet( + windowSet, + CaptureHostSnapshots(), + m_hostRuntime.GetPrimaryWindowTitle(), + preferredNewWindowId, + preferredPlacement, + error); + if (!plan.valid) { + outError = error; + return false; + } + + return ApplySynchronizationPlan(plan, outError); +} + +std::vector EditorWorkspaceWindowSynchronizer::CaptureHostSnapshots() const { + std::vector snapshots = {}; + const std::vector windows = + std::as_const(m_hostRuntime).GetWindows(); + snapshots.reserve(windows.size()); + + for (const EditorHostWindow* window : windows) { + if (window == nullptr) { + continue; + } + + EditorWindowHostSnapshot snapshot = {}; + snapshot.windowId = std::string(window->GetWindowId()); + snapshot.workspaceWindow = window->IsWorkspaceWindow(); + snapshot.utilityWindow = window->IsUtilityWindow(); + snapshot.primary = window->IsPrimary(); + snapshot.running = window->GetLifecycleState() == EditorWindowLifecycleState::Running; + snapshot.destroyed = window->IsDestroyed(); + snapshot.hasNativeWindow = window->HasLiveHostWindow(); + snapshot.title = window->GetTitle(); + if (window->IsWorkspaceWindow()) { + if (const UIEditorWindowWorkspaceState* windowState = + m_productRuntime.FindWindowWorkspaceState(window->GetWindowId()); + windowState != nullptr) { + snapshot.hasWorkspaceState = true; + snapshot.workspaceState = *windowState; + } + } + snapshots.push_back(std::move(snapshot)); + } + + return snapshots; +} + +bool EditorWorkspaceWindowSynchronizer::ApplySynchronizationPlan( + const EditorWindowSynchronizationPlan& plan, + std::string& outError) { + const auto restoreWindowSnapshot = [this](const ExistingWindowSnapshot& snapshot) { + EditorHostWindow* const window = m_hostRuntime.FindWindowById(snapshot.windowId); + if (window == nullptr) { + return; + } + + window->SetPrimary(snapshot.primary); + window->RefreshWorkspaceProjection(snapshot.projection); + window->ResetInteractionState(); + RefreshWindowTitle(*window); + }; + + const auto destroyAndEraseWindowById = [this](std::string_view windowId) { + EditorHostWindow* const window = m_hostRuntime.FindWindowById(windowId); + if (window == nullptr) { + return false; + } + + m_windowManager.AbortWindow(*window); + return true; + }; + + std::vector existingWindowSnapshots = {}; + std::vector createdWindowIds = {}; + + for (const EditorWindowSynchronizationAction& action : plan.actions) { + switch (action.kind) { + case EditorWindowSynchronizationActionKind::UpdateWorkspaceWindow: { + EditorHostWindow* existingWindow = + m_hostRuntime.FindWindowById(action.update.windowState.windowId); + if (existingWindow == nullptr) { + m_windowManager.ReapDestroyedWindows(); + existingWindow = m_hostRuntime.FindWindowById(action.update.windowState.windowId); + } + if (existingWindow == nullptr) { + outError = + "workspace synchronization missing existing window '" + + action.update.windowState.windowId + "'"; + return false; + } + + existingWindowSnapshots.push_back(ExistingWindowSnapshot{ + std::string(existingWindow->GetWindowId()), + BuildWorkspaceProjectionForWindow(*existingWindow), + existingWindow->IsPrimary(), + }); + EditorWorkspaceWindowProjection projection = BuildWorkspaceProjectionForState( + action.update.windowState, + action.update.primary, + action.update.title); + existingWindow->SetPrimary(action.update.primary); + existingWindow->RefreshWorkspaceProjection(std::move(projection)); + existingWindow->ResetInteractionState(); + RefreshWindowTitle(*existingWindow); + break; + } + case EditorWindowSynchronizationActionKind::CreateWorkspaceWindow: { + EditorWindowCreateParams createParams = {}; + createParams.windowId = action.create.windowState.windowId; + createParams.category = EditorWindowCategory::Workspace; + createParams.primary = action.create.primary; + createParams.title = action.create.title; + if (action.create.placement.enabled) { + createParams.initialX = action.create.placement.initialX; + createParams.initialY = action.create.placement.initialY; + createParams.initialWidth = action.create.placement.initialWidth; + createParams.initialHeight = action.create.placement.initialHeight; + } + + EditorHostWindow* const createdWindow = m_windowManager.CreateWorkspaceWindow( + action.create.windowState, + createParams); + if (createdWindow == nullptr) { + for (const ExistingWindowSnapshot& snapshot : existingWindowSnapshots) { + restoreWindowSnapshot(snapshot); + } + for (auto it = createdWindowIds.rbegin(); it != createdWindowIds.rend(); ++it) { + destroyAndEraseWindowById(*it); + } + outError = + "workspace synchronization failed to create window '" + + action.create.windowState.windowId + "'"; + return false; + } + createdWindow->RefreshWorkspaceProjection(BuildWorkspaceProjectionForState( + action.create.windowState, + action.create.primary, + action.create.title)); + RefreshWindowTitle(*createdWindow); + createdWindowIds.push_back(action.create.windowState.windowId); + break; + } + case EditorWindowSynchronizationActionKind::CloseWorkspaceWindow: + if (EditorHostWindow* window = m_hostRuntime.FindWindowById(action.close.windowId); + window != nullptr) { + m_windowManager.RequestWindowClose(*window); + } + break; + } + } + + m_productRuntime.CommitWindowWorkspaceSet(plan.targetWindowSet); + outError.clear(); + return true; +} + +EditorWorkspaceWindowMutationResult +EditorWorkspaceWindowSynchronizer::CommitWorkspaceMutation( + const EditorWindowWorkspaceMutation& mutation, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth, + int preferredHeight) { + EditorWorkspaceWindowMutationResult result = {}; + result.operation = mutation.operation; + if (!mutation.HasStateChange()) { + return result; + } + + if (!SynchronizeWindowsFromWindowSet( + mutation.targetWindowSet, + mutation.preferredNewWindowId, + preferredScreenPoint, + preferredWidth, + preferredHeight, + result.errorMessage)) { + return result; + } + + result.committed = true; + return result; +} + +EditorWorkspaceWindowMutationResult +EditorWorkspaceWindowSynchronizer::DetachPanelToNewWindow( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth, + int preferredHeight) { + return CommitWorkspaceMutation( + m_windowSystem.EvaluateDetachPanelToNewWindow( + m_productRuntime.GetWindowWorkspace(), + sourceWindowId, + sourceNodeId, + panelId), + preferredScreenPoint, + preferredWidth, + preferredHeight); +} + +EditorWorkspaceWindowMutationResult +EditorWorkspaceWindowSynchronizer::MovePanelToStack( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + std::size_t targetVisibleInsertionIndex) { + return CommitWorkspaceMutation( + m_windowSystem.EvaluateMovePanelToStack( + m_productRuntime.GetWindowWorkspace(), + sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + targetVisibleInsertionIndex), + {}); +} + +EditorWorkspaceWindowMutationResult +EditorWorkspaceWindowSynchronizer::DockPanelRelative( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + UIEditorWorkspaceDockPlacement placement, + float splitRatio) { + return CommitWorkspaceMutation( + m_windowSystem.EvaluateDockPanelRelative( + m_productRuntime.GetWindowWorkspace(), + sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + placement, + splitRatio), + {}); +} + +UIEditorWindowWorkspaceState EditorWorkspaceWindowSynchronizer::BuildWindowStateForWindow( + const EditorHostWindow& window) const { + if (const UIEditorWindowWorkspaceState* windowState = + m_productRuntime.FindWindowWorkspaceState(window.GetWindowId()); + windowState != nullptr) { + return *windowState; + } + + return {}; +} + +EditorWorkspaceWindowProjection EditorWorkspaceWindowSynchronizer::BuildWorkspaceProjectionForState( + const UIEditorWindowWorkspaceState& windowState, + bool primary, + std::wstring title) const { + EditorWorkspaceWindowProjection projection = BuildEditorWorkspaceWindowProjection( + m_hostRuntime.GetPrimaryWindowTitle(), + m_windowSystem.GetPanelRegistry(), + windowState, + primary); + if (!title.empty()) { + projection.windowTitle = std::move(title); + } + return projection; +} + +EditorWorkspaceWindowProjection EditorWorkspaceWindowSynchronizer::BuildWorkspaceProjectionForWindow( + const EditorHostWindow& window) const { + if (const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection(); + projection != nullptr) { + return *projection; + } + + return BuildWorkspaceProjectionForState( + BuildWindowStateForWindow(window), + window.IsPrimary(), + window.GetTitle()); +} + +bool EditorWorkspaceWindowSynchronizer::RefreshWorkspaceProjectionFromAuthoritativeState( + EditorHostWindow& window) const { + if (!window.IsWorkspaceWindow()) { + return false; + } + + const UIEditorWindowWorkspaceState* authoritativeState = + m_productRuntime.FindWindowWorkspaceState(window.GetWindowId()); + if (authoritativeState == nullptr) { + return false; + } + + const bool primary = m_productRuntime.IsPrimaryWorkspaceWindow(window.GetWindowId()); + window.SetPrimary(primary); + window.RefreshWorkspaceProjection(BuildWorkspaceProjectionForState( + *authoritativeState, + primary, + {})); + return true; +} + +void EditorWorkspaceWindowSynchronizer::RefreshWindowTitle(EditorHostWindow& window) const { + const EditorWorkspaceWindowProjection* projection = window.TryGetWorkspaceProjection(); + if (projection == nullptr || projection->windowTitle.empty()) { + return; + } + + const std::wstring& title = projection->windowTitle; + if (title == window.GetTitle()) { + return; + } + + window.SetTitle(title); + window.ApplyHostWindowTitle(); +} + +void EditorWorkspaceWindowSynchronizer::LogRuntimeTrace( + std::string_view channel, + std::string_view message) const { + m_hostRuntime.LogRuntimeTrace(channel, message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h new file mode 100644 index 00000000..11f876ea --- /dev/null +++ b/editor/src/Product/Runtime/Windowing/Workspace/EditorWorkspaceWindowSynchronizer.h @@ -0,0 +1,106 @@ +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include "EditorWindowHostInterfaces.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor { +class EditorWindowSystem; +struct EditorWindowWorkspaceMutation; +} + +namespace XCEngine::UI::Editor::App { + +class EditorWindowManager; +class EditorProductRuntime; + +struct EditorWorkspaceWindowMutationResult { + UIEditorWindowWorkspaceOperationResult operation = {}; + bool committed = false; + std::string errorMessage = {}; +}; + +class EditorWorkspaceWindowSynchronizer final { +public: + EditorWorkspaceWindowSynchronizer( + EditorWindowHost& hostRuntime, + EditorWindowManager& windowManager, + EditorProductRuntime& productRuntime, + EditorWindowSystem& windowSystem); + ~EditorWorkspaceWindowSynchronizer(); + + void RegisterExistingWindow(EditorHostWindow& window) const; + void RefreshWindowPresentation(EditorHostWindow& window) const; + void HandleNativeWindowDestroyed(std::string_view windowId); + bool IsPrimaryWindowId(std::string_view windowId) const; + std::string DescribeWindowSet() const; + + EditorWorkspaceWindowMutationResult DetachPanelToNewWindow( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth = 0, + int preferredHeight = 0); + EditorWorkspaceWindowMutationResult MovePanelToStack( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + std::size_t targetVisibleInsertionIndex); + EditorWorkspaceWindowMutationResult DockPanelRelative( + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + UIEditorWorkspaceDockPlacement placement, + float splitRatio = 0.5f); + +private: + bool SynchronizeWindowsFromWindowSet( + const UIEditorWindowWorkspaceSet& windowSet, + std::string_view preferredNewWindowId, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth, + int preferredHeight, + std::string& outError); + bool ApplySynchronizationPlan( + const EditorWindowSynchronizationPlan& plan, + std::string& outError); + EditorWorkspaceWindowMutationResult CommitWorkspaceMutation( + const EditorWindowWorkspaceMutation& mutation, + const EditorWindowScreenPoint& preferredScreenPoint, + int preferredWidth = 0, + int preferredHeight = 0); + std::vector CaptureHostSnapshots() const; + UIEditorWindowWorkspaceState BuildWindowStateForWindow(const EditorHostWindow& window) const; + EditorWorkspaceWindowProjection BuildWorkspaceProjectionForState( + const UIEditorWindowWorkspaceState& windowState, + bool primary, + std::wstring title) const; + EditorWorkspaceWindowProjection BuildWorkspaceProjectionForWindow( + const EditorHostWindow& window) const; + bool RefreshWorkspaceProjectionFromAuthoritativeState(EditorHostWindow& window) const; + void RefreshWindowTitle(EditorHostWindow& window) const; + void LogRuntimeTrace(std::string_view channel, std::string_view message) const; + + EditorWindowHost& m_hostRuntime; + EditorWindowManager& m_windowManager; + EditorProductRuntime& m_productRuntime; + EditorWindowSystem& m_windowSystem; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Services/Engine/EngineEditorServices.cpp b/editor/src/Product/Services/Engine/EngineEditorServices.cpp similarity index 93% rename from editor/app/Services/Engine/EngineEditorServices.cpp rename to editor/src/Product/Services/Engine/EngineEditorServices.cpp index 0a3941d8..d331c66b 100644 --- a/editor/app/Services/Engine/EngineEditorServices.cpp +++ b/editor/src/Product/Services/Engine/EngineEditorServices.cpp @@ -1,13 +1,13 @@ -#include "Engine/EngineEditorServices.h" +#include "Product/Services/Engine/EngineEditorServices.h" -#include "Engine/EditorEngineLifecycle.h" -#include "Engine/EditorSceneBackendFactory.h" -#include "Engine/EditorShaderProvider.h" -#include "Engine/EngineGameViewportBridge.h" -#include "Engine/EngineSceneViewportBridge.h" -#include "Engine/GameViewportEngineBridge.h" -#include "Engine/SceneViewportEngineBridge.h" -#include "Scene/EngineEditorSceneBackend.h" +#include "Product/Core/Engine/EditorEngineLifecycle.h" +#include "Product/Core/Engine/EditorSceneBackendFactory.h" +#include "Product/Core/Engine/EditorShaderProvider.h" +#include "Product/Services/Engine/EngineGameViewportBridge.h" +#include "Product/Services/Engine/EngineSceneViewportBridge.h" +#include "Product/Core/Engine/GameViewportEngineBridge.h" +#include "Product/Core/Engine/SceneViewportEngineBridge.h" +#include "Product/Services/Scene/EngineEditorSceneBackend.h" #include #include diff --git a/editor/app/Services/Engine/EngineEditorServices.h b/editor/src/Product/Services/Engine/EngineEditorServices.h similarity index 76% rename from editor/app/Services/Engine/EngineEditorServices.h rename to editor/src/Product/Services/Engine/EngineEditorServices.h index 77112dff..6ec409df 100644 --- a/editor/app/Services/Engine/EngineEditorServices.h +++ b/editor/src/Product/Services/Engine/EngineEditorServices.h @@ -1,10 +1,10 @@ #pragma once -#include "Engine/EditorEngineLifecycle.h" -#include "Engine/EditorSceneBackendFactory.h" -#include "Engine/EditorShaderProvider.h" -#include "Engine/GameViewportEngineBridge.h" -#include "Engine/SceneViewportEngineBridge.h" +#include "Product/Core/Engine/EditorEngineLifecycle.h" +#include "Product/Core/Engine/EditorSceneBackendFactory.h" +#include "Product/Core/Engine/EditorShaderProvider.h" +#include "Product/Core/Engine/GameViewportEngineBridge.h" +#include "Product/Core/Engine/SceneViewportEngineBridge.h" #include diff --git a/editor/app/Services/Engine/EngineGameViewportBridge.cpp b/editor/src/Product/Services/Engine/EngineGameViewportBridge.cpp similarity index 96% rename from editor/app/Services/Engine/EngineGameViewportBridge.cpp rename to editor/src/Product/Services/Engine/EngineGameViewportBridge.cpp index 4aa8dcc7..7a1a48cb 100644 --- a/editor/app/Services/Engine/EngineGameViewportBridge.cpp +++ b/editor/src/Product/Services/Engine/EngineGameViewportBridge.cpp @@ -1,4 +1,4 @@ -#include "Engine/EngineGameViewportBridge.h" +#include "Product/Services/Engine/EngineGameViewportBridge.h" #include #include diff --git a/editor/app/Services/Engine/EngineGameViewportBridge.h b/editor/src/Product/Services/Engine/EngineGameViewportBridge.h similarity index 91% rename from editor/app/Services/Engine/EngineGameViewportBridge.h rename to editor/src/Product/Services/Engine/EngineGameViewportBridge.h index 442154b3..2b10d384 100644 --- a/editor/app/Services/Engine/EngineGameViewportBridge.h +++ b/editor/src/Product/Services/Engine/EngineGameViewportBridge.h @@ -1,6 +1,6 @@ #pragma once -#include "Engine/GameViewportEngineBridge.h" +#include "Product/Core/Engine/GameViewportEngineBridge.h" #include diff --git a/editor/app/Services/Engine/EngineSceneViewportBridge.cpp b/editor/src/Product/Services/Engine/EngineSceneViewportBridge.cpp similarity index 98% rename from editor/app/Services/Engine/EngineSceneViewportBridge.cpp rename to editor/src/Product/Services/Engine/EngineSceneViewportBridge.cpp index 957cce58..adf88522 100644 --- a/editor/app/Services/Engine/EngineSceneViewportBridge.cpp +++ b/editor/src/Product/Services/Engine/EngineSceneViewportBridge.cpp @@ -1,4 +1,4 @@ -#include "Engine/EngineSceneViewportBridge.h" +#include "Product/Services/Engine/EngineSceneViewportBridge.h" #include #include diff --git a/editor/app/Services/Engine/EngineSceneViewportBridge.h b/editor/src/Product/Services/Engine/EngineSceneViewportBridge.h similarity index 95% rename from editor/app/Services/Engine/EngineSceneViewportBridge.h rename to editor/src/Product/Services/Engine/EngineSceneViewportBridge.h index 76f981dc..3014efc9 100644 --- a/editor/app/Services/Engine/EngineSceneViewportBridge.h +++ b/editor/src/Product/Services/Engine/EngineSceneViewportBridge.h @@ -1,6 +1,6 @@ #pragma once -#include "Engine/SceneViewportEngineBridge.h" +#include "Product/Core/Engine/SceneViewportEngineBridge.h" #include diff --git a/editor/app/Services/Project/EditorProjectRuntime.cpp b/editor/src/Product/Services/Project/EditorProjectRuntime.cpp similarity index 99% rename from editor/app/Services/Project/EditorProjectRuntime.cpp rename to editor/src/Product/Services/Project/EditorProjectRuntime.cpp index c1070064..4e9053c1 100644 --- a/editor/app/Services/Project/EditorProjectRuntime.cpp +++ b/editor/src/Product/Services/Project/EditorProjectRuntime.cpp @@ -1,4 +1,4 @@ -#include "Project/EditorProjectRuntime.h" +#include "Product/Services/Project/EditorProjectRuntime.h" namespace XCEngine::UI::Editor::App { diff --git a/editor/app/Services/Project/EditorProjectRuntime.h b/editor/src/Product/Services/Project/EditorProjectRuntime.h similarity index 96% rename from editor/app/Services/Project/EditorProjectRuntime.h rename to editor/src/Product/Services/Project/EditorProjectRuntime.h index be3e2a6e..32bc9594 100644 --- a/editor/app/Services/Project/EditorProjectRuntime.h +++ b/editor/src/Product/Services/Project/EditorProjectRuntime.h @@ -1,9 +1,9 @@ #pragma once -#include "Project/ProjectBrowserModel.h" +#include "Product/Services/Project/ProjectBrowserModel.h" -#include "State/EditorSelectionService.h" -#include "State/EditorSession.h" +#include "Product/State/EditorSelectionService.h" +#include "Product/State/EditorSession.h" #include diff --git a/editor/app/Services/Project/ProjectBrowserModel.cpp b/editor/src/Product/Services/Project/ProjectBrowserModel.cpp similarity index 99% rename from editor/app/Services/Project/ProjectBrowserModel.cpp rename to editor/src/Product/Services/Project/ProjectBrowserModel.cpp index 4eed9bf3..9ec49035 100644 --- a/editor/app/Services/Project/ProjectBrowserModel.cpp +++ b/editor/src/Product/Services/Project/ProjectBrowserModel.cpp @@ -1,4 +1,4 @@ -#include "ProjectBrowserModel.h" +#include "Product/Services/Project/ProjectBrowserModel.h" #include #include #include diff --git a/editor/app/Services/Project/ProjectBrowserModel.h b/editor/src/Product/Services/Project/ProjectBrowserModel.h similarity index 100% rename from editor/app/Services/Project/ProjectBrowserModel.h rename to editor/src/Product/Services/Project/ProjectBrowserModel.h diff --git a/editor/app/Services/Project/ProjectGraphicsSettings.h b/editor/src/Product/Services/Project/ProjectGraphicsSettings.h similarity index 100% rename from editor/app/Services/Project/ProjectGraphicsSettings.h rename to editor/src/Product/Services/Project/ProjectGraphicsSettings.h diff --git a/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.cpp b/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.cpp new file mode 100644 index 00000000..10e24233 --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.cpp @@ -0,0 +1,316 @@ +#include "Product/Services/Runtime/EditorPlayModeRuntime.h" + +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/State/EditorSession.h" + +#include + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +UIEditorHostCommandEvaluationResult BuildDisabledResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandEvaluationResult BuildExecutableResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = true; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildRejectedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildExecutedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = true; + result.message = std::string(message); + return result; +} + +} // namespace + +void EditorPlayModeRuntime::Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime) { + Shutdown(); + m_session = &session; + m_sceneRuntime = &sceneRuntime; + SetRuntimeMode(EditorRuntimeMode::Edit); +} + +void EditorPlayModeRuntime::Shutdown() { + m_runtimeLoop.Stop(); + m_playSession.reset(); + if (m_session != nullptr) { + SetRuntimeMode(EditorRuntimeMode::Edit); + } + m_session = nullptr; + m_sceneRuntime = nullptr; + m_lastFrameTickTime = {}; + m_lastMessage.clear(); +} + +void EditorPlayModeRuntime::TickFrame() { + const auto now = std::chrono::steady_clock::now(); + if (m_lastFrameTickTime == std::chrono::steady_clock::time_point{}) { + m_lastFrameTickTime = now; + return; + } + + const float deltaSeconds = std::chrono::duration( + now - m_lastFrameTickTime).count(); + m_lastFrameTickTime = now; + Tick(deltaSeconds); +} + +bool EditorPlayModeRuntime::IsReady() const { + return m_session != nullptr && m_sceneRuntime != nullptr; +} + +bool EditorPlayModeRuntime::IsPlayModeActive() const { + return IsReady() && m_session->runtimeMode != EditorRuntimeMode::Edit; +} + +bool EditorPlayModeRuntime::StopPlayMode() { + if (!IsReady()) { + return false; + } + + m_runtimeLoop.Stop(); + m_playSession.reset(); + m_sceneRuntime->RefreshScene(); + SetRuntimeMode(EditorRuntimeMode::Edit); + return true; +} + +const std::string& EditorPlayModeRuntime::GetLastMessage() const { + return m_lastMessage; +} + +UIEditorHostCommandEvaluationResult EditorPlayModeRuntime::EvaluateRunCommand( + std::string_view commandId) const { + if (!IsReady()) { + return BuildDisabledResult("Play mode runtime service is unavailable."); + } + + if (commandId == "run.play") { + if (!HasActiveScene()) { + return BuildDisabledResult("No active scene is available for play mode."); + } + return IsPlayModeActive() + ? BuildExecutableResult("Stop play mode.") + : BuildExecutableResult("Start play mode."); + } + + if (commandId == "run.pause") { + if (!IsPlayModeActive()) { + return BuildDisabledResult("Play mode is not running."); + } + return m_session->runtimeMode == EditorRuntimeMode::Paused + ? BuildExecutableResult("Resume play mode.") + : BuildExecutableResult("Pause play mode."); + } + + if (commandId == "run.step") { + if (!HasActiveScene()) { + return BuildDisabledResult("No active scene is available for play mode."); + } + return BuildExecutableResult("Step play mode by one frame."); + } + + if (commandId == "run.stop") { + return IsPlayModeActive() + ? BuildExecutableResult("Stop play mode.") + : BuildDisabledResult("Play mode is not running."); + } + + return BuildDisabledResult( + "Run command is not owned by the play mode runtime service."); +} + +UIEditorHostCommandDispatchResult EditorPlayModeRuntime::DispatchRunCommand( + std::string_view commandId) { + const UIEditorHostCommandEvaluationResult evaluation = + EvaluateRunCommand(commandId); + if (!evaluation.executable) { + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); + } + + if (commandId == "run.play") { + const bool executed = IsPlayModeActive() ? StopPlayMode() : StartPlayMode(); + SetLastMessage(executed + ? (m_session->runtimeMode == EditorRuntimeMode::Edit + ? std::string("Play mode stopped.") + : std::string("Play mode started.")) + : std::string("Failed to toggle play mode.")); + return executed + ? BuildExecutedDispatch(m_lastMessage) + : BuildRejectedDispatch(m_lastMessage); + } + + if (commandId == "run.pause") { + const bool executed = m_session->runtimeMode == EditorRuntimeMode::Paused + ? ResumePlayMode() + : PausePlayMode(); + SetLastMessage(executed + ? (m_session->runtimeMode == EditorRuntimeMode::Paused + ? std::string("Play mode paused.") + : std::string("Play mode resumed.")) + : std::string("Failed to toggle play mode pause.")); + return executed + ? BuildExecutedDispatch(m_lastMessage) + : BuildRejectedDispatch(m_lastMessage); + } + + if (commandId == "run.step") { + if (!StepPlayMode()) { + SetLastMessage("Failed to step play mode."); + return BuildRejectedDispatch(m_lastMessage); + } + + SetLastMessage("Play mode stepped one frame."); + return BuildExecutedDispatch(m_lastMessage); + } + + if (commandId == "run.stop") { + if (!StopPlayMode()) { + SetLastMessage("Failed to stop play mode."); + return BuildRejectedDispatch(m_lastMessage); + } + + SetLastMessage("Play mode stopped."); + return BuildExecutedDispatch(m_lastMessage); + } + + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); +} + +bool EditorPlayModeRuntime::HasActiveScene() const { + return IsReady() && m_sceneRuntime->GetActiveScene() != nullptr; +} + +void EditorPlayModeRuntime::Tick(float deltaSeconds) { + if (!IsReady()) { + return; + } + + if (!m_runtimeLoop.IsRunning()) { + if (m_session->runtimeMode != EditorRuntimeMode::Edit) { + SetRuntimeMode(EditorRuntimeMode::Edit); + } + return; + } + + m_runtimeLoop.Tick(deltaSeconds); + if (m_runtimeLoop.IsPaused()) { + SetRuntimeMode(EditorRuntimeMode::Paused); + } else { + SetRuntimeMode(EditorRuntimeMode::Play); + } +} + +bool EditorPlayModeRuntime::EnsurePlaySession() { + if (!HasActiveScene()) { + return false; + } + + if (m_playSession != nullptr && + m_playSession->GetRuntimeScene() != nullptr) { + return true; + } + + m_playSession = m_sceneRuntime->BeginPlaySession(); + if (m_playSession == nullptr || + m_playSession->GetRuntimeScene() == nullptr) { + m_playSession.reset(); + return false; + } + + return true; +} + +bool EditorPlayModeRuntime::StartPlayMode() { + if (!EnsurePlaySession()) { + return false; + } + + ::XCEngine::Scripting::ScriptEngine::Get().SetRuntimeFixedDeltaTime( + m_runtimeLoop.GetSettings().fixedDeltaTime); + m_runtimeLoop.Start(m_playSession->GetRuntimeScene()); + SetRuntimeMode(EditorRuntimeMode::Play); + m_lastFrameTickTime = std::chrono::steady_clock::now(); + return m_runtimeLoop.IsRunning(); +} + +bool EditorPlayModeRuntime::PausePlayMode() { + if (!m_runtimeLoop.IsRunning()) { + return false; + } + + m_runtimeLoop.Pause(); + SetRuntimeMode(EditorRuntimeMode::Paused); + return true; +} + +bool EditorPlayModeRuntime::ResumePlayMode() { + if (!m_runtimeLoop.IsRunning()) { + return false; + } + + m_runtimeLoop.Resume(); + SetRuntimeMode(EditorRuntimeMode::Play); + m_lastFrameTickTime = std::chrono::steady_clock::now(); + return true; +} + +bool EditorPlayModeRuntime::StepPlayMode() { + if (!HasActiveScene()) { + return false; + } + + if (!m_runtimeLoop.IsRunning()) { + if (!EnsurePlaySession()) { + return false; + } + + ::XCEngine::Scripting::ScriptEngine::Get().SetRuntimeFixedDeltaTime( + m_runtimeLoop.GetSettings().fixedDeltaTime); + m_runtimeLoop.Start(m_playSession->GetRuntimeScene()); + } + + m_runtimeLoop.StepFrame(); + m_runtimeLoop.Tick(m_runtimeLoop.GetSettings().fixedDeltaTime); + SetRuntimeMode(EditorRuntimeMode::Paused); + m_lastFrameTickTime = std::chrono::steady_clock::now(); + return true; +} + +void EditorPlayModeRuntime::SetRuntimeMode(EditorRuntimeMode mode) { + if (m_session != nullptr) { + m_session->runtimeMode = mode; + } +} + +void EditorPlayModeRuntime::SetLastMessage(std::string message) { + m_lastMessage = std::move(message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.h b/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.h new file mode 100644 index 00000000..e8eeaeab --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorPlayModeRuntime.h @@ -0,0 +1,58 @@ +#pragma once + +#include "Product/Commands/EditorHostCommandBridge.h" +#include "Product/State/EditorSession.h" + +#include + +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorSceneRuntime; +class EditorScenePlaySession; +struct EditorSession; + +class EditorPlayModeRuntime final { +public: + void Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime); + void Shutdown(); + + void TickFrame(); + + bool IsReady() const; + bool IsPlayModeActive() const; + bool StopPlayMode(); + const std::string& GetLastMessage() const; + + UIEditorHostCommandEvaluationResult EvaluateRunCommand( + std::string_view commandId) const; + UIEditorHostCommandDispatchResult DispatchRunCommand( + std::string_view commandId); + +private: + bool HasActiveScene() const; + void Tick(float deltaSeconds); + bool EnsurePlaySession(); + bool StartPlayMode(); + bool PausePlayMode(); + bool ResumePlayMode(); + bool StepPlayMode(); + void SetRuntimeMode(EditorRuntimeMode mode); + void SetLastMessage(std::string message); + + EditorSession* m_session = nullptr; + EditorSceneRuntime* m_sceneRuntime = nullptr; + ::XCEngine::Components::RuntimeLoop m_runtimeLoop{ + ::XCEngine::Components::RuntimeLoop::Settings{} }; + std::unique_ptr m_playSession = {}; + std::chrono::steady_clock::time_point m_lastFrameTickTime = {}; + std::string m_lastMessage = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.cpp b/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.cpp new file mode 100644 index 00000000..cb9d6582 --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.cpp @@ -0,0 +1,196 @@ +#include "Product/Services/Runtime/EditorRuntimeCommandService.h" + +#include "Product/Services/Scene/EditorSceneRuntime.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +UIEditorHostCommandEvaluationResult BuildDisabledResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandEvaluationResult BuildExecutableResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = true; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildRejectedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildExecutedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = true; + result.message = std::string(message); + return result; +} + +} // namespace + +EditorRuntimeCommandService::~EditorRuntimeCommandService() { + Shutdown(); +} + +void EditorRuntimeCommandService::Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime, + const EditorRuntimePaths& runtimePaths, + const EditorStartupSceneResult& startupScene) { + Shutdown(); + m_sceneRuntime = &sceneRuntime; + m_sceneDocumentRuntime.Initialize(session, sceneRuntime, startupScene); + m_playModeRuntime.Initialize(session, sceneRuntime); + m_scriptingRuntimeService.Initialize(runtimePaths.projectRoot); +} + +void EditorRuntimeCommandService::Shutdown() { + m_playModeRuntime.Shutdown(); + m_sceneDocumentRuntime.Shutdown(); + m_sceneRuntime = nullptr; + m_scriptingRuntimeService.Shutdown(); + m_lastMessage.clear(); +} + +void EditorRuntimeCommandService::TickFrame() { + m_sceneDocumentRuntime.Tick(); + m_playModeRuntime.TickFrame(); +} + +bool EditorRuntimeCommandService::RequestOpenSceneAsset( + const std::filesystem::path& scenePath) { + const bool opened = m_sceneDocumentRuntime.RequestOpenSceneAsset( + scenePath, + [this]() { + m_playModeRuntime.StopPlayMode(); + }); + SetLastMessage(m_sceneDocumentRuntime.GetLastMessage()); + return opened; +} + +const std::string& EditorRuntimeCommandService::GetLastMessage() const { + return m_lastMessage; +} + +UIEditorHostCommandEvaluationResult +EditorRuntimeCommandService::EvaluateFileCommand( + std::string_view commandId) const { + return m_sceneDocumentRuntime.EvaluateFileCommand( + commandId, + m_playModeRuntime.IsPlayModeActive()); +} + +UIEditorHostCommandDispatchResult +EditorRuntimeCommandService::DispatchFileCommand( + std::string_view commandId) { + UIEditorHostCommandDispatchResult result = + m_sceneDocumentRuntime.DispatchFileCommand( + commandId, + m_playModeRuntime.IsPlayModeActive(), + [this]() { + m_playModeRuntime.StopPlayMode(); + }); + SetLastMessage(result.message); + return result; +} + +UIEditorHostCommandEvaluationResult +EditorRuntimeCommandService::EvaluateRunCommand( + std::string_view commandId) const { + return m_playModeRuntime.EvaluateRunCommand(commandId); +} + +UIEditorHostCommandDispatchResult +EditorRuntimeCommandService::DispatchRunCommand( + std::string_view commandId) { + UIEditorHostCommandDispatchResult result = + m_playModeRuntime.DispatchRunCommand(commandId); + SetLastMessage(result.message); + return result; +} + +UIEditorHostCommandEvaluationResult +EditorRuntimeCommandService::EvaluateScriptCommand( + std::string_view commandId) const { + if (!IsReady()) { + return BuildDisabledResult("Runtime services are unavailable."); + } + + if (commandId == "scripts.rebuild") { + if (m_playModeRuntime.IsPlayModeActive()) { + return BuildDisabledResult( + "Stop play mode before rebuilding script assemblies."); + } + + if (!m_scriptingRuntimeService.CanRebuildProjectAssemblies()) { + const std::string& message = m_scriptingRuntimeService.GetLastMessage(); + return BuildDisabledResult( + message.empty() + ? std::string_view( + "Script rebuild is unavailable because the scripting runtime is not initialized.") + : std::string_view(message)); + } + + return BuildExecutableResult( + "Rebuild project script assemblies and reload the editor scripting runtime."); + } + + return BuildDisabledResult( + "Script command is not owned by the runtime command service."); +} + +UIEditorHostCommandDispatchResult +EditorRuntimeCommandService::DispatchScriptCommand( + std::string_view commandId) { + const UIEditorHostCommandEvaluationResult evaluation = + EvaluateScriptCommand(commandId); + if (!evaluation.executable) { + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); + } + + if (commandId == "scripts.rebuild") { + const bool rebuilt = m_scriptingRuntimeService.RebuildProjectAssemblies(); + if (m_sceneRuntime != nullptr) { + m_sceneRuntime->NotifyExternalInspectorStateChanged(); + } + + const std::string& message = m_scriptingRuntimeService.GetLastMessage(); + SetLastMessage( + !message.empty() + ? message + : rebuilt + ? std::string("Script assemblies rebuilt.") + : std::string("Failed to rebuild script assemblies.")); + return rebuilt + ? BuildExecutedDispatch(m_lastMessage) + : BuildRejectedDispatch(m_lastMessage); + } + + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); +} + +bool EditorRuntimeCommandService::IsReady() const { + return m_sceneDocumentRuntime.IsReady() && m_playModeRuntime.IsReady(); +} + +void EditorRuntimeCommandService::SetLastMessage(std::string message) { + m_lastMessage = std::move(message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.h b/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.h new file mode 100644 index 00000000..c7d7b202 --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorRuntimeCommandService.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Product/Commands/EditorHostCommandBridge.h" +#include "Product/Core/Environment/EditorRuntimePaths.h" +#include "Product/Services/Runtime/EditorPlayModeRuntime.h" +#include "Product/Services/Runtime/EditorSceneDocumentRuntime.h" +#include "Product/Services/Runtime/EditorScriptingRuntimeService.h" + +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorSceneRuntime; +struct EditorStartupSceneResult; +struct EditorSession; + +class EditorRuntimeCommandService final + : public EditorHostCommandBridge::RuntimeCommandOwner { +public: + EditorRuntimeCommandService() = default; + ~EditorRuntimeCommandService(); + EditorRuntimeCommandService(const EditorRuntimeCommandService&) = delete; + EditorRuntimeCommandService& operator=(const EditorRuntimeCommandService&) = delete; + + void Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime, + const EditorRuntimePaths& runtimePaths, + const EditorStartupSceneResult& startupScene); + void Shutdown(); + + void TickFrame(); + + bool RequestOpenSceneAsset(const std::filesystem::path& scenePath); + const std::string& GetLastMessage() const; + + UIEditorHostCommandEvaluationResult EvaluateFileCommand( + std::string_view commandId) const override; + UIEditorHostCommandDispatchResult DispatchFileCommand( + std::string_view commandId) override; + UIEditorHostCommandEvaluationResult EvaluateRunCommand( + std::string_view commandId) const override; + UIEditorHostCommandDispatchResult DispatchRunCommand( + std::string_view commandId) override; + UIEditorHostCommandEvaluationResult EvaluateScriptCommand( + std::string_view commandId) const override; + UIEditorHostCommandDispatchResult DispatchScriptCommand( + std::string_view commandId) override; + +private: + bool IsReady() const; + void SetLastMessage(std::string message); + + EditorSceneDocumentRuntime m_sceneDocumentRuntime = {}; + EditorPlayModeRuntime m_playModeRuntime = {}; + EditorScriptingRuntimeService m_scriptingRuntimeService = {}; + EditorSceneRuntime* m_sceneRuntime = nullptr; + std::string m_lastMessage = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.cpp b/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.cpp new file mode 100644 index 00000000..e5a6d8d7 --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.cpp @@ -0,0 +1,284 @@ +#include "Product/Services/Runtime/EditorSceneDocumentRuntime.h" + +#include "Product/Services/Scene/EditorSceneRuntime.h" +#include "Product/State/EditorSession.h" + +#include + +namespace XCEngine::UI::Editor::App { + +namespace { + +UIEditorHostCommandEvaluationResult BuildDisabledResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandEvaluationResult BuildExecutableResult( + std::string_view message) { + UIEditorHostCommandEvaluationResult result = {}; + result.executable = true; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildRejectedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = false; + result.message = std::string(message); + return result; +} + +UIEditorHostCommandDispatchResult BuildExecutedDispatch( + std::string_view message) { + UIEditorHostCommandDispatchResult result = {}; + result.commandExecuted = true; + result.message = std::string(message); + return result; +} + +std::string ResolveSceneDisplayName(const std::filesystem::path& scenePath) { + return scenePath.empty() + ? std::string("Untitled") + : scenePath.stem().string(); +} + +std::string ResolveDocumentSceneName( + std::string_view sceneName, + const std::filesystem::path& scenePath) { + return !sceneName.empty() + ? std::string(sceneName) + : ResolveSceneDisplayName(scenePath); +} + +} // namespace + +void EditorSceneDocumentRuntime::Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime, + const EditorStartupSceneResult& startupScene) { + Shutdown(); + m_session = &session; + m_sceneRuntime = &sceneRuntime; + ApplyStartupSceneDocument(startupScene); + CaptureCleanSceneRevision(); +} + +void EditorSceneDocumentRuntime::Shutdown() { + if (m_session != nullptr) { + m_sceneDocument = {}; + SyncSessionDocumentProjection(); + } + m_session = nullptr; + m_sceneRuntime = nullptr; + m_sceneDocument = {}; + m_lastCleanSceneContentRevision = 0u; + m_lastObservedSceneContentRevision = 0u; + m_lastMessage.clear(); +} + +void EditorSceneDocumentRuntime::Tick() { + SyncSceneDocumentDirtyFromRevision(); +} + +bool EditorSceneDocumentRuntime::IsReady() const { + return m_session != nullptr && m_sceneRuntime != nullptr; +} + +bool EditorSceneDocumentRuntime::HasActiveScene() const { + return IsReady() && m_sceneRuntime->GetActiveScene() != nullptr; +} + +bool EditorSceneDocumentRuntime::RequestOpenSceneAsset( + const std::filesystem::path& scenePath, + const std::function& stopPlayMode) { + if (!IsReady()) { + SetLastMessage("Runtime scene document service is unavailable."); + return false; + } + + if (scenePath.empty()) { + SetLastMessage("Scene asset path is empty."); + return false; + } + + if (stopPlayMode) { + stopPlayMode(); + } + if (!m_sceneRuntime->OpenSceneAsset(scenePath)) { + SetLastMessage("Failed to open scene asset: " + scenePath.string()); + return false; + } + + m_sceneDocument.ready = true; + m_sceneDocument.loadedFromDisk = true; + m_sceneDocument.currentPath = scenePath; + m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath); + m_sceneDocument.dirty = false; + CaptureCleanSceneRevision(); + SyncSessionDocumentProjection(); + SetLastMessage("Opened scene: " + scenePath.string()); + return true; +} + +const std::string& EditorSceneDocumentRuntime::GetLastMessage() const { + return m_lastMessage; +} + +UIEditorHostCommandEvaluationResult EditorSceneDocumentRuntime::EvaluateFileCommand( + std::string_view commandId, + bool playModeActive) const { + if (!IsReady()) { + return BuildDisabledResult("Runtime scene document service is unavailable."); + } + + if (commandId == "file.new_scene") { + return BuildExecutableResult("Create a new unsaved scene document."); + } + + if (commandId == "file.open_scene") { + return BuildDisabledResult( + "Open Scene requires a project scene asset selection in the current shell."); + } + + if (commandId == "file.save_scene") { + if (playModeActive) { + return BuildDisabledResult("Stop play mode before saving the scene."); + } + if (!HasActiveScene()) { + return BuildDisabledResult("No active scene to save."); + } + if (m_sceneDocument.currentPath.empty()) { + return BuildDisabledResult( + "Save Scene As is required before saving this scene."); + } + return BuildExecutableResult("Save the active scene document."); + } + + if (commandId == "file.save_scene_as") { + return BuildDisabledResult( + "Save Scene As requires a file dialog host; the current shell does not provide one."); + } + + return BuildDisabledResult( + "File command is not owned by the runtime scene document service."); +} + +UIEditorHostCommandDispatchResult EditorSceneDocumentRuntime::DispatchFileCommand( + std::string_view commandId, + bool playModeActive, + const std::function& stopPlayMode) { + const UIEditorHostCommandEvaluationResult evaluation = + EvaluateFileCommand(commandId, playModeActive); + if (!evaluation.executable) { + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); + } + + if (commandId == "file.new_scene") { + if (stopPlayMode) { + stopPlayMode(); + } + if (!m_sceneRuntime->NewScene("Untitled")) { + SetLastMessage("Failed to create a new scene."); + return BuildRejectedDispatch(m_lastMessage); + } + + m_sceneDocument.ready = true; + m_sceneDocument.loadedFromDisk = false; + m_sceneDocument.currentPath.clear(); + m_sceneDocument.currentName = ResolveActiveSceneDisplayName(); + m_sceneDocument.dirty = true; + CaptureCleanSceneRevision(); + SyncSessionDocumentProjection(); + SetLastMessage("New scene created."); + return BuildExecutedDispatch(m_lastMessage); + } + + if (commandId == "file.save_scene") { + const std::filesystem::path scenePath = m_sceneDocument.currentPath; + if (!m_sceneRuntime->SaveScene(scenePath)) { + SetLastMessage("Failed to save scene: " + scenePath.string()); + return BuildRejectedDispatch(m_lastMessage); + } + + m_sceneDocument.ready = true; + m_sceneDocument.loadedFromDisk = !scenePath.empty(); + m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath); + m_sceneDocument.dirty = false; + CaptureCleanSceneRevision(); + SyncSessionDocumentProjection(); + SetLastMessage("Scene saved: " + scenePath.string()); + return BuildExecutedDispatch(m_lastMessage); + } + + SetLastMessage(evaluation.message); + return BuildRejectedDispatch(evaluation.message); +} + +void EditorSceneDocumentRuntime::ApplyStartupSceneDocument( + const EditorStartupSceneResult& startupScene) { + m_sceneDocument.ready = startupScene.ready; + m_sceneDocument.loadedFromDisk = startupScene.loadedFromDisk; + m_sceneDocument.currentPath = startupScene.scenePath; + m_sceneDocument.currentName = + ResolveDocumentSceneName(startupScene.sceneName, startupScene.scenePath); + m_sceneDocument.dirty = false; + SyncSessionDocumentProjection(); +} + +std::string EditorSceneDocumentRuntime::ResolveActiveSceneDisplayName( + const std::filesystem::path& fallbackPath) const { + const std::filesystem::path& scenePath = + fallbackPath.empty() ? m_sceneDocument.currentPath : fallbackPath; + const std::string sceneName = + IsReady() ? m_sceneRuntime->GetActiveSceneName() : std::string(); + return ResolveDocumentSceneName(sceneName, scenePath); +} + +void EditorSceneDocumentRuntime::SyncSessionDocumentProjection() { + if (m_session == nullptr) { + return; + } + + m_session->currentScenePath = m_sceneDocument.currentPath; + m_session->currentSceneName = m_sceneDocument.currentName; + m_session->sceneDocumentDirty = m_sceneDocument.dirty; +} + +void EditorSceneDocumentRuntime::SyncSceneDocumentDirtyFromRevision() { + if (!IsReady()) { + return; + } + + const std::uint64_t revision = m_sceneRuntime->GetSceneContentRevision(); + if (revision != m_lastObservedSceneContentRevision) { + m_lastObservedSceneContentRevision = revision; + } + if (revision != m_lastCleanSceneContentRevision) { + m_sceneDocument.dirty = true; + SyncSessionDocumentProjection(); + } +} + +void EditorSceneDocumentRuntime::CaptureCleanSceneRevision() { + if (!IsReady()) { + m_lastCleanSceneContentRevision = 0u; + m_lastObservedSceneContentRevision = 0u; + return; + } + + const std::uint64_t revision = m_sceneRuntime->GetSceneContentRevision(); + m_lastCleanSceneContentRevision = revision; + m_lastObservedSceneContentRevision = revision; +} + +void EditorSceneDocumentRuntime::SetLastMessage(std::string message) { + m_lastMessage = std::move(message); +} + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.h b/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.h new file mode 100644 index 00000000..990d8386 --- /dev/null +++ b/editor/src/Product/Services/Runtime/EditorSceneDocumentRuntime.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Product/Commands/EditorHostCommandBridge.h" + +#include +#include +#include +#include +#include + +namespace XCEngine::UI::Editor::App { + +class EditorSceneRuntime; +struct EditorSession; +struct EditorStartupSceneResult; + +class EditorSceneDocumentRuntime final { +public: + void Initialize( + EditorSession& session, + EditorSceneRuntime& sceneRuntime, + const EditorStartupSceneResult& startupScene); + void Shutdown(); + + void Tick(); + + bool IsReady() const; + bool HasActiveScene() const; + bool RequestOpenSceneAsset( + const std::filesystem::path& scenePath, + const std::function& stopPlayMode); + const std::string& GetLastMessage() const; + + UIEditorHostCommandEvaluationResult EvaluateFileCommand( + std::string_view commandId, + bool playModeActive) const; + UIEditorHostCommandDispatchResult DispatchFileCommand( + std::string_view commandId, + bool playModeActive, + const std::function& stopPlayMode); + +private: + struct SceneDocumentState { + bool ready = false; + bool loadedFromDisk = false; + std::filesystem::path currentPath = {}; + std::string currentName = {}; + bool dirty = false; + }; + + void ApplyStartupSceneDocument(const EditorStartupSceneResult& startupScene); + std::string ResolveActiveSceneDisplayName( + const std::filesystem::path& fallbackPath = {}) const; + void SyncSessionDocumentProjection(); + void SyncSceneDocumentDirtyFromRevision(); + void CaptureCleanSceneRevision(); + void SetLastMessage(std::string message); + + EditorSession* m_session = nullptr; + EditorSceneRuntime* m_sceneRuntime = nullptr; + SceneDocumentState m_sceneDocument = {}; + std::uint64_t m_lastCleanSceneContentRevision = 0u; + std::uint64_t m_lastObservedSceneContentRevision = 0u; + std::string m_lastMessage = {}; +}; + +} // namespace XCEngine::UI::Editor::App diff --git a/editor/app/Services/Runtime/EditorScriptAssemblyBuilder.cpp b/editor/src/Product/Services/Runtime/EditorScriptAssemblyBuilder.cpp similarity index 99% rename from editor/app/Services/Runtime/EditorScriptAssemblyBuilder.cpp rename to editor/src/Product/Services/Runtime/EditorScriptAssemblyBuilder.cpp index e31a5c7c..60df9a3f 100644 --- a/editor/app/Services/Runtime/EditorScriptAssemblyBuilder.cpp +++ b/editor/src/Product/Services/Runtime/EditorScriptAssemblyBuilder.cpp @@ -1,6 +1,6 @@ -#include "Runtime/EditorScriptAssemblyBuilder.h" +#include "Product/Services/Runtime/EditorScriptAssemblyBuilder.h" -#include "StringEncoding.h" +#include "Product/Support/StringEncoding.h" #include diff --git a/editor/app/Services/Runtime/EditorScriptAssemblyBuilder.h b/editor/src/Product/Services/Runtime/EditorScriptAssemblyBuilder.h similarity index 100% rename from editor/app/Services/Runtime/EditorScriptAssemblyBuilder.h rename to editor/src/Product/Services/Runtime/EditorScriptAssemblyBuilder.h diff --git a/editor/app/Services/Runtime/EditorScriptingRuntimeService.cpp b/editor/src/Product/Services/Runtime/EditorScriptingRuntimeService.cpp similarity index 96% rename from editor/app/Services/Runtime/EditorScriptingRuntimeService.cpp rename to editor/src/Product/Services/Runtime/EditorScriptingRuntimeService.cpp index 53566358..540184ff 100644 --- a/editor/app/Services/Runtime/EditorScriptingRuntimeService.cpp +++ b/editor/src/Product/Services/Runtime/EditorScriptingRuntimeService.cpp @@ -1,8 +1,8 @@ -#include "Runtime/EditorScriptingRuntimeService.h" +#include "Product/Services/Runtime/EditorScriptingRuntimeService.h" -#include "Project/ProjectGraphicsSettings.h" -#include "Runtime/EditorScriptAssemblyBuilder.h" -#include "StringEncoding.h" +#include "Product/Services/Project/ProjectGraphicsSettings.h" +#include "Product/Services/Runtime/EditorScriptAssemblyBuilder.h" +#include "Product/Support/StringEncoding.h" #include #include diff --git a/editor/app/Services/Runtime/EditorScriptingRuntimeService.h b/editor/src/Product/Services/Runtime/EditorScriptingRuntimeService.h similarity index 100% rename from editor/app/Services/Runtime/EditorScriptingRuntimeService.h rename to editor/src/Product/Services/Runtime/EditorScriptingRuntimeService.h diff --git a/editor/app/Services/Scene/EditorSceneRuntime.cpp b/editor/src/Product/Services/Scene/EditorSceneRuntime.cpp similarity index 99% rename from editor/app/Services/Scene/EditorSceneRuntime.cpp rename to editor/src/Product/Services/Scene/EditorSceneRuntime.cpp index eac3d47a..a535c1a7 100644 --- a/editor/app/Services/Scene/EditorSceneRuntime.cpp +++ b/editor/src/Product/Services/Scene/EditorSceneRuntime.cpp @@ -1,4 +1,4 @@ -#include "Scene/EditorSceneRuntime.h" +#include "Product/Services/Scene/EditorSceneRuntime.h" #include diff --git a/editor/app/Services/Scene/EditorSceneRuntime.h b/editor/src/Product/Services/Scene/EditorSceneRuntime.h similarity index 97% rename from editor/app/Services/Scene/EditorSceneRuntime.h rename to editor/src/Product/Services/Scene/EditorSceneRuntime.h index 3e292e76..8688805a 100644 --- a/editor/app/Services/Scene/EditorSceneRuntime.h +++ b/editor/src/Product/Services/Scene/EditorSceneRuntime.h @@ -1,8 +1,8 @@ #pragma once -#include "Scene/EditorSceneBackend.h" -#include "Scene/SceneToolState.h" -#include "State/EditorSelectionService.h" +#include "Product/Core/Scene/EditorSceneBackend.h" +#include "Product/Services/Scene/SceneToolState.h" +#include "Product/State/EditorSelectionService.h" #include diff --git a/editor/app/Services/Scene/EngineEditorSceneBackend.cpp b/editor/src/Product/Services/Scene/EngineEditorSceneBackend.cpp similarity index 99% rename from editor/app/Services/Scene/EngineEditorSceneBackend.cpp rename to editor/src/Product/Services/Scene/EngineEditorSceneBackend.cpp index 26dbe060..46a6aa11 100644 --- a/editor/app/Services/Scene/EngineEditorSceneBackend.cpp +++ b/editor/src/Product/Services/Scene/EngineEditorSceneBackend.cpp @@ -1,4 +1,4 @@ -#include "Scene/EngineEditorSceneBackend.h" +#include "Product/Services/Scene/EngineEditorSceneBackend.h" #include #include diff --git a/editor/app/Services/Scene/EngineEditorSceneBackend.h b/editor/src/Product/Services/Scene/EngineEditorSceneBackend.h similarity index 89% rename from editor/app/Services/Scene/EngineEditorSceneBackend.h rename to editor/src/Product/Services/Scene/EngineEditorSceneBackend.h index 754e485a..68a4838e 100644 --- a/editor/app/Services/Scene/EngineEditorSceneBackend.h +++ b/editor/src/Product/Services/Scene/EngineEditorSceneBackend.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include diff --git a/editor/app/Services/Scene/SceneToolState.h b/editor/src/Product/Services/Scene/SceneToolState.h similarity index 97% rename from editor/app/Services/Scene/SceneToolState.h rename to editor/src/Product/Services/Scene/SceneToolState.h index 5bcff40a..e9b509f5 100644 --- a/editor/app/Services/Scene/SceneToolState.h +++ b/editor/src/Product/Services/Scene/SceneToolState.h @@ -1,6 +1,6 @@ #pragma once -#include "Scene/EditorSceneBackend.h" +#include "Product/Core/Scene/EditorSceneBackend.h" #include #include diff --git a/editor/app/Services/Scene/SceneViewportCameraController.h b/editor/src/Product/Services/Scene/SceneViewportCameraController.h similarity index 100% rename from editor/app/Services/Scene/SceneViewportCameraController.h rename to editor/src/Product/Services/Scene/SceneViewportCameraController.h diff --git a/editor/app/Core/State/EditorColorPickerToolState.cpp b/editor/src/Product/State/EditorColorPickerToolState.cpp similarity index 96% rename from editor/app/Core/State/EditorColorPickerToolState.cpp rename to editor/src/Product/State/EditorColorPickerToolState.cpp index 3f093623..a9f2b702 100644 --- a/editor/app/Core/State/EditorColorPickerToolState.cpp +++ b/editor/src/Product/State/EditorColorPickerToolState.cpp @@ -1,4 +1,4 @@ -#include "State/EditorColorPickerToolState.h" +#include "Product/State/EditorColorPickerToolState.h" #include diff --git a/editor/app/Core/State/EditorColorPickerToolState.h b/editor/src/Product/State/EditorColorPickerToolState.h similarity index 100% rename from editor/app/Core/State/EditorColorPickerToolState.h rename to editor/src/Product/State/EditorColorPickerToolState.h diff --git a/editor/app/Core/State/EditorCommandFocusService.h b/editor/src/Product/State/EditorCommandFocusService.h similarity index 98% rename from editor/app/Core/State/EditorCommandFocusService.h rename to editor/src/Product/State/EditorCommandFocusService.h index 159ef0e6..926bba51 100644 --- a/editor/app/Core/State/EditorCommandFocusService.h +++ b/editor/src/Product/State/EditorCommandFocusService.h @@ -1,6 +1,6 @@ #pragma once -#include "State/EditorSession.h" +#include "Product/State/EditorSession.h" #include diff --git a/editor/app/Core/State/EditorSelectionService.h b/editor/src/Product/State/EditorSelectionService.h similarity index 97% rename from editor/app/Core/State/EditorSelectionService.h rename to editor/src/Product/State/EditorSelectionService.h index e7ddd0ae..b751b5e0 100644 --- a/editor/app/Core/State/EditorSelectionService.h +++ b/editor/src/Product/State/EditorSelectionService.h @@ -1,7 +1,7 @@ #pragma once -#include "State/EditorSelectionStamp.h" -#include "State/EditorSession.h" +#include "Product/State/EditorSelectionStamp.h" +#include "Product/State/EditorSession.h" #include #include diff --git a/editor/app/Core/State/EditorSelectionStamp.h b/editor/src/Product/State/EditorSelectionStamp.h similarity index 100% rename from editor/app/Core/State/EditorSelectionStamp.h rename to editor/src/Product/State/EditorSelectionStamp.h diff --git a/editor/app/Core/State/EditorSession.cpp b/editor/src/Product/State/EditorSession.cpp similarity index 84% rename from editor/app/Core/State/EditorSession.cpp rename to editor/src/Product/State/EditorSession.cpp index e72cf3df..4f5a0ec4 100644 --- a/editor/app/Core/State/EditorSession.cpp +++ b/editor/src/Product/State/EditorSession.cpp @@ -1,6 +1,6 @@ -#include "State/EditorSession.h" +#include "Product/State/EditorSession.h" -#include "Product/EditorProductManifest.h" +#include "Product/Registry/EditorProductRegistry.h" #include @@ -52,10 +52,10 @@ std::string_view GetEditorSelectionKindName(EditorSelectionKind kind) { } EditorActionRoute ResolveEditorActionRoute(std::string_view panelId) { - if (const EditorProductPanelDescriptor* panel = - FindEditorProductPanel(panelId); - panel != nullptr) { - return panel->actionRoute; + if (const EditorWorkspaceFeatureDescriptor* feature = + FindEditorWorkspaceFeature(panelId); + feature != nullptr) { + return feature->actionRoute; } return EditorActionRoute::None; } diff --git a/editor/app/Core/State/EditorSession.h b/editor/src/Product/State/EditorSession.h similarity index 100% rename from editor/app/Core/State/EditorSession.h rename to editor/src/Product/State/EditorSession.h diff --git a/editor/src/Product/State/EditorState.h b/editor/src/Product/State/EditorState.h new file mode 100644 index 00000000..371faff4 --- /dev/null +++ b/editor/src/Product/State/EditorState.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Product/State/EditorSession.h" +#include "Product/Framework/UtilityWindow/EditorUtilityWindowContent.h" + +#include + +#include +#include +#include + +namespace XCEngine::UI::Editor::Product { + +struct EditorStatusState { + std::string channel = {}; + std::string message = {}; +}; + +struct EditorState { + App::EditorSession session = {}; + UIEditorWindowWorkspaceSet windowWorkspace = {}; + std::vector consoleEntries = {}; + EditorStatusState status = {}; + std::optional pendingUtilityWindowRequest = {}; +}; + +} // namespace XCEngine::UI::Editor::Product diff --git a/editor/app/Support/EnvironmentFlags.h b/editor/src/Product/Support/EnvironmentFlags.h similarity index 100% rename from editor/app/Support/EnvironmentFlags.h rename to editor/src/Product/Support/EnvironmentFlags.h diff --git a/editor/app/Support/StringEncoding.h b/editor/src/Product/Support/StringEncoding.h similarity index 100% rename from editor/app/Support/StringEncoding.h rename to editor/src/Product/Support/StringEncoding.h diff --git a/editor/app/Support/TextFormat.h b/editor/src/Product/Support/TextFormat.h similarity index 100% rename from editor/app/Support/TextFormat.h rename to editor/src/Product/Support/TextFormat.h diff --git a/editor/src/Windowing/System/EditorWindowSystem.cpp b/editor/src/Windowing/System/EditorWindowSystem.cpp index c2c26b9c..bab84800 100644 --- a/editor/src/Windowing/System/EditorWindowSystem.cpp +++ b/editor/src/Windowing/System/EditorWindowSystem.cpp @@ -1,7 +1,6 @@ #include #include -#include "Windowing/System/EditorWindowWorkspaceStore.h" #include @@ -11,72 +10,58 @@ namespace XCEngine::UI::Editor { EditorWindowSystem::EditorWindowSystem(UIEditorPanelRegistry panelRegistry) - : m_workspaceStore(std::make_unique(std::move(panelRegistry))) {} + : m_panelRegistry(std::move(panelRegistry)) {} EditorWindowSystem::~EditorWindowSystem() = default; const UIEditorPanelRegistry& EditorWindowSystem::GetPanelRegistry() const { - return m_workspaceStore->GetPanelRegistry(); -} - -bool EditorWindowSystem::BootstrapPrimaryWindow( - std::string_view primaryWindowId, - const UIEditorWindowWorkspaceState& primaryWindowState, - std::string& outError) { - const std::string resolvedPrimaryWindowId = - primaryWindowId.empty() ? std::string("main-window") : std::string(primaryWindowId); - - UIEditorWindowWorkspaceSet windowSet = {}; - windowSet.primaryWindowId = resolvedPrimaryWindowId; - windowSet.activeWindowId = resolvedPrimaryWindowId; - - UIEditorWindowWorkspaceState resolvedPrimaryWindowState = primaryWindowState; - resolvedPrimaryWindowState.windowId = resolvedPrimaryWindowId; - windowSet.windows.push_back(std::move(resolvedPrimaryWindowState)); - - return m_workspaceStore->TrySetWindowSet(std::move(windowSet), outError); + return m_panelRegistry; } bool EditorWindowSystem::ValidateWindowSet( const UIEditorWindowWorkspaceSet& windowSet, std::string& outError) const { - return m_workspaceStore->ValidateWindowSet(windowSet, outError); + const UIEditorWindowWorkspaceValidationResult validation = + ValidateUIEditorWindowWorkspaceSet(m_panelRegistry, windowSet); + if (!validation.IsValid()) { + outError = validation.message; + return false; + } + + outError.clear(); + return true; } -bool EditorWindowSystem::IsPrimaryWindowId(std::string_view windowId) const { - return m_workspaceStore->IsPrimaryWindowId(windowId); -} - -const UIEditorWindowWorkspaceSet& EditorWindowSystem::GetWindowSet() const { - return m_workspaceStore->GetWindowSet(); +bool EditorWindowSystem::IsPrimaryWindowId( + const UIEditorWindowWorkspaceSet& windowSet, + std::string_view windowId) const { + return !windowId.empty() && windowSet.primaryWindowId == windowId; } const UIEditorWindowWorkspaceState* EditorWindowSystem::FindWindowState( + const UIEditorWindowWorkspaceSet& windowSet, std::string_view windowId) const { - return FindUIEditorWindowWorkspaceState(GetWindowSet(), windowId); + return FindUIEditorWindowWorkspaceState(windowSet, windowId); } -bool EditorWindowSystem::TryBuildLiveWindowWorkspaceController( +bool EditorWindowSystem::TryBuildWindowWorkspaceController( + const UIEditorWindowWorkspaceSet& windowSet, std::string_view windowId, - UIEditorWorkspaceController& outController) { - UIEditorWindowWorkspaceState* const windowState = - m_workspaceStore->FindMutableWindowState(windowId); + UIEditorWorkspaceController& outController) const { + const UIEditorWindowWorkspaceState* const windowState = + FindUIEditorWindowWorkspaceState(windowSet, windowId); if (windowState == nullptr) { outController = {}; return false; } - outController = UIEditorWorkspaceController::BindToState( + outController = UIEditorWorkspaceController( GetPanelRegistry(), windowState->workspace, windowState->session); return true; } -UIEditorWindowWorkspaceController EditorWindowSystem::BuildWorkspaceMutationController() const { - return UIEditorWindowWorkspaceController(GetPanelRegistry(), GetWindowSet()); -} - EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForWindowSet( const UIEditorWindowWorkspaceSet& targetWindowSet, const std::vector& hostWindows, @@ -96,6 +81,7 @@ EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForWindowSet( } EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForDestroyedWindow( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, std::string_view windowId, const std::vector& hostWindows, std::wstring_view primaryWindowTitle, @@ -105,7 +91,7 @@ EditorWindowSynchronizationPlan EditorWindowSystem::BuildPlanForDestroyedWindow( return {}; } - UIEditorWindowWorkspaceSet nextWindowSet = GetWindowSet(); + UIEditorWindowWorkspaceSet nextWindowSet = authoritativeWindowSet; const bool destroyedPrimary = nextWindowSet.primaryWindowId == windowId; if (!RemoveWindowStateFromSet(nextWindowSet, windowId)) { outError = @@ -168,32 +154,99 @@ EditorWindowSynchronizationPlan EditorWindowSystem::BuildSynchronizationPlan( return plan; } -bool EditorWindowSystem::CommitSynchronizationPlan( - const EditorWindowSynchronizationPlan& plan, - std::string& outError) { - if (!plan.valid) { - outError = !plan.errorMessage.empty() - ? plan.errorMessage - : "cannot commit invalid synchronization plan"; - return false; +EditorWindowWorkspaceMutation EditorWindowSystem::BuildWorkspaceMutation( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + const std::function& evaluator, + std::string preferredNewWindowId) const { + EditorWindowWorkspaceMutation mutation = {}; + if (!evaluator) { + mutation.operation.status = UIEditorWindowWorkspaceOperationStatus::Rejected; + mutation.operation.message = "window workspace mutation evaluator is unavailable"; + return mutation; } - if (plan.targetWindowSet.windows.empty()) { - m_workspaceStore->ClearWindowSet(); - outError.clear(); - return true; + UIEditorWindowWorkspaceController controller( + GetPanelRegistry(), + authoritativeWindowSet); + mutation.operation = evaluator(controller); + if (mutation.HasStateChange()) { + mutation.targetWindowSet = controller.GetWindowSet(); + mutation.preferredNewWindowId = std::move(preferredNewWindowId); } - return m_workspaceStore->TrySetWindowSet(plan.targetWindowSet, outError); + return mutation; } -UIEditorWindowWorkspaceOperationResult EditorWindowSystem::EvaluateDetachPanelToNewWindow( +EditorWindowWorkspaceMutation EditorWindowSystem::EvaluateDetachPanelToNewWindow( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId) const { + return BuildWorkspaceMutation( + authoritativeWindowSet, + [sourceWindowId, sourceNodeId, panelId](UIEditorWindowWorkspaceController& controller) { + return controller.DetachPanelToNewWindow( + sourceWindowId, + sourceNodeId, + panelId); + }, + std::string(panelId) + "-window"); +} + +EditorWindowWorkspaceMutation EditorWindowSystem::EvaluateMovePanelToStack( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, std::string_view sourceWindowId, std::string_view sourceNodeId, std::string_view panelId, - UIEditorWindowWorkspaceController& outController) const { - outController = BuildWorkspaceMutationController(); - return outController.DetachPanelToNewWindow(sourceWindowId, sourceNodeId, panelId); + std::string_view targetWindowId, + std::string_view targetNodeId, + std::size_t targetVisibleInsertionIndex) const { + return BuildWorkspaceMutation( + authoritativeWindowSet, + [sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + targetVisibleInsertionIndex](UIEditorWindowWorkspaceController& controller) { + return controller.MovePanelToStack( + sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + targetVisibleInsertionIndex); + }); +} + +EditorWindowWorkspaceMutation EditorWindowSystem::EvaluateDockPanelRelative( + const UIEditorWindowWorkspaceSet& authoritativeWindowSet, + std::string_view sourceWindowId, + std::string_view sourceNodeId, + std::string_view panelId, + std::string_view targetWindowId, + std::string_view targetNodeId, + UIEditorWorkspaceDockPlacement placement, + float splitRatio) const { + return BuildWorkspaceMutation( + authoritativeWindowSet, + [sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + placement, + splitRatio](UIEditorWindowWorkspaceController& controller) { + return controller.DockPanelRelative( + sourceWindowId, + sourceNodeId, + panelId, + targetWindowId, + targetNodeId, + placement, + splitRatio); + }); } bool EditorWindowSystem::RemoveWindowStateFromSet( diff --git a/editor/src/Windowing/System/EditorWindowWorkspaceStore.cpp b/editor/src/Windowing/System/EditorWindowWorkspaceStore.cpp deleted file mode 100644 index bd49f534..00000000 --- a/editor/src/Windowing/System/EditorWindowWorkspaceStore.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "Windowing/System/EditorWindowWorkspaceStore.h" - -#include -#include - -namespace XCEngine::UI::Editor { - -EditorWindowWorkspaceStore::EditorWindowWorkspaceStore(UIEditorPanelRegistry panelRegistry) - : m_panelRegistry(std::move(panelRegistry)) {} - -bool EditorWindowWorkspaceStore::ValidateWindowSet( - const UIEditorWindowWorkspaceSet& windowSet, - std::string& outError) const { - const UIEditorWindowWorkspaceValidationResult validation = - ValidateUIEditorWindowWorkspaceSet(m_panelRegistry, windowSet); - if (!validation.IsValid()) { - outError = validation.message; - return false; - } - - outError.clear(); - return true; -} - -bool EditorWindowWorkspaceStore::TrySetWindowSet( - UIEditorWindowWorkspaceSet windowSet, - std::string& outError) { - if (!ValidateWindowSet(windowSet, outError)) { - return false; - } - - m_windowSet = std::move(windowSet); - outError.clear(); - return true; -} - -bool EditorWindowWorkspaceStore::IsPrimaryWindowId(std::string_view windowId) const { - return !windowId.empty() && m_windowSet.primaryWindowId == windowId; -} - -UIEditorWindowWorkspaceState* EditorWindowWorkspaceStore::FindMutableWindowState( - std::string_view windowId) { - return FindMutableUIEditorWindowWorkspaceState(m_windowSet, windowId); -} - -} // namespace XCEngine::UI::Editor diff --git a/editor/src/Windowing/System/EditorWindowWorkspaceStore.h b/editor/src/Windowing/System/EditorWindowWorkspaceStore.h deleted file mode 100644 index 02d19c87..00000000 --- a/editor/src/Windowing/System/EditorWindowWorkspaceStore.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace XCEngine::UI::Editor { - -class EditorWindowWorkspaceStore final { -public: - explicit EditorWindowWorkspaceStore(UIEditorPanelRegistry panelRegistry); - - const UIEditorPanelRegistry& GetPanelRegistry() const { - return m_panelRegistry; - } - - bool ValidateWindowSet( - const UIEditorWindowWorkspaceSet& windowSet, - std::string& outError) const; - bool TrySetWindowSet( - UIEditorWindowWorkspaceSet windowSet, - std::string& outError); - void ClearWindowSet() { - m_windowSet = {}; - } - bool IsPrimaryWindowId(std::string_view windowId) const; - const UIEditorWindowWorkspaceSet& GetWindowSet() const { - return m_windowSet; - } - UIEditorWindowWorkspaceState* FindMutableWindowState(std::string_view windowId); - -private: - UIEditorPanelRegistry m_panelRegistry = {}; - UIEditorWindowWorkspaceSet m_windowSet = {}; -}; - -} // namespace XCEngine::UI::Editor diff --git a/editor/src/Workspace/UIEditorWorkspaceController.cpp b/editor/src/Workspace/UIEditorWorkspaceController.cpp index 7605058c..cb732968 100644 --- a/editor/src/Workspace/UIEditorWorkspaceController.cpp +++ b/editor/src/Workspace/UIEditorWorkspaceController.cpp @@ -139,13 +139,33 @@ UIEditorWorkspaceController::UIEditorWorkspaceController( , m_session(std::move(session)) { } +UIEditorWorkspaceController UIEditorWorkspaceController::Bind( + const UIEditorPanelRegistry& panelRegistry, + UIEditorWorkspaceModel& workspace, + UIEditorWorkspaceSession& session) { + UIEditorWorkspaceController controller = {}; + controller.m_panelRegistry = panelRegistry; + controller.m_baselineWorkspace = CanonicalizeUIEditorWorkspaceModel(workspace); + controller.m_baselineSession = session; + controller.Rebind(workspace, session); + return controller; +} + +void UIEditorWorkspaceController::Rebind( + UIEditorWorkspaceModel& workspace, + UIEditorWorkspaceSession& session) { + m_workspaceBinding = &workspace; + m_sessionBinding = &session; + workspace = CanonicalizeUIEditorWorkspaceModel(workspace); +} + UIEditorWorkspaceController::UIEditorWorkspaceController( const UIEditorWorkspaceController& other) : m_panelRegistry(other.m_panelRegistry) , m_baselineWorkspace(other.m_baselineWorkspace) , m_baselineSession(other.m_baselineSession) - , m_workspace(other.m_workspace) - , m_session(other.m_session) { + , m_workspace(other.GetWorkspace()) + , m_session(other.GetSession()) { } UIEditorWorkspaceController& UIEditorWorkspaceController::operator=( @@ -157,36 +177,28 @@ UIEditorWorkspaceController& UIEditorWorkspaceController::operator=( m_panelRegistry = other.m_panelRegistry; m_baselineWorkspace = other.m_baselineWorkspace; m_baselineSession = other.m_baselineSession; - m_workspace = other.m_workspace; - m_session = other.m_session; - m_boundWorkspace = nullptr; - m_boundSession = nullptr; + m_workspace = other.GetWorkspace(); + m_session = other.GetSession(); + m_workspaceBinding = nullptr; + m_sessionBinding = nullptr; return *this; } -UIEditorWorkspaceController UIEditorWorkspaceController::BindToState( - UIEditorPanelRegistry panelRegistry, - UIEditorWorkspaceModel& workspace, - UIEditorWorkspaceSession& session) { - UIEditorWorkspaceController controller( - std::move(panelRegistry), - workspace, - session); - controller.m_boundWorkspace = &workspace; - controller.m_boundSession = &session; - return controller; +UIEditorWorkspaceModel& UIEditorWorkspaceController::GetMutableWorkspace() { + return m_workspaceBinding != nullptr + ? *m_workspaceBinding + : m_workspace; } -void UIEditorWorkspaceController::SyncBoundState() { - if (m_boundWorkspace != nullptr) { - *m_boundWorkspace = m_workspace; - } - if (m_boundSession != nullptr) { - *m_boundSession = m_session; - } +UIEditorWorkspaceSession& UIEditorWorkspaceController::GetMutableSession() { + return m_sessionBinding != nullptr + ? *m_sessionBinding + : m_session; } UIEditorWorkspaceControllerValidationResult UIEditorWorkspaceController::ValidateState() const { + const UIEditorWorkspaceModel& workspace = GetWorkspace(); + const UIEditorWorkspaceSession& session = GetSession(); const UIEditorPanelRegistryValidationResult registryValidation = ValidateUIEditorPanelRegistry(m_panelRegistry); if (!registryValidation.IsValid()) { @@ -197,7 +209,7 @@ UIEditorWorkspaceControllerValidationResult UIEditorWorkspaceController::Validat } const UIEditorWorkspaceValidationResult workspaceValidation = - ValidateUIEditorWorkspace(m_workspace); + ValidateUIEditorWorkspace(workspace); if (!workspaceValidation.IsValid()) { UIEditorWorkspaceControllerValidationResult result = {}; result.code = UIEditorWorkspaceControllerValidationCode::InvalidWorkspace; @@ -206,7 +218,7 @@ UIEditorWorkspaceControllerValidationResult UIEditorWorkspaceController::Validat } const UIEditorWorkspaceSessionValidationResult sessionValidation = - ValidateUIEditorWorkspaceSession(m_panelRegistry, m_workspace, m_session); + ValidateUIEditorWorkspaceSession(m_panelRegistry, workspace, session); if (!sessionValidation.IsValid()) { UIEditorWorkspaceControllerValidationResult result = {}; result.code = UIEditorWorkspaceControllerValidationCode::InvalidWorkspaceSession; @@ -218,7 +230,7 @@ UIEditorWorkspaceControllerValidationResult UIEditorWorkspaceController::Validat } UIEditorWorkspaceLayoutSnapshot UIEditorWorkspaceController::CaptureLayoutSnapshot() const { - return BuildUIEditorWorkspaceLayoutSnapshot(m_workspace, m_session); + return BuildUIEditorWorkspaceLayoutSnapshot(GetWorkspace(), GetSession()); } UIEditorWorkspaceCommandResult UIEditorWorkspaceController::BuildResult( @@ -230,8 +242,9 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::BuildResult( result.status = status; result.panelId = command.panelId; result.message = std::move(message); - result.activePanelId = m_workspace.activePanelId; - result.visiblePanelIds = Internal::CollectVisiblePanelIds(m_workspace, m_session); + result.activePanelId = GetWorkspace().activePanelId; + result.visiblePanelIds = + Internal::CollectVisiblePanelIds(GetWorkspace(), GetSession()); return result; } @@ -241,8 +254,9 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::BuildLayoutO UIEditorWorkspaceLayoutOperationResult result = {}; result.status = status; result.message = std::move(message); - result.activePanelId = m_workspace.activePanelId; - result.visiblePanelIds = Internal::CollectVisiblePanelIds(m_workspace, m_session); + result.activePanelId = GetWorkspace().activePanelId; + result.visiblePanelIds = + Internal::CollectVisiblePanelIds(GetWorkspace(), GetSession()); return result; } @@ -262,16 +276,14 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::FinalizeMutation( const UIEditorWorkspaceControllerValidationResult validation = ValidateState(); if (!validation.IsValid()) { - m_workspace = previousWorkspace; - m_session = previousSession; + GetMutableWorkspace() = previousWorkspace; + GetMutableSession() = previousSession; return BuildResult( command, UIEditorWorkspaceCommandStatus::Rejected, "Command produced invalid workspace state: " + validation.message); } - SyncBoundState(); - return BuildResult( command, UIEditorWorkspaceCommandStatus::Changed, @@ -296,6 +308,8 @@ UIEditorWorkspaceController BuildDefaultUIEditorWorkspaceController( UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( const UIEditorWorkspaceCommand& command) { + UIEditorWorkspaceModel& workspace = GetMutableWorkspace(); + UIEditorWorkspaceSession& session = GetMutableSession(); const UIEditorWorkspaceControllerValidationResult validation = ValidateState(); if (command.kind != UIEditorWorkspaceCommandKind::ResetWorkspace && !validation.IsValid()) { @@ -305,12 +319,12 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( "Controller state invalid: " + validation.message); } - const UIEditorWorkspaceModel previousWorkspace = m_workspace; - const UIEditorWorkspaceSession previousSession = m_session; + const UIEditorWorkspaceModel previousWorkspace = workspace; + const UIEditorWorkspaceSession previousSession = session; const UIEditorPanelSessionState* panelState = command.kind == UIEditorWorkspaceCommandKind::ResetWorkspace ? nullptr - : FindUIEditorPanelSessionState(m_session, command.panelId); + : FindUIEditorPanelSessionState(session, command.panelId); const UIEditorPanelDescriptor* panelDescriptor = command.kind == UIEditorWorkspaceCommandKind::ResetWorkspace ? nullptr @@ -340,8 +354,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( command, TryOpenUIEditorWorkspacePanel( m_panelRegistry, - m_workspace, - m_session, + workspace, + session, command.panelId), "Panel opened and activated.", "OpenPanel failed unexpectedly.", @@ -377,8 +391,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( command, TryCloseUIEditorWorkspacePanel( m_panelRegistry, - m_workspace, - m_session, + workspace, + session, command.panelId), "Panel closed.", "ClosePanel failed unexpectedly.", @@ -414,8 +428,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( command, TryShowUIEditorWorkspacePanel( m_panelRegistry, - m_workspace, - m_session, + workspace, + session, command.panelId), "Panel shown and activated.", "ShowPanel failed unexpectedly.", @@ -457,8 +471,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( command, TryHideUIEditorWorkspacePanel( m_panelRegistry, - m_workspace, - m_session, + workspace, + session, command.panelId), "Panel hidden and active panel re-resolved.", "HidePanel failed unexpectedly.", @@ -484,7 +498,7 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( UIEditorWorkspaceCommandStatus::Rejected, "Only open and visible panels can be activated."); } - if (m_workspace.activePanelId == command.panelId) { + if (workspace.activePanelId == command.panelId) { return BuildResult( command, UIEditorWorkspaceCommandStatus::NoOp, @@ -494,8 +508,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( command, TryActivateUIEditorWorkspacePanel( m_panelRegistry, - m_workspace, - m_session, + workspace, + session, command.panelId), "Panel activated.", "ActivatePanel failed unexpectedly.", @@ -503,16 +517,16 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( previousSession); case UIEditorWorkspaceCommandKind::ResetWorkspace: - if (AreUIEditorWorkspaceModelsEquivalent(m_workspace, m_baselineWorkspace) && - AreUIEditorWorkspaceSessionsEquivalent(m_session, m_baselineSession)) { + if (AreUIEditorWorkspaceModelsEquivalent(workspace, m_baselineWorkspace) && + AreUIEditorWorkspaceSessionsEquivalent(session, m_baselineSession)) { return BuildResult( command, UIEditorWorkspaceCommandStatus::NoOp, "Workspace already matches the baseline state."); } - m_workspace = m_baselineWorkspace; - m_session = m_baselineSession; + workspace = m_baselineWorkspace; + session = m_baselineSession; return FinalizeMutation( command, true, @@ -530,6 +544,8 @@ UIEditorWorkspaceCommandResult UIEditorWorkspaceController::Dispatch( UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::RestoreLayoutSnapshot( const UIEditorWorkspaceLayoutSnapshot& snapshot) { + UIEditorWorkspaceModel& workspace = GetMutableWorkspace(); + UIEditorWorkspaceSession& session = GetMutableSession(); UIEditorWorkspaceLayoutSnapshot canonicalSnapshot = snapshot; canonicalSnapshot.workspace = CanonicalizeUIEditorWorkspaceModel(std::move(canonicalSnapshot.workspace)); @@ -561,29 +577,27 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::RestoreLayou "Layout session invalid: " + sessionValidation.message); } - if (AreUIEditorWorkspaceModelsEquivalent(m_workspace, canonicalSnapshot.workspace) && - AreUIEditorWorkspaceSessionsEquivalent(m_session, canonicalSnapshot.session)) { + if (AreUIEditorWorkspaceModelsEquivalent(workspace, canonicalSnapshot.workspace) && + AreUIEditorWorkspaceSessionsEquivalent(session, canonicalSnapshot.session)) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::NoOp, "Current state already matches the requested layout snapshot."); } - const UIEditorWorkspaceModel previousWorkspace = m_workspace; - const UIEditorWorkspaceSession previousSession = m_session; - m_workspace = canonicalSnapshot.workspace; - m_session = canonicalSnapshot.session; + const UIEditorWorkspaceModel previousWorkspace = workspace; + const UIEditorWorkspaceSession previousSession = session; + workspace = canonicalSnapshot.workspace; + session = canonicalSnapshot.session; const UIEditorWorkspaceControllerValidationResult validation = ValidateState(); if (!validation.IsValid()) { - m_workspace = previousWorkspace; - m_session = previousSession; + workspace = previousWorkspace; + session = previousSession; return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "Restored layout produced invalid controller state: " + validation.message); } - SyncBoundState(); - return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Changed, "Layout restored."); @@ -605,6 +619,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::RestoreSeria UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::SetSplitRatio( std::string_view nodeId, float splitRatio) { + UIEditorWorkspaceModel& workspace = GetMutableWorkspace(); const UIEditorWorkspaceControllerValidationResult validation = ValidateState(); if (!validation.IsValid()) { return BuildLayoutOperationResult( @@ -618,7 +633,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::SetSplitRati "SetSplitRatio requires a split node id."); } - const UIEditorWorkspaceNode* splitNode = FindUIEditorWorkspaceNode(m_workspace, nodeId); + const UIEditorWorkspaceNode* splitNode = FindUIEditorWorkspaceNode(workspace, nodeId); if (splitNode == nullptr || splitNode->kind != UIEditorWorkspaceNodeKind::Split) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, @@ -631,8 +646,8 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::SetSplitRati "Split ratio already matches the requested value."); } - const UIEditorWorkspaceModel previousWorkspace = m_workspace; - if (!TrySetUIEditorWorkspaceSplitRatio(m_workspace, nodeId, splitRatio)) { + const UIEditorWorkspaceModel previousWorkspace = workspace; + if (!TrySetUIEditorWorkspaceSplitRatio(workspace, nodeId, splitRatio)) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "Split ratio update rejected."); @@ -640,14 +655,12 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::SetSplitRati const UIEditorWorkspaceControllerValidationResult postValidation = ValidateState(); if (!postValidation.IsValid()) { - m_workspace = previousWorkspace; + workspace = previousWorkspace; return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "Split ratio update produced invalid controller state: " + postValidation.message); } - SyncBoundState(); - return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Changed, "Split ratio updated."); @@ -658,6 +671,8 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta std::string_view panelId, std::string_view targetNodeId, std::size_t targetVisibleInsertionIndex) { + UIEditorWorkspaceModel& workspace = GetMutableWorkspace(); + UIEditorWorkspaceSession& session = GetMutableSession(); { std::ostringstream trace = {}; trace << "MoveTabToStack begin sourceNode=" << sourceNodeId @@ -693,9 +708,9 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta } const UIEditorWorkspaceNode* sourceTabStack = - FindUIEditorWorkspaceNode(m_workspace, sourceNodeId); + FindUIEditorWorkspaceNode(workspace, sourceNodeId); const UIEditorWorkspaceNode* targetTabStack = - FindUIEditorWorkspaceNode(m_workspace, targetNodeId); + FindUIEditorWorkspaceNode(workspace, targetNodeId); if (sourceTabStack == nullptr || targetTabStack == nullptr || sourceTabStack->kind != UIEditorWorkspaceNodeKind::TabStack || @@ -706,7 +721,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta } const Internal::VisibleTabStackInfo sourceInfo = - Internal::ResolveVisibleTabStackInfo(*sourceTabStack, m_session, panelId); + Internal::ResolveVisibleTabStackInfo(*sourceTabStack, session, panelId); if (!sourceInfo.panelExists) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, @@ -720,17 +735,17 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta } const std::size_t visibleTargetCount = - Internal::CountVisibleTabs(*targetTabStack, m_session); + Internal::CountVisibleTabs(*targetTabStack, session); if (targetVisibleInsertionIndex > visibleTargetCount) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "MoveTabToStack target visible insertion index is out of range."); } - const UIEditorWorkspaceModel previousWorkspace = m_workspace; + const UIEditorWorkspaceModel previousWorkspace = workspace; if (!TryMoveUIEditorWorkspaceTabToStack( - m_workspace, - m_session, + workspace, + session, sourceNodeId, panelId, targetNodeId, @@ -740,7 +755,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta "MoveTabToStack rejected."); } - if (AreUIEditorWorkspaceModelsEquivalent(previousWorkspace, m_workspace)) { + if (AreUIEditorWorkspaceModelsEquivalent(previousWorkspace, workspace)) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::NoOp, "Tab already matches the requested target stack insertion."); @@ -748,14 +763,12 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::MoveTabToSta const UIEditorWorkspaceControllerValidationResult postValidation = ValidateState(); if (!postValidation.IsValid()) { - m_workspace = previousWorkspace; + workspace = previousWorkspace; return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "MoveTabToStack produced invalid controller state: " + postValidation.message); } - SyncBoundState(); - return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Changed, "Tab moved to target stack."); @@ -767,6 +780,8 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat std::string_view targetNodeId, UIEditorWorkspaceDockPlacement placement, float splitRatio) { + UIEditorWorkspaceModel& workspace = GetMutableWorkspace(); + UIEditorWorkspaceSession& session = GetMutableSession(); { std::ostringstream trace = {}; trace << "DockTabRelative begin sourceNode=" << sourceNodeId @@ -797,9 +812,9 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat } const UIEditorWorkspaceNode* sourceTabStack = - FindUIEditorWorkspaceNode(m_workspace, sourceNodeId); + FindUIEditorWorkspaceNode(workspace, sourceNodeId); const UIEditorWorkspaceNode* targetTabStack = - FindUIEditorWorkspaceNode(m_workspace, targetNodeId); + FindUIEditorWorkspaceNode(workspace, targetNodeId); if (sourceTabStack == nullptr || targetTabStack == nullptr || sourceTabStack->kind != UIEditorWorkspaceNodeKind::TabStack || @@ -810,7 +825,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat } const Internal::VisibleTabStackInfo sourceInfo = - Internal::ResolveVisibleTabStackInfo(*sourceTabStack, m_session, panelId); + Internal::ResolveVisibleTabStackInfo(*sourceTabStack, session, panelId); if (!sourceInfo.panelExists) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, @@ -823,10 +838,10 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat "DockTabRelative only supports open and visible tabs."); } - const UIEditorWorkspaceModel previousWorkspace = m_workspace; + const UIEditorWorkspaceModel previousWorkspace = workspace; if (!TryDockUIEditorWorkspaceTabRelative( - m_workspace, - m_session, + workspace, + session, sourceNodeId, panelId, targetNodeId, @@ -837,7 +852,7 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat "DockTabRelative rejected."); } - if (AreUIEditorWorkspaceModelsEquivalent(previousWorkspace, m_workspace)) { + if (AreUIEditorWorkspaceModelsEquivalent(previousWorkspace, workspace)) { return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::NoOp, "Dock layout already matches the requested placement."); @@ -845,14 +860,12 @@ UIEditorWorkspaceLayoutOperationResult UIEditorWorkspaceController::DockTabRelat const UIEditorWorkspaceControllerValidationResult postValidation = ValidateState(); if (!postValidation.IsValid()) { - m_workspace = previousWorkspace; + workspace = previousWorkspace; return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Rejected, "DockTabRelative produced invalid controller state: " + postValidation.message); } - SyncBoundState(); - return BuildLayoutOperationResult( UIEditorWorkspaceLayoutOperationStatus::Changed, "Tab docked relative to target stack.");