Files
XCEngine/editor/app/Features/EditorWorkspacePanelRegistry.cpp

644 lines
21 KiB
C++

#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 <XCEditor/Panels/UIEditorHostedPanelDispatch.h>
#include <memory>
#include <sstream>
#include <utility>
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<EditorWorkspacePanelFrameEvent> CollectFrameEvents() const override {
std::vector<EditorWorkspacePanelFrameEvent> 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<EditorWorkspacePanelFrameEvent> CollectFrameEvents() const override {
std::vector<EditorWorkspacePanelFrameEvent> 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<EditorWorkspacePanel> 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<ConsoleWorkspacePanel>(session);
case EditorProductPanelRuntimeKind::Game:
return std::make_unique<GameWorkspacePanel>(commandFocusService);
case EditorProductPanelRuntimeKind::Hierarchy:
return std::make_unique<HierarchyWorkspacePanel>(
sceneRuntime,
commandFocusService);
case EditorProductPanelRuntimeKind::Inspector:
return std::make_unique<InspectorWorkspacePanel>(
session,
commandFocusService,
projectRuntime,
sceneRuntime,
colorPickerToolState,
requestOpenUtilityWindow);
case EditorProductPanelRuntimeKind::Project:
return std::make_unique<ProjectWorkspacePanel>(
projectRuntime,
commandFocusService,
systemInteractionHost,
requestOpenSceneAsset);
case EditorProductPanelRuntimeKind::Scene:
return std::make_unique<SceneWorkspacePanel>(
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<EditorWorkspacePanel> 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