#include "EditorWorkspacePanelRegistry.h" #include "Panels/EditorPanelIds.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 "Product/EditorProductManifest.h" #include #include #include #include namespace XCEngine::UI::Editor::App { namespace { 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: explicit ConsoleWorkspacePanel(const EditorSession& session) : m_session(session) {} std::string_view GetPanelId() const override { return kConsolePanelId; } std::string_view GetDrawListId() const override { return "XCEditorPanel.Console"; } EditorActionRoute GetActionRoute() const override { return EditorActionRoute::Console; } EditorWorkspacePanelUpdatePhase GetUpdatePhase() const override { return EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync; } int GetUpdatePriority() const override { return kConsoleUpdatePriority; } void Update(const EditorWorkspacePanelUpdateContext& context) override { m_panel.Update( m_session, ResolveHostedPanelDispatchEntry( context.shellFrame.hostedPanelDispatchFrame, GetPanelId())); } void Append(::XCEngine::UI::UIDrawList& drawList) const override { m_panel.Append(drawList); } private: const EditorSession& m_session; ConsolePanel m_panel = {}; }; class HierarchyWorkspacePanel final : public EditorWorkspacePanel { public: HierarchyWorkspacePanel( EditorSceneRuntime& sceneRuntime, EditorCommandFocusService& commandFocusService) : 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 EditorActionRoute::Hierarchy; } 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: EditorSceneRuntime& m_sceneRuntime; EditorCommandFocusService& m_commandFocusService; HierarchyPanel m_panel = {}; }; class InspectorWorkspacePanel final : public EditorWorkspacePanel { public: InspectorWorkspacePanel( const EditorSession& session, EditorCommandFocusService& commandFocusService, EditorProjectRuntime& projectRuntime, EditorSceneRuntime& sceneRuntime, EditorColorPickerToolState& colorPickerToolState, RequestOpenUtilityWindowCallback requestOpenUtilityWindow) : m_session(session) , 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 EditorActionRoute::Inspector; } int GetUpdatePriority() const override { return kInspectorUpdatePriority; } void Update(const EditorWorkspacePanelUpdateContext& context) override { m_panel.SetCommandFocusService(&m_commandFocusService); InspectorPanelContext panelContext{ .session = m_session, .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 Initialize(const EditorWorkspacePanelInitializationContext& context) override { m_textMeasurer = &context.textMeasurer; } EditorEditCommandRoute* GetEditCommandRoute() override { return &m_panel; } private: const EditorSession& m_session; 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( EditorProjectRuntime& projectRuntime, EditorCommandFocusService& commandFocusService, ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, RequestOpenSceneAssetCallback requestOpenSceneAsset) : 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 EditorActionRoute::Project; } 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); } 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: 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( EditorSceneRuntime& sceneRuntime, EditorCommandFocusService& commandFocusService) : 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 EditorActionRoute::Scene; } 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: EditorSceneRuntime& m_sceneRuntime; EditorCommandFocusService& m_commandFocusService; SceneViewportFeature m_feature = {}; SceneEditCommandRoute m_commandRoute = {}; }; class GameWorkspacePanel final : public EditorWorkspacePanel { public: explicit GameWorkspacePanel(EditorCommandFocusService& commandFocusService) : 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 EditorActionRoute::Game; } 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: EditorCommandFocusService& m_commandFocusService; GameViewportFeature m_feature = {}; }; std::unique_ptr CreateWorkspacePanelRuntime( const EditorProductPanelDescriptor& panel, const EditorSession& session, EditorCommandFocusService& commandFocusService, EditorProjectRuntime& projectRuntime, EditorSceneRuntime& sceneRuntime, EditorColorPickerToolState& colorPickerToolState, ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, const RequestOpenUtilityWindowCallback& requestOpenUtilityWindow, const RequestOpenSceneAssetCallback& requestOpenSceneAsset) { switch (panel.runtimeKind) { case EditorProductPanelRuntimeKind::Console: return std::make_unique(session); case EditorProductPanelRuntimeKind::Game: return std::make_unique(commandFocusService); case EditorProductPanelRuntimeKind::Hierarchy: return std::make_unique( sceneRuntime, commandFocusService); case EditorProductPanelRuntimeKind::Inspector: return std::make_unique( session, commandFocusService, projectRuntime, sceneRuntime, colorPickerToolState, requestOpenUtilityWindow); case EditorProductPanelRuntimeKind::Project: return std::make_unique( projectRuntime, commandFocusService, systemInteractionHost, requestOpenSceneAsset); case EditorProductPanelRuntimeKind::Scene: return std::make_unique( sceneRuntime, commandFocusService); case EditorProductPanelRuntimeKind::None: default: return nullptr; } } } // namespace EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet( const EditorSession& session, EditorCommandFocusService& commandFocusService, EditorProjectRuntime& projectRuntime, EditorSceneRuntime& sceneRuntime, EditorColorPickerToolState& colorPickerToolState, ::XCEngine::UI::Editor::System::SystemInteractionService* systemInteractionHost, RequestOpenUtilityWindowCallback requestOpenUtilityWindow, RequestOpenSceneAssetCallback requestOpenSceneAsset) { EditorWorkspacePanelRuntimeSet panels = {}; for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { if (std::unique_ptr runtime = CreateWorkspacePanelRuntime( panel, session, commandFocusService, projectRuntime, sceneRuntime, colorPickerToolState, systemInteractionHost, requestOpenUtilityWindow, requestOpenSceneAsset); runtime != nullptr) { panels.AddPanel(std::move(runtime)); } } return panels; } } // namespace XCEngine::UI::Editor::App