#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 "Assets/BuiltInIcons.h" #include "Viewport/ViewportHostService.h" #include #include #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(); } 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)); } } 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.SetBuiltInIcons(&context.builtInIcons); 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.SetBuiltInIcons(&context.builtInIcons); 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 { m_feature.Initialize( context.repoRoot, context.textureHost, &context.builtInIcons, context.viewportHostService); } void Shutdown(const EditorWorkspacePanelShutdownContext& context) override { m_feature.Shutdown(context.textureHost, context.viewportHostService); 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 = {}; }; } // namespace void EditorWorkspacePanelRuntimeSet::AddPanel( std::unique_ptr panel) { if (panel != nullptr) { m_panels.push_back(std::move(panel)); } } 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() { for (const std::unique_ptr& panel : m_panels) { panel->ResetInteractionState(); } } void EditorWorkspacePanelRuntimeSet::PrepareForShellDefinition( EditorPanelServices& services, UIEditorWorkspaceController& workspaceController) { for (const std::unique_ptr& panel : m_panels) { panel->PrepareForShellDefinition(services, 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); }); } } 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::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; } EditorWorkspacePanelRuntimeSet CreateEditorWorkspacePanelRuntimeSet() { EditorWorkspacePanelRuntimeSet panels = {}; panels.AddPanel(std::make_unique()); panels.AddPanel(std::make_unique()); panels.AddPanel(std::make_unique()); panels.AddPanel(std::make_unique()); panels.AddPanel(std::make_unique()); return panels; } } // namespace XCEngine::UI::Editor::App