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:
2026-03-25 15:35:00 +08:00
parent 3478fb414a
commit 56ec2e9b85
13 changed files with 941 additions and 20 deletions

574
Editor重构计划.md Normal file
View File

@@ -0,0 +1,574 @@
# Editor 重构计划
## 一、当前问题总结
### 1.1 架构问题
| 问题 | 位置 | 严重程度 |
|------|------|----------|
| `Panel` 滥用 `Core::Layer` 继承 | `editor/src/panels/Panel.h` | P0 |
| 单例模式滥用 | 所有 Manager 类 | P1 |
| 直接使用 `GameObject*` 裸指针 | 各 Panel 文件 | P1 |
| `EditorSceneManager` 职责过重 | `editor/src/Managers/SceneManager.h` | P1 |
| 无统一事件总线 | 整体架构 | P1 |
### 1.2 功能缺失
| 功能 | 当前状态 |
|------|----------|
| Undo/Redo | 完全缺失 |
| 场景序列化/反序列化 | 完全缺失 |
| 资源引用系统 | 完全缺失 |
| Play/Edit 模式切换 | 只有框架 |
| Transform Gizmos | 完全缺失 |
| Inspector 组件扩展 | 仅支持 TransformComponent |
| 场景/游戏视图相机控制 | 完全缺失 |
### 1.3 代码质量
- 硬编码路径(崩溃日志路径)
- 调试日志残留InspectorPanel 中大量无意义的 Debug 日志)
- 内存泄漏风险raw new 无智能指针)
- 异常处理硬编码
---
## 二、重构目标
### 2.1 短期目标(原型验证)
- [ ] 解决 P0 级架构问题
- [ ] 实现基础 Undo/Redo
- [ ] 实现场景序列化
- [ ] 分离引擎核心与编辑器状态
### 2.2 中期目标(功能完善)
- [ ] 实现 Transform Gizmos
- [ ] 实现 Inspector 组件扩展机制
- [ ] 实现场景/游戏视图相机控制
- [ ] 实现 Play/Edit 模式切换
- [ ] 实现资源引用系统
### 2.3 长期目标(生产可用)
- [ ] 编辑器配置序列化
- [ ] 插件系统
- [ ] 多场景编辑
- [ ] 性能优化
---
## 三、重构方案
### 3.1 架构重构
#### 3.1.1 Panel 独立化
**现状**
```cpp
class Panel : public Core::Layer { // ❌ 编辑器面板不是游戏层级
```
**重构**
```cpp
namespace XCEngine {
namespace Editor {
class Panel {
public:
Panel(const std::string& name) : m_name(name), m_isOpen(true) {}
virtual ~Panel() = default;
virtual void OnAttach() {}
virtual void OnDetach() {}
virtual void OnUpdate(float dt) {}
virtual void OnEvent(void* event) {}
virtual void Render() = 0;
bool IsOpen() const { return m_isOpen; }
void SetOpen(bool open) { m_isOpen = open; }
protected:
std::string m_name;
bool m_isOpen;
};
} // namespace Editor
} // namespace XCEngine
```
**理由**`Core::Layer` 设计用于游戏层级系统UI/World/Background编辑器面板只是 UI 控件,不应继承它。
#### 3.1.2 单例改造为依赖注入
**现状**
```cpp
class SelectionManager {
public:
static SelectionManager& Get() { ... } // ❌ 单例
};
```
**重构**
```cpp
class IEditorContext {
public:
virtual ~IEditorContext() = default;
virtual ISelectionManager& GetSelectionManager() = 0;
virtual ISceneManager& GetSceneManager() = 0;
virtual IProjectManager& GetProjectManager() = 0;
virtual IUndoRedoSystem& GetUndoRedoSystem() = 0;
};
class EditorLayer {
public:
void SetContext(std::shared_ptr<IEditorContext> context) { m_context = context; }
private:
std::shared_ptr<IEditorContext> m_context;
};
```
#### 3.1.3 引入事件总线
**新增 `EditorEventBus`**
```cpp
class EditorEventBus {
public:
template<typename T>
void Subscribe(std::function<void(const T&)> handler);
template<typename T>
void Unsubscribe(uint64_t id);
template<typename T>
void Publish(const T& event);
private:
std::unordered_map<size_t, std::vector<std::function<void(void*)>>> m_handlers;
std::unordered_map<size_t, uint64_t> m_nextId;
};
```
**定义事件类型**
```cpp
struct SelectionChangedEvent {
std::vector<GameObjectID> selectedObjects;
};
struct EntityCreatedEvent {
GameObjectID entityId;
};
struct EntityDestroyedEvent {
GameObjectID entityId;
};
```
#### 3.1.4 GameObject 生命周期管理
**现状**
```cpp
::XCEngine::Components::GameObject* m_selectedGameObject = nullptr; // ❌ 裸指针
```
**重构**
```cpp
using GameObjectHandle = ResourceHandle<GameObject>;
struct SelectionState {
std::vector<GameObjectHandle> selectedObjects;
GameObjectHandle primarySelection; // 最后选中的
};
```
---
### 3.2 功能重构
#### 3.2.1 Undo/Redo 系统
**Command Pattern 实现**
```cpp
class ICommand {
public:
virtual ~ICommand() = default;
virtual void Execute() = 0;
virtual void Undo() = 0;
virtual std::string GetName() const = 0;
};
class CreateEntityCommand : public ICommand {
public:
CreateEntityCommand(ISceneManager& scene, GameObjectHandle parent, std::string name);
void Execute() override;
void Undo() override;
std::string GetName() const override { return "Create " + m_name; }
private:
ISceneManager& m_scene;
GameObjectHandle m_parent;
std::string m_name;
GameObjectHandle m_createdEntity;
};
class DeleteEntityCommand : public ICommand {
public:
DeleteEntityCommand(ISceneManager& scene, GameObjectHandle entity);
void Execute() override;
void Undo() override;
std::string GetName() const override { return "Delete " + m_entityName; }
private:
ISceneManager& m_scene;
GameObjectHandle m_entity;
std::string m_entityName;
std::vector<GameObjectHandle> m_children;
GameObjectHandle m_parent;
// 存储删除前的完整状态用于恢复
};
class TransformCommand : public ICommand {
public:
TransformCommand(GameObjectHandle entity, const Transform& oldState, const Transform& newState);
void Execute() override { Apply(m_newState); }
void Undo() override { Apply(m_oldState); }
private:
void Apply(const Transform& t);
GameObjectHandle m_entity;
Transform m_oldState;
Transform m_newState;
};
```
**Undo/Redo Manager**
```cpp
class UndoRedoSystem {
public:
void Execute(std::unique_ptr<ICommand> command);
void Undo();
void Redo();
bool CanUndo() const { return m_undoStack.size() > m_historyIndex + 1; }
bool CanRedo() const { return m_historyIndex >= 0; }
private:
std::vector<std::unique_ptr<ICommand>> m_undoStack;
int m_historyIndex = -1;
static constexpr size_t MAX_HISTORY = 100;
};
```
#### 3.2.2 场景序列化
**Scene 序列化接口**
```cpp
class ISceneSerializer {
public:
virtual ~ISceneSerializer() = default;
virtual bool Serialize(IScene* scene, const std::string& path) = 0;
virtual bool Deserialize(IScene* scene, const std::string& path) = 0;
};
class JsonSceneSerializer : public ISceneSerializer {
public:
bool Serialize(IScene* scene, const std::string& path) override;
bool Deserialize(IScene* scene, const std::string& path) override;
private:
nlohmann::json GameObjectToJson(GameObject* go);
GameObject* JsonToGameObject(const nlohmann::json& j, GameObject* parent);
};
```
**序列化格式JSON**
```json
{
"version": "1.0",
"name": "MainScene",
"entities": [
{
"id": "1234567890",
"name": "Player",
"components": [
{
"type": "TransformComponent",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1]
},
{
"type": "MeshRendererComponent",
"mesh": "Assets/Meshes/cube.mesh",
"material": "Assets/Materials/default.mat"
}
],
"children": [
{
"id": "9876543210",
"name": "Weapon",
"components": [...]
}
]
}
]
}
```
#### 3.2.3 Inspector 组件扩展机制
**抽象 Component 编辑器**
```cpp
class IComponentEditor {
public:
virtual ~IComponentEditor() = default;
virtual std::string GetComponentTypeName() const = 0;
virtual bool CanEdit(Component* component) const = 0;
virtual void Render(Component* component) = 0;
};
class TransformComponentEditor : public IComponentEditor {
public:
std::string GetComponentTypeName() const override { return "TransformComponent"; }
bool CanEdit(Component* component) const override {
return dynamic_cast<TransformComponent*>(component) != nullptr;
}
void Render(Component* component) override;
};
class InspectorPanel {
public:
void RegisterComponentEditor(std::unique_ptr<IComponentEditor> editor);
void RenderGameObject(GameObject* gameObject);
private:
std::vector<std::unique_ptr<IComponentEditor>> m_editors;
std::unique_ptr<IComponentEditor> GetEditorFor(Component* component);
};
```
**注册默认编辑器**
```cpp
void InspectorPanel::Initialize() {
RegisterComponentEditor(std::make_unique<TransformComponentEditor>());
// 后续添加更多编辑器
}
```
#### 3.2.4 资源引用系统
**AssetHandle**
```cpp
template<typename T>
class AssetHandle {
public:
AssetHandle() = default;
explicit AssetHandle(AssetID id, std::shared_ptr<T> asset = nullptr);
AssetID GetID() const { return m_id; }
T* Get() const { return m_asset.get(); }
T* operator->() const { return m_asset.get(); }
explicit operator bool() const { return m_asset != nullptr; }
private:
AssetID m_id;
std::shared_ptr<T> m_asset;
};
class MeshRendererComponent {
public:
AssetHandle<Mesh> GetMesh() const { return m_mesh; }
void SetMesh(AssetHandle<Mesh> mesh) { m_mesh = mesh; }
private:
AssetHandle<Mesh> m_mesh;
AssetHandle<Material> m_material;
};
```
---
### 3.3 界面重构
#### 3.3.1 SceneView 实现
**相机控制器**
```cpp
class SceneViewCameraController {
public:
enum class Mode { Pan, Orbit, Zoom };
void OnUpdate(float dt);
void OnMouseMove(int x, int y);
void OnMouseWheel(int delta);
Matrix4 GetViewMatrix() const;
Matrix4 GetProjectionMatrix() const;
private:
Mode m_mode = Mode::Orbit;
Vector3 m_target = Vector3::Zero();
float m_distance = 10.0f;
float m_pitch = 0.0f;
float m_yaw = 0.0f;
bool m_isDragging = false;
ImVec2 m_lastMousePos;
};
```
**Gizmos 渲染**
```cpp
class GizmosRenderer {
public:
void DrawTranslateGizmo(GameObjectHandle target);
void DrawRotateGizmo(GameObjectHandle target);
void DrawScaleGizmo(GameObjectHandle target);
enum class Axis { X, Y, Z, XY, XZ, YZ, XYZ };
std::optional<Axis> HandleInput(const Ray& ray);
};
```
#### 3.3.2 Editor 模式状态机
```cpp
enum class EditorMode { Edit, Play, Paused };
class EditorStateMachine {
public:
void SetMode(EditorMode mode);
EditorMode GetMode() const { return m_mode; }
void Update(float dt);
private:
EditorMode m_mode = EditorMode::Edit;
void EnterEditMode();
void ExitEditMode();
void EnterPlayMode();
void ExitPlayMode();
};
```
---
## 四、重构实施计划
### 阶段一:架构重构(第 1-2 周)
| 任务 | 依赖 | 状态 |
|------|------|------|
| Panel 类独立化 | - | ✅ 完成 |
| 引入事件总线 | - | ✅ 完成 |
| EditorContext 接口定义 | Panel 独立化 | ✅ 完成 |
| SelectionManager 改造 | 事件总线 | ✅ 完成 |
| EditorLayer 重构 | 上述全部 | ✅ 完成 |
**已完成的文件**
- `editor/src/panels/Panel.h/cpp` - 独立的 Panel 基类
- `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` - 编辑器上下文实现
### 阶段二:核心功能(第 3-4 周)
| 任务 | 依赖 | 状态 |
|------|------|------|
| Undo/Redo 系统 | 事件总线 | TODO |
| Command 基类 | - | TODO |
| 常用 Command 实现 | Undo/Redo 系统 | TODO |
| 场景序列化 | Command 基类 | TODO |
| SceneManager 重构 | 事件总线、Command | TODO |
### 阶段三Inspector 改进(第 5-6 周)
| 任务 | 依赖 | 状态 |
|------|------|------|
| ComponentEditor 接口 | 阶段一完成 | TODO |
| TransformComponentEditor | ComponentEditor 接口 | TODO |
| MeshRendererComponentEditor | AssetHandle 系统 | TODO |
| 动态添加组件 UI | ComponentEditor 接口 | TODO |
### 阶段四SceneView 实现(第 7-8 周)
| 任务 | 依赖 | 状态 |
|------|------|------|
| CameraController | - | TODO |
| GizmosRenderer | CameraController | TODO |
| Selection Outline | GizmosRenderer | TODO |
| Grid Renderer | - | TODO |
### 阶段五Play/Edit 模式(第 9-10 周)
| 任务 | 依赖 | 状态 |
|------|------|------|
| EditorStateMachine | 阶段一完成 | TODO |
| 场景状态保存/恢复 | 序列化 | TODO |
| GameView 集成 | StateMachine | TODO |
| 工具栏 UI | GizmosRenderer | TODO |
---
## 五、注意事项
### 5.1 向后兼容
- 保持 `GameObject``Component` 等核心类接口不变
- 新增接口使用 `virtual` 或默认实现
- 序列化格式预留 `version` 字段
### 5.2 性能考虑
- Undo/Redo 栈限制最大容量
- 事件总线使用 `std::function``std::any` 存储避免类型擦除开销
- Inspector 使用 `std::unordered_map` 加速组件编辑器查找
### 5.3 测试计划
- 单元测试Command、Serializer、EventBus
- 集成测试Undo/Redo 完整流程
- UI 测试:手动测试为主
---
## 六、代码规范
### 6.1 命名规范
- Editor 相关类放在 `XCEngine::Editor` 命名空间
- 接口类前缀 `I`(如 `ISelectionManager`
- Editor 内部实现后缀 `Impl`(如 `SelectionManagerImpl`
### 6.2 文件组织
```
editor/src/
├── Core/
│ ├── EditorContext.h/cpp
│ ├── EventBus.h/cpp
│ └── UndoRedo.h/cpp
├── Commands/
│ ├── Command.h
│ ├── CreateEntityCommand.h/cpp
│ ├── DeleteEntityCommand.h/cpp
│ └── TransformCommand.h/cpp
├── Panels/
│ ├── Panel.h # 基础类
│ ├── HierarchyPanel.h/cpp
│ ├── InspectorPanel.h/cpp
│ └── ...
└── UI/
├── Gizmos.h/cpp
└── CameraController.h/cpp
```
### 6.3 禁止事项
- ❌ 禁止在 Editor 代码中使用 `static` 单例模式
- ❌ 禁止直接持有 `GameObject*` 裸指针
- ❌ 禁止在 Release 版本保留 `Debug::Logger` 调用
- ❌ 禁止硬编码任何路径

View File

@@ -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();

View File

@@ -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;
};
}

View 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;
};
}
}

View 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;
};
}
}

View 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;
};
}
}

View 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;
};
}
}

View 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;
};
}
}

View 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;
};
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -3,5 +3,11 @@
namespace XCEngine {
namespace Editor {
Panel::Panel(const std::string& name)
: m_name(name), m_isOpen(true) {
}
Panel::~Panel() = default;
}
}

View File

@@ -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;
};