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

611 lines
19 KiB
C++

#include "Features/EditorWorkspacePanelRegistry.h"
#include "Composition/EditorPanelIds.h"
#include "Features/Console/ConsolePanel.h"
#include "Features/Hierarchy/HierarchyPanel.h"
#include "Features/Inspector/InspectorPanel.h"
#include "Features/Project/ProjectPanel.h"
#include "Features/Scene/SceneEditCommandRoute.h"
#include "Features/Scene/SceneViewportFeature.h"
#include "Rendering/Assets/BuiltInIcons.h"
#include "Rendering/Viewport/ViewportHostService.h"
#include "Composition/EditorContext.h"
#include <XCEditor/Panels/UIEditorHostedPanelDispatch.h>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <utility>
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<typename AppendFn>
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.context.GetSession(),
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(
EditorContext& context,
UIEditorWorkspaceController&) override {
m_panel.SetSceneRuntime(&context.GetSceneRuntime());
m_panel.SetCommandFocusService(&context.GetCommandFocusService());
}
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:
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.context.GetCommandFocusService());
m_panel.Update(
context.context,
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.context.GetProjectRuntime());
m_panel.SetCommandFocusService(&context.context.GetCommandFocusService());
m_panel.SetSystemInteractionHost(context.context.GetSystemInteractionHost());
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:
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(
EditorContext& context,
UIEditorWorkspaceController&) override {
m_commandRoute.BindSceneRuntime(&context.GetSceneRuntime());
m_feature.SetCommandFocusService(&context.GetCommandFocusService());
m_feature.SyncRenderRequest(context.GetSceneRuntime());
}
void Update(const EditorWorkspacePanelUpdateContext& context) override {
m_feature.Update(
context.context.GetSceneRuntime(),
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<EditorWorkspacePanel> panel) {
if (panel != nullptr) {
m_panels.push_back(std::move(panel));
}
}
void EditorWorkspacePanelRuntimeSet::Initialize(
const EditorWorkspacePanelInitializationContext& context) {
for (const std::unique_ptr<EditorWorkspacePanel>& panel : m_panels) {
panel->Initialize(context);
}
}
void EditorWorkspacePanelRuntimeSet::Shutdown(
const EditorWorkspacePanelShutdownContext& context) {
for (const std::unique_ptr<EditorWorkspacePanel>& panel : m_panels) {
panel->Shutdown(context);
}
}
void EditorWorkspacePanelRuntimeSet::ResetInteractionState() {
for (const std::unique_ptr<EditorWorkspacePanel>& panel : m_panels) {
panel->ResetInteractionState();
}
}
void EditorWorkspacePanelRuntimeSet::PrepareForShellDefinition(
EditorContext& context,
UIEditorWorkspaceController& workspaceController) {
for (const std::unique_ptr<EditorWorkspacePanel>& panel : m_panels) {
panel->PrepareForShellDefinition(context, workspaceController);
}
}
void EditorWorkspacePanelRuntimeSet::Update(
const EditorWorkspacePanelUpdateContext& context) {
const std::vector<EditorWorkspacePanel*> mainPanels =
BuildUpdateOrder(EditorWorkspacePanelUpdatePhase::Main);
for (EditorWorkspacePanel* panel : mainPanels) {
panel->Update(context);
}
context.context.SyncSessionFromCommandFocusService();
const std::vector<EditorWorkspacePanel*> afterFocusSyncPanels =
BuildUpdateOrder(EditorWorkspacePanelUpdatePhase::AfterCommandFocusSync);
for (EditorWorkspacePanel* panel : afterFocusSyncPanels) {
panel->Update(context);
}
}
void EditorWorkspacePanelRuntimeSet::AppendDrawPackets(
::XCEngine::UI::UIDrawData& drawData) const {
for (const std::unique_ptr<EditorWorkspacePanel>& 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<EditorWorkspacePanel>& panel : m_panels) {
if (panel->HasActivePointerCapture()) {
return true;
}
}
return false;
}
EditorWorkspacePanelCursorKind
EditorWorkspacePanelRuntimeSet::GetHostedContentCursorKind() const {
for (const std::unique_ptr<EditorWorkspacePanel>& 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<EditorWorkspacePanel>& panel : m_panels) {
if (panel->GetActionRoute() == route) {
return panel->GetEditCommandRoute();
}
}
return nullptr;
}
std::vector<EditorWorkspacePanelFrameEvent>
EditorWorkspacePanelRuntimeSet::CollectFrameEvents() const {
std::vector<EditorWorkspacePanelFrameEvent> events = {};
for (const std::unique_ptr<EditorWorkspacePanel>& panel : m_panels) {
std::vector<EditorWorkspacePanelFrameEvent> panelEvents =
panel->CollectFrameEvents();
events.insert(
events.end(),
std::make_move_iterator(panelEvents.begin()),
std::make_move_iterator(panelEvents.end()));
}
return events;
}
std::vector<EditorWorkspacePanel*> EditorWorkspacePanelRuntimeSet::BuildUpdateOrder(
EditorWorkspacePanelUpdatePhase phase) const {
std::vector<EditorWorkspacePanel*> panels = {};
panels.reserve(m_panels.size());
for (const std::unique_ptr<EditorWorkspacePanel>& 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<ConsoleWorkspacePanel>());
panels.AddPanel(std::make_unique<HierarchyWorkspacePanel>());
panels.AddPanel(std::make_unique<InspectorWorkspacePanel>());
panels.AddPanel(std::make_unique<ProjectWorkspacePanel>());
panels.AddPanel(std::make_unique<SceneWorkspacePanel>());
return panels;
}
} // namespace XCEngine::UI::Editor::App