refactor(editor): Phase 1 architecture refactoring
- Decouple Panel from Core::Layer (P0 issue resolved) - Add EventBus with type-safe event system - Add ISelectionManager interface with SelectionManagerImpl - Add IEditorContext for dependency injection - Update EditorLayer to use new architecture - Update Application to create and inject EditorContext New files: - editor/src/Core/EventBus.h - editor/src/Core/EditorEvents.h - editor/src/Core/ISelectionManager.h - editor/src/Core/SelectionManagerImpl.h - editor/src/Core/IEditorContext.h - editor/src/Core/EditorContextImpl.h This enables future improvements: Undo/Redo, serialization, component extensibility.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "Application.h"
|
||||
#include "Layers/EditorLayer.h"
|
||||
#include "Core/EditorContextImpl.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Debug/FileLogSink.h>
|
||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||
@@ -115,8 +116,11 @@ bool Application::Initialize(HWND hwnd) {
|
||||
m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
|
||||
m_srvHeap->GetGPUDescriptorHandleForHeapStart());
|
||||
|
||||
m_editorContext = std::make_shared<EditorContextImpl>();
|
||||
m_editorContext->SetProjectPath(exeDir);
|
||||
|
||||
m_editorLayer = new EditorLayer();
|
||||
m_editorLayer->SetProjectPath(exeDir);
|
||||
m_editorLayer->SetContext(m_editorContext);
|
||||
m_layerStack.pushLayer(std::unique_ptr<Core::Layer>(m_editorLayer));
|
||||
m_layerStack.onAttach();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EditorLayer;
|
||||
class IEditorContext;
|
||||
|
||||
class Application {
|
||||
public:
|
||||
@@ -23,6 +24,8 @@ public:
|
||||
void Render();
|
||||
void OnResize(int width, int height);
|
||||
|
||||
IEditorContext& GetEditorContext() const { return *m_editorContext; }
|
||||
|
||||
private:
|
||||
Application() = default;
|
||||
~Application() = default;
|
||||
@@ -50,6 +53,7 @@ private:
|
||||
|
||||
Core::LayerStack m_layerStack;
|
||||
EditorLayer* m_editorLayer = nullptr;
|
||||
std::shared_ptr<IEditorContext> m_editorContext;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
42
editor/src/Core/EditorContextImpl.h
Normal file
42
editor/src/Core/EditorContextImpl.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "IEditorContext.h"
|
||||
#include "EventBus.h"
|
||||
#include "SelectionManagerImpl.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EditorContextImpl : public IEditorContext {
|
||||
public:
|
||||
EditorContextImpl()
|
||||
: m_eventBus(std::make_unique<EventBus>())
|
||||
, m_selectionManager(std::make_unique<SelectionManagerImpl>(*m_eventBus)) {
|
||||
}
|
||||
|
||||
EventBus& GetEventBus() override {
|
||||
return *m_eventBus;
|
||||
}
|
||||
|
||||
ISelectionManager& GetSelectionManager() override {
|
||||
return *m_selectionManager;
|
||||
}
|
||||
|
||||
void SetProjectPath(const std::string& path) override {
|
||||
m_projectPath = path;
|
||||
}
|
||||
|
||||
const std::string& GetProjectPath() const override {
|
||||
return m_projectPath;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<EventBus> m_eventBus;
|
||||
std::unique_ptr<SelectionManagerImpl> m_selectionManager;
|
||||
std::string m_projectPath;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
52
editor/src/Core/EditorEvents.h
Normal file
52
editor/src/Core/EditorEvents.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
using GameObjectID = uint64_t;
|
||||
|
||||
struct SelectionChangedEvent {
|
||||
std::vector<GameObjectID> selectedObjects;
|
||||
GameObjectID primarySelection;
|
||||
};
|
||||
|
||||
struct EntityCreatedEvent {
|
||||
GameObjectID entityId;
|
||||
};
|
||||
|
||||
struct EntityDeletedEvent {
|
||||
GameObjectID entityId;
|
||||
};
|
||||
|
||||
struct EntityChangedEvent {
|
||||
GameObjectID entityId;
|
||||
};
|
||||
|
||||
struct EntityParentChangedEvent {
|
||||
GameObjectID entityId;
|
||||
GameObjectID oldParentId;
|
||||
GameObjectID newParentId;
|
||||
};
|
||||
|
||||
struct SceneChangedEvent {
|
||||
};
|
||||
|
||||
struct PlayModeStartedEvent {
|
||||
};
|
||||
|
||||
struct PlayModeStoppedEvent {
|
||||
};
|
||||
|
||||
struct PlayModePausedEvent {
|
||||
};
|
||||
|
||||
struct EditorModeChangedEvent {
|
||||
int oldMode;
|
||||
int newMode;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
83
editor/src/Core/EventBus.h
Normal file
83
editor/src/Core/EventBus.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EventBus {
|
||||
public:
|
||||
using EventHandler = std::function<void()>;
|
||||
|
||||
template<typename T>
|
||||
uint64_t Subscribe(std::function<void(const T&)> handler) {
|
||||
static_assert(sizeof(T) > 0, "Event type must be defined");
|
||||
size_t typeId = typeid(T).hash_code();
|
||||
uint64_t handlerId = m_nextHandlerId++;
|
||||
|
||||
auto it = m_handlers.find(typeId);
|
||||
if (it == m_handlers.end()) {
|
||||
m_handlers[typeId] = std::vector<HandlerEntry>();
|
||||
}
|
||||
|
||||
HandlerEntry entry;
|
||||
entry.id = handlerId;
|
||||
entry.handler = [handler](const void* data) {
|
||||
handler(*static_cast<const T*>(data));
|
||||
};
|
||||
m_handlers[typeId].push_back(entry);
|
||||
|
||||
return handlerId;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Unsubscribe(uint64_t handlerId) {
|
||||
static_assert(sizeof(T) > 0, "Event type must be defined");
|
||||
size_t typeId = typeid(T).hash_code();
|
||||
|
||||
auto it = m_handlers.find(typeId);
|
||||
if (it != m_handlers.end()) {
|
||||
auto& handlers = it->second;
|
||||
handlers.erase(
|
||||
std::remove_if(handlers.begin(), handlers.end(),
|
||||
[handlerId](const HandlerEntry& entry) { return entry.id == handlerId; }),
|
||||
handlers.end()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Publish(const T& event) {
|
||||
static_assert(sizeof(T) > 0, "Event type must be defined");
|
||||
size_t typeId = typeid(T).hash_code();
|
||||
|
||||
auto it = m_handlers.find(typeId);
|
||||
if (it != m_handlers.end()) {
|
||||
for (const auto& entry : it->second) {
|
||||
entry.handler(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
m_handlers.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
struct HandlerEntry {
|
||||
uint64_t id;
|
||||
std::function<void(const void*)> handler;
|
||||
};
|
||||
|
||||
std::unordered_map<size_t, std::vector<HandlerEntry>> m_handlers;
|
||||
uint64_t m_nextHandlerId = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
24
editor/src/Core/IEditorContext.h
Normal file
24
editor/src/Core/IEditorContext.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EventBus;
|
||||
class ISelectionManager;
|
||||
|
||||
class IEditorContext {
|
||||
public:
|
||||
virtual ~IEditorContext() = default;
|
||||
|
||||
virtual EventBus& GetEventBus() = 0;
|
||||
virtual ISelectionManager& GetSelectionManager() = 0;
|
||||
|
||||
virtual void SetProjectPath(const std::string& path) = 0;
|
||||
virtual const std::string& GetProjectPath() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
27
editor/src/Core/ISelectionManager.h
Normal file
27
editor/src/Core/ISelectionManager.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class ISelectionManager {
|
||||
public:
|
||||
virtual ~ISelectionManager() = default;
|
||||
|
||||
virtual void SetSelectedEntity(uint64_t entityId) = 0;
|
||||
virtual void SetSelectedEntities(const std::vector<uint64_t>& entityIds) = 0;
|
||||
virtual void AddToSelection(uint64_t entityId) = 0;
|
||||
virtual void RemoveFromSelection(uint64_t entityId) = 0;
|
||||
virtual void ClearSelection() = 0;
|
||||
|
||||
virtual uint64_t GetSelectedEntity() const = 0;
|
||||
virtual const std::vector<uint64_t>& GetSelectedEntities() const = 0;
|
||||
virtual bool HasSelection() const = 0;
|
||||
virtual size_t GetSelectionCount() const = 0;
|
||||
virtual bool IsSelected(uint64_t entityId) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
86
editor/src/Core/SelectionManagerImpl.h
Normal file
86
editor/src/Core/SelectionManagerImpl.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include "ISelectionManager.h"
|
||||
#include "EventBus.h"
|
||||
#include "EditorEvents.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class SelectionManagerImpl : public ISelectionManager {
|
||||
public:
|
||||
explicit SelectionManagerImpl(EventBus& eventBus) : m_eventBus(eventBus) {}
|
||||
|
||||
void SetSelectedEntity(uint64_t entityId) override {
|
||||
if (m_selectedEntities.empty()) {
|
||||
m_selectedEntities.push_back(entityId);
|
||||
} else {
|
||||
m_selectedEntities[0] = entityId;
|
||||
m_selectedEntities.resize(1);
|
||||
}
|
||||
PublishSelectionChanged();
|
||||
}
|
||||
|
||||
void SetSelectedEntities(const std::vector<uint64_t>& entityIds) override {
|
||||
m_selectedEntities = entityIds;
|
||||
PublishSelectionChanged();
|
||||
}
|
||||
|
||||
void AddToSelection(uint64_t entityId) override {
|
||||
if (entityId != 0 && !IsSelected(entityId)) {
|
||||
m_selectedEntities.push_back(entityId);
|
||||
PublishSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveFromSelection(uint64_t entityId) override {
|
||||
auto it = std::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId);
|
||||
if (it != m_selectedEntities.end()) {
|
||||
m_selectedEntities.erase(it);
|
||||
PublishSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearSelection() override {
|
||||
if (!m_selectedEntities.empty()) {
|
||||
m_selectedEntities.clear();
|
||||
PublishSelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetSelectedEntity() const override {
|
||||
return m_selectedEntities.empty() ? 0 : m_selectedEntities.back();
|
||||
}
|
||||
|
||||
const std::vector<uint64_t>& GetSelectedEntities() const override {
|
||||
return m_selectedEntities;
|
||||
}
|
||||
|
||||
bool HasSelection() const override {
|
||||
return !m_selectedEntities.empty();
|
||||
}
|
||||
|
||||
size_t GetSelectionCount() const override {
|
||||
return m_selectedEntities.size();
|
||||
}
|
||||
|
||||
bool IsSelected(uint64_t entityId) const override {
|
||||
return std::find(m_selectedEntities.begin(), m_selectedEntities.end(), entityId) != m_selectedEntities.end();
|
||||
}
|
||||
|
||||
private:
|
||||
void PublishSelectionChanged() {
|
||||
SelectionChangedEvent event;
|
||||
event.selectedObjects = m_selectedEntities;
|
||||
event.primarySelection = GetSelectedEntity();
|
||||
m_eventBus.Publish(event);
|
||||
}
|
||||
|
||||
EventBus& m_eventBus;
|
||||
std::vector<uint64_t> m_selectedEntities;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "panels/InspectorPanel.h"
|
||||
#include "panels/ConsolePanel.h"
|
||||
#include "panels/ProjectPanel.h"
|
||||
#include "Managers/SelectionManager.h"
|
||||
#include "Managers/SceneManager.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/EditorContextImpl.h"
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
@@ -16,7 +16,15 @@ namespace Editor {
|
||||
|
||||
EditorLayer::EditorLayer() : Layer("Editor") {}
|
||||
|
||||
void EditorLayer::SetContext(std::shared_ptr<IEditorContext> context) {
|
||||
m_context = context;
|
||||
}
|
||||
|
||||
void EditorLayer::onAttach() {
|
||||
if (!m_context) {
|
||||
m_context = std::make_shared<EditorContextImpl>();
|
||||
}
|
||||
|
||||
m_menuBar = std::make_unique<MenuBar>();
|
||||
m_hierarchyPanel = std::make_unique<HierarchyPanel>();
|
||||
m_sceneViewPanel = std::make_unique<SceneViewPanel>();
|
||||
@@ -25,14 +33,25 @@ void EditorLayer::onAttach() {
|
||||
m_consolePanel = std::make_unique<ConsolePanel>();
|
||||
m_projectPanel = std::make_unique<ProjectPanel>();
|
||||
|
||||
m_projectPanel->Initialize(m_projectPath);
|
||||
m_menuBar->OnAttach();
|
||||
m_hierarchyPanel->OnAttach();
|
||||
m_sceneViewPanel->OnAttach();
|
||||
m_gameViewPanel->OnAttach();
|
||||
m_inspectorPanel->OnAttach();
|
||||
m_consolePanel->OnAttach();
|
||||
m_projectPanel->OnAttach();
|
||||
|
||||
auto& selection = SelectionManager::Get();
|
||||
selection.OnSelectionChanged.Subscribe([](uint64_t id) {
|
||||
});
|
||||
m_projectPanel->Initialize(m_context->GetProjectPath());
|
||||
}
|
||||
|
||||
void EditorLayer::onDetach() {
|
||||
m_menuBar->OnDetach();
|
||||
m_hierarchyPanel->OnDetach();
|
||||
m_sceneViewPanel->OnDetach();
|
||||
m_gameViewPanel->OnDetach();
|
||||
m_inspectorPanel->OnDetach();
|
||||
m_consolePanel->OnDetach();
|
||||
m_projectPanel->OnDetach();
|
||||
}
|
||||
|
||||
void EditorLayer::onUpdate(float dt) {
|
||||
@@ -58,10 +77,6 @@ void EditorLayer::onImGuiRender() {
|
||||
renderAllPanels();
|
||||
}
|
||||
|
||||
void EditorLayer::SetProjectPath(const std::string& path) {
|
||||
m_projectPath = path;
|
||||
}
|
||||
|
||||
void EditorLayer::setupDockspace() {
|
||||
static ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_NoWindowMenuButton;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class IEditorContext;
|
||||
class MenuBar;
|
||||
class HierarchyPanel;
|
||||
class SceneViewPanel;
|
||||
@@ -27,13 +28,13 @@ public:
|
||||
void onEvent(void* event) override;
|
||||
void onImGuiRender() override;
|
||||
|
||||
void SetProjectPath(const std::string& path);
|
||||
void SetContext(std::shared_ptr<IEditorContext> context);
|
||||
|
||||
private:
|
||||
void setupDockspace();
|
||||
void renderAllPanels();
|
||||
|
||||
std::string m_projectPath;
|
||||
std::shared_ptr<IEditorContext> m_context;
|
||||
|
||||
std::unique_ptr<MenuBar> m_menuBar;
|
||||
std::unique_ptr<HierarchyPanel> m_hierarchyPanel;
|
||||
|
||||
@@ -3,5 +3,11 @@
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
Panel::Panel(const std::string& name)
|
||||
: m_name(name), m_isOpen(true) {
|
||||
}
|
||||
|
||||
Panel::~Panel() = default;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Layer.h>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class Panel : public Core::Layer {
|
||||
class Panel {
|
||||
public:
|
||||
Panel(const std::string& name) : Layer(name), m_isOpen(true) {}
|
||||
virtual ~Panel() = default;
|
||||
Panel(const std::string& name);
|
||||
virtual ~Panel();
|
||||
|
||||
virtual void OnAttach() {}
|
||||
virtual void OnDetach() {}
|
||||
virtual void OnUpdate(float dt) {}
|
||||
virtual void OnEvent(void* event) {}
|
||||
virtual void Render() = 0;
|
||||
|
||||
|
||||
const std::string& GetName() const { return m_name; }
|
||||
bool IsOpen() const { return m_isOpen; }
|
||||
void SetOpen(bool open) { m_isOpen = open; }
|
||||
void Toggle() { m_isOpen = !m_isOpen; }
|
||||
|
||||
void onImGuiRender() override { Render(); }
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
bool m_isOpen;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user