#include "EditorContext.h" #include "EditorShellAssetBuilder.h" #include "Engine/EditorSceneBackendFactory.h" #include "Scene/EditorSceneRuntime.h" #include "Panels/EditorPanelIds.h" #include "Viewport/EditorViewportRuntimeServices.h" #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::UI::Editor::UIEditorWorkspacePanelPresentationModel; using ::XCEngine::UI::Editor::AppendUIEditorRuntimeTrace; std::string ComposeStatusText( std::string_view status, std::string_view message) { if (status.empty()) { return std::string(message); } if (message.empty()) { return std::string(status); } return std::string(status) + ": " + std::string(message); } UIEditorWorkspacePanelPresentationModel* FindMutablePresentation( std::vector& presentations, std::string_view panelId) { for (UIEditorWorkspacePanelPresentationModel& presentation : presentations) { if (presentation.panelId == panelId) { return &presentation; } } return nullptr; } } // namespace bool EditorContext::Initialize( const EditorRuntimePaths& runtimePaths, EditorSceneBackendFactory& sceneBackendFactory) { m_valid = false; m_validationMessage.clear(); AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize begin"); m_shellAsset = BuildEditorApplicationShellAsset(runtimePaths); AppendUIEditorRuntimeTrace("startup", "BuildEditorApplicationShellAsset complete"); m_shellValidation = ValidateEditorShellAsset(m_shellAsset); AppendUIEditorRuntimeTrace( "startup", std::string("ValidateEditorShellAsset complete valid=") + (m_shellValidation.IsValid() ? "1" : "0")); if (!m_shellValidation.IsValid()) { m_validationMessage = m_shellValidation.message; 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); ResetEditorUtilityWindowRequestState(m_utilityWindowRequestState); SyncSessionFromSelectionService(); m_textMeasurer = nullptr; m_lastStatus.clear(); m_lastMessage.clear(); SetReadyStatus(); m_valid = true; AppendUIEditorRuntimeTrace("startup", "EditorContext::Initialize end"); return true; } void EditorContext::AttachTextMeasurer( const UIEditorTextMeasurer& textMeasurer) { m_textMeasurer = &textMeasurer; } void EditorContext::SetSystemInteractionHost( ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost) { m_systemInteractionHost = systemInteractionHost; } 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_shellAsset; } 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) { RequestEditorUtilityWindow(m_utilityWindowRequestState, kind); } std::optional EditorContext::ConsumeOpenUtilityWindowRequest() { return ConsumeEditorUtilityWindowRequest(m_utilityWindowRequestState); } void EditorContext::SetSelection(EditorSelectionState selection) { m_selectionService.SetSelection(std::move(selection)); SyncSessionFromSelectionService(); } void EditorContext::ClearSelection() { m_selectionService.ClearSelection(); SyncSessionFromSelectionService(); } void EditorContext::SyncSessionFromSelectionService() { m_session.selection = m_selectionService.GetSelection(); } UIEditorWorkspaceController EditorContext::BuildWorkspaceController() const { return UIEditorWorkspaceController( m_shellAsset.panelRegistry, m_shellAsset.workspace, m_shellAsset.workspaceSession); } const UIEditorTextMeasurer* EditorContext::GetTextMeasurer() const { return m_textMeasurer; } bool EditorContext::RequestOpenSceneAsset(const std::filesystem::path& scenePath) { const bool opened = m_runtimeCoordinator.RequestOpenSceneAsset(scenePath); SetStatus( "Scene", opened ? m_runtimeCoordinator.GetLastMessage() : m_runtimeCoordinator.GetLastMessage().empty() ? std::string("Failed to open scene asset.") : m_runtimeCoordinator.GetLastMessage()); SyncSessionFromSelectionService(); return opened; } UIEditorShellInteractionDefinition EditorContext::BuildShellDefinition( const UIEditorWorkspaceController& workspaceController, std::string_view captureText, EditorShellVariant variant) const { UIEditorShellInteractionDefinition definition = BuildEditorApplicationShellInteractionDefinition( m_shellAsset, workspaceController, ComposeStatusText(m_lastStatus, m_lastMessage), 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; } std::string EditorContext::DescribeWorkspaceState( const UIEditorWorkspaceController& workspaceController, const UIEditorShellInteractionState& interactionState) const { std::ostringstream stream = {}; stream << "active=" << workspaceController.GetWorkspace().activePanelId; const auto visiblePanels = CollectUIEditorWorkspaceVisiblePanels( workspaceController.GetWorkspace(), workspaceController.GetSession()); stream << " visible=["; for (std::size_t index = 0; index < visiblePanels.size(); ++index) { if (index > 0u) { stream << ','; } stream << visiblePanels[index].panelId; } stream << ']'; const auto& dockState = interactionState.workspaceInteractionState.dockHostInteractionState; stream << " dragNode=" << dockState.activeTabDragNodeId; stream << " dragPanel=" << dockState.activeTabDragPanelId; if (dockState.dockHostState.dropPreview.visible) { stream << " dropTarget=" << dockState.dockHostState.dropPreview.targetNodeId; } return stream.str(); } std::vector EditorContext::SyncWorkspacePanelFrameEvents( const std::vector& panelEvents) { return SyncWorkspacePanelEvents(panelEvents); } void EditorContext::SyncSceneViewportRenderRequest( EditorSceneViewportRuntime& sceneViewportRuntime) { (void)sceneViewportRuntime; } } // namespace XCEngine::UI::Editor::App namespace XCEngine::UI::Editor::App { namespace { constexpr std::size_t kMaxConsoleEntries = 256u; std::string ResolveViewportStatusMessage( const UIEditorShellInteractionResult& result) { if (result.viewportInputFrame.captureStarted) { return "Viewport capture started."; } if (result.viewportInputFrame.captureEnded) { return "Viewport capture ended."; } if (result.viewportInputFrame.focusGained) { return "Viewport focused."; } if (result.viewportInputFrame.focusLost) { return "Viewport focus lost."; } if (result.viewportInputFrame.pointerPressedInside) { return "Viewport pointer down."; } if (result.viewportInputFrame.pointerReleasedInside) { return "Viewport pointer up."; } if (result.viewportInputFrame.pointerMoved) { return "Viewport pointer move."; } if (result.viewportInputFrame.wheelDelta != 0.0f) { return "Viewport wheel."; } return {}; } } // namespace void EditorContext::SetReadyStatus() { SetStatus("Ready", "Application shell loaded."); } void EditorContext::SetStatus( std::string status, std::string message) { if (m_lastStatus != status || m_lastMessage != message) { AppendConsoleEntry(status, message); } m_lastStatus = std::move(status); m_lastMessage = std::move(message); } void EditorContext::UpdateStatusFromShellResult( const UIEditorWorkspaceController& workspaceController, const UIEditorShellInteractionResult& result) { (void)workspaceController; if (result.commandDispatched) { SetStatus( std::string(GetUIEditorCommandDispatchStatusName(result.commandDispatchResult.status)), result.commandDispatchResult.message.empty() ? result.commandDispatchResult.displayName : result.commandDispatchResult.message); return; } if (result.workspaceResult.dockHostResult.layoutChanged) { SetStatus("Layout", result.workspaceResult.dockHostResult.layoutResult.message); return; } if (result.workspaceResult.dockHostResult.commandExecuted) { SetStatus("Workspace", result.workspaceResult.dockHostResult.commandResult.message); return; } if (!result.viewportPanelId.empty()) { std::string message = ResolveViewportStatusMessage(result); if (!message.empty()) { SetStatus(result.viewportPanelId, std::move(message)); } return; } if (!result.menuMutation.changed) { return; } if (!result.itemId.empty() && !result.menuMutation.openedPopupId.empty()) { SetStatus("Menu", result.itemId + " opened child popup."); } else if (!result.menuId.empty() && !result.menuMutation.openedPopupId.empty()) { SetStatus("Menu", result.menuId + " opened."); } else { SetStatus("Menu", "Popup chain dismissed."); } } void EditorContext::AppendConsoleEntry( std::string channel, std::string message) { 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 EditorContext::SyncSessionConsoleProjection() { m_session.consoleEntries = m_consoleEntries; } std::vector EditorContext::SyncWorkspacePanelEvents( const std::vector& panelEvents) { std::vector entries = {}; SyncSessionFromSelectionService(); for (const EditorWorkspacePanelFrameEvent& event : panelEvents) { SetStatus(event.status, event.message); entries.push_back(WorkspaceTraceEntry{ .channel = event.traceChannel, .message = event.message, }); } return entries; } } // namespace XCEngine::UI::Editor::App