#include "EditorContext.h" #include "Composition/EditorShellAssetBuilder.h" #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::UI::Editor::BuildEditorShellShortcutManager; constexpr std::size_t kMaxConsoleEntries = 256u; 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); } } // namespace bool EditorContext::Initialize(const std::filesystem::path& repoRoot) { m_shellAsset = BuildEditorShellAsset(repoRoot); m_shellValidation = ValidateEditorShellAsset(m_shellAsset); if (!m_shellValidation.IsValid()) { return false; } m_session = {}; m_session.repoRoot = repoRoot; m_session.projectRoot = (repoRoot / "project").lexically_normal(); m_hostCommandBridge.BindSession(m_session); m_shortcutManager = BuildEditorShellShortcutManager(m_shellAsset); m_shortcutManager.SetHostCommandHandler(&m_hostCommandBridge); m_shellServices = {}; m_shellServices.commandDispatcher = &m_shortcutManager.GetCommandDispatcher(); m_shellServices.shortcutManager = &m_shortcutManager; SetReadyStatus(); return true; } void EditorContext::AttachTextMeasurer( const UIEditorTextMeasurer& textMeasurer) { m_shellServices.textMeasurer = &textMeasurer; } void EditorContext::SetExitRequestHandler(std::function handler) { m_hostCommandBridge.SetExitRequestHandler(std::move(handler)); } void EditorContext::SyncSessionFromWorkspace( const UIEditorWorkspaceController& workspaceController) { SyncEditorSessionFromWorkspace(m_session, workspaceController); } bool EditorContext::IsValid() const { return m_shellValidation.IsValid(); } const std::string& EditorContext::GetValidationMessage() const { return m_shellValidation.message; } const EditorShellAsset& EditorContext::GetShellAsset() const { return m_shellAsset; } const EditorSession& EditorContext::GetSession() const { return m_session; } void EditorContext::SetSelection(EditorSelectionState selection) { m_session.selection = std::move(selection); } void EditorContext::ClearSelection() { m_session.selection = {}; } UIEditorWorkspaceController EditorContext::BuildWorkspaceController() const { return UIEditorWorkspaceController( m_shellAsset.panelRegistry, m_shellAsset.workspace, m_shellAsset.workspaceSession); } const UIEditorShellInteractionServices& EditorContext::GetShellServices() const { return m_shellServices; } UIEditorShellInteractionDefinition EditorContext::BuildShellDefinition( const UIEditorWorkspaceController& workspaceController, std::string_view captureText, EditorShellVariant variant) const { return BuildEditorShellInteractionDefinition( m_shellAsset, workspaceController, ComposeStatusText(m_lastStatus, m_lastMessage), captureText, variant); } void EditorContext::SetReadyStatus() { SetStatus("Ready", "Old editor shell baseline 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) { 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 = {}; if (result.viewportInputFrame.captureStarted) { message = "Viewport capture started."; } else if (result.viewportInputFrame.captureEnded) { message = "Viewport capture ended."; } else if (result.viewportInputFrame.focusGained) { message = "Viewport focused."; } else if (result.viewportInputFrame.focusLost) { message = "Viewport focus lost."; } else if (result.viewportInputFrame.pointerPressedInside) { message = "Viewport pointer down."; } else if (result.viewportInputFrame.pointerReleasedInside) { message = "Viewport pointer up."; } else if (result.viewportInputFrame.pointerMoved) { message = "Viewport pointer move."; } else if (result.viewportInputFrame.wheelDelta != 0.0f) { message = "Viewport wheel."; } if (!message.empty()) { SetStatus(result.viewportPanelId, std::move(message)); } return; } if (result.menuMutation.changed) { 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_session.consoleEntries.push_back(std::move(entry)); if (m_session.consoleEntries.size() > kMaxConsoleEntries) { m_session.consoleEntries.erase(m_session.consoleEntries.begin()); } } 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(); } } // namespace XCEngine::UI::Editor::App