#include "EditorWorkspacePanelRegistry.h" #include "Panels/EditorPanelIds.h" #include "Console/ConsolePanel.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 namespace XCEngine::UI::Editor::App { namespace { constexpr int kSceneUpdatePriority = 0; 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: 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( context.services.session, ResolveHostedPanelDispatchEntry( context.shellFrame.hostedPanelDispatchFrame, GetPanelId())); } void Append(::XCEngine::UI::UIDrawList& drawList) const override { m_panel.Append(drawList); } private: ConsolePanel m_panel = {}; }; class HierarchyWorkspacePanel final : public EditorWorkspacePanel { public: 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( EditorPanelServices& services, UIEditorWorkspaceController&) override { m_panel.SetSceneRuntime(&services.sceneRuntime); m_panel.SetCommandFocusService(&services.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: HierarchyPanel m_panel = {}; }; class InspectorWorkspacePanel final : public EditorWorkspacePanel { public: 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(&context.services.commandFocusService); m_panel.Update( context.services, ResolveHostedPanelDispatchEntry( context.shellFrame.hostedPanelDispatchFrame, GetPanelId()), context.hostedContentEvents); } void Append(::XCEngine::UI::UIDrawList& drawList) const override { m_panel.Append(drawList); } EditorEditCommandRoute* GetEditCommandRoute() override { return &m_panel; } private: InspectorPanel m_panel = {}; }; class ProjectWorkspacePanel final : public EditorWorkspacePanel { public: 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(&context.services.projectRuntime); m_panel.SetCommandFocusService(&context.services.commandFocusService); m_panel.SetSystemInteractionHost(context.services.systemInteractionHost); 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: ProjectPanel m_panel = {}; }; class SceneWorkspacePanel final : public EditorWorkspacePanel { public: 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( EditorPanelServices& services, UIEditorWorkspaceController&) override { m_commandRoute.BindSceneRuntime(&services.sceneRuntime); m_feature.SetCommandFocusService(&services.commandFocusService); m_feature.SyncRenderRequest(services.sceneRuntime); } void Update(const EditorWorkspacePanelUpdateContext& context) override { m_feature.Update( context.services.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: SceneViewportFeature m_feature = {}; SceneEditCommandRoute m_commandRoute = {}; }; std::unique_ptr CreateWorkspacePanelRuntime( const EditorProductPanelDescriptor& panel) { switch (panel.runtimeKind) { case EditorProductPanelRuntimeKind::Console: return std::make_unique(); case EditorProductPanelRuntimeKind::Hierarchy: return std::make_unique(); case EditorProductPanelRuntimeKind::Inspector: return std::make_unique(); case EditorProductPanelRuntimeKind::Project: return std::make_unique(); case EditorProductPanelRuntimeKind::Scene: return std::make_unique(); case EditorProductPanelRuntimeKind::None: default: return nullptr; } } } // namespace EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet() { EditorWorkspacePanelRuntimeSet panels = {}; for (const EditorProductPanelDescriptor& panel : GetEditorProductPanels()) { if (std::unique_ptr runtime = CreateWorkspacePanelRuntime(panel); runtime != nullptr) { panels.AddPanel(std::move(runtime)); } } return panels; } } // namespace XCEngine::UI::Editor::App