611 lines
19 KiB
C++
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
|