diff --git a/Editor重构计划.md b/Editor重构计划.md new file mode 100644 index 00000000..9b4d70b6 --- /dev/null +++ b/Editor重构计划.md @@ -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 context) { m_context = context; } + +private: + std::shared_ptr m_context; +}; +``` + +#### 3.1.3 引入事件总线 + +**新增 `EditorEventBus`**: +```cpp +class EditorEventBus { +public: + template + void Subscribe(std::function handler); + template + void Unsubscribe(uint64_t id); + template + void Publish(const T& event); + +private: + std::unordered_map>> m_handlers; + std::unordered_map m_nextId; +}; +``` + +**定义事件类型**: +```cpp +struct SelectionChangedEvent { + std::vector selectedObjects; +}; + +struct EntityCreatedEvent { + GameObjectID entityId; +}; + +struct EntityDestroyedEvent { + GameObjectID entityId; +}; +``` + +#### 3.1.4 GameObject 生命周期管理 + +**现状**: +```cpp +::XCEngine::Components::GameObject* m_selectedGameObject = nullptr; // ❌ 裸指针 +``` + +**重构**: +```cpp +using GameObjectHandle = ResourceHandle; + +struct SelectionState { + std::vector 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 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 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> 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(component) != nullptr; + } + void Render(Component* component) override; +}; + +class InspectorPanel { +public: + void RegisterComponentEditor(std::unique_ptr editor); + void RenderGameObject(GameObject* gameObject); + +private: + std::vector> m_editors; + std::unique_ptr GetEditorFor(Component* component); +}; +``` + +**注册默认编辑器**: +```cpp +void InspectorPanel::Initialize() { + RegisterComponentEditor(std::make_unique()); + // 后续添加更多编辑器 +} +``` + +#### 3.2.4 资源引用系统 + +**AssetHandle**: +```cpp +template +class AssetHandle { +public: + AssetHandle() = default; + explicit AssetHandle(AssetID id, std::shared_ptr 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 m_asset; +}; + +class MeshRendererComponent { +public: + AssetHandle GetMesh() const { return m_mesh; } + void SetMesh(AssetHandle mesh) { m_mesh = mesh; } + +private: + AssetHandle m_mesh; + AssetHandle 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 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` 调用 +- ❌ 禁止硬编码任何路径 diff --git a/editor/src/Application.cpp b/editor/src/Application.cpp index 9cfa439f..05bf1e5f 100644 --- a/editor/src/Application.cpp +++ b/editor/src/Application.cpp @@ -1,5 +1,6 @@ #include "Application.h" #include "Layers/EditorLayer.h" +#include "Core/EditorContextImpl.h" #include #include #include @@ -115,8 +116,11 @@ bool Application::Initialize(HWND hwnd) { m_srvHeap->GetCPUDescriptorHandleForHeapStart(), m_srvHeap->GetGPUDescriptorHandleForHeapStart()); + m_editorContext = std::make_shared(); + m_editorContext->SetProjectPath(exeDir); + m_editorLayer = new EditorLayer(); - m_editorLayer->SetProjectPath(exeDir); + m_editorLayer->SetContext(m_editorContext); m_layerStack.pushLayer(std::unique_ptr(m_editorLayer)); m_layerStack.onAttach(); diff --git a/editor/src/Application.h b/editor/src/Application.h index 4000b7af..0c635b35 100644 --- a/editor/src/Application.h +++ b/editor/src/Application.h @@ -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 m_editorContext; }; } diff --git a/editor/src/Core/EditorContextImpl.h b/editor/src/Core/EditorContextImpl.h new file mode 100644 index 00000000..de3826f9 --- /dev/null +++ b/editor/src/Core/EditorContextImpl.h @@ -0,0 +1,42 @@ +#pragma once + +#include "IEditorContext.h" +#include "EventBus.h" +#include "SelectionManagerImpl.h" +#include +#include + +namespace XCEngine { +namespace Editor { + +class EditorContextImpl : public IEditorContext { +public: + EditorContextImpl() + : m_eventBus(std::make_unique()) + , m_selectionManager(std::make_unique(*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 m_eventBus; + std::unique_ptr m_selectionManager; + std::string m_projectPath; +}; + +} +} diff --git a/editor/src/Core/EditorEvents.h b/editor/src/Core/EditorEvents.h new file mode 100644 index 00000000..ba59e746 --- /dev/null +++ b/editor/src/Core/EditorEvents.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace Editor { + +using GameObjectID = uint64_t; + +struct SelectionChangedEvent { + std::vector 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; +}; + +} +} diff --git a/editor/src/Core/EventBus.h b/editor/src/Core/EventBus.h new file mode 100644 index 00000000..02f7efbf --- /dev/null +++ b/editor/src/Core/EventBus.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace XCEngine { +namespace Editor { + +class EventBus { +public: + using EventHandler = std::function; + + template + uint64_t Subscribe(std::function 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 entry; + entry.id = handlerId; + entry.handler = [handler](const void* data) { + handler(*static_cast(data)); + }; + m_handlers[typeId].push_back(entry); + + return handlerId; + } + + template + 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 + 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 handler; + }; + + std::unordered_map> m_handlers; + uint64_t m_nextHandlerId = 0; +}; + +} +} diff --git a/editor/src/Core/IEditorContext.h b/editor/src/Core/IEditorContext.h new file mode 100644 index 00000000..4120026d --- /dev/null +++ b/editor/src/Core/IEditorContext.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +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; +}; + +} +} diff --git a/editor/src/Core/ISelectionManager.h b/editor/src/Core/ISelectionManager.h new file mode 100644 index 00000000..d2303784 --- /dev/null +++ b/editor/src/Core/ISelectionManager.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace Editor { + +class ISelectionManager { +public: + virtual ~ISelectionManager() = default; + + virtual void SetSelectedEntity(uint64_t entityId) = 0; + virtual void SetSelectedEntities(const std::vector& 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& GetSelectedEntities() const = 0; + virtual bool HasSelection() const = 0; + virtual size_t GetSelectionCount() const = 0; + virtual bool IsSelected(uint64_t entityId) const = 0; +}; + +} +} diff --git a/editor/src/Core/SelectionManagerImpl.h b/editor/src/Core/SelectionManagerImpl.h new file mode 100644 index 00000000..8f817816 --- /dev/null +++ b/editor/src/Core/SelectionManagerImpl.h @@ -0,0 +1,86 @@ +#pragma once + +#include "ISelectionManager.h" +#include "EventBus.h" +#include "EditorEvents.h" +#include +#include + +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& 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& 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 m_selectedEntities; +}; + +} +} diff --git a/editor/src/Layers/EditorLayer.cpp b/editor/src/Layers/EditorLayer.cpp index ea70a7b1..68c87a8b 100644 --- a/editor/src/Layers/EditorLayer.cpp +++ b/editor/src/Layers/EditorLayer.cpp @@ -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 #include @@ -16,7 +16,15 @@ namespace Editor { EditorLayer::EditorLayer() : Layer("Editor") {} +void EditorLayer::SetContext(std::shared_ptr context) { + m_context = context; +} + void EditorLayer::onAttach() { + if (!m_context) { + m_context = std::make_shared(); + } + m_menuBar = std::make_unique(); m_hierarchyPanel = std::make_unique(); m_sceneViewPanel = std::make_unique(); @@ -25,14 +33,25 @@ void EditorLayer::onAttach() { m_consolePanel = std::make_unique(); m_projectPanel = std::make_unique(); - 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; diff --git a/editor/src/Layers/EditorLayer.h b/editor/src/Layers/EditorLayer.h index a975dc00..5f047199 100644 --- a/editor/src/Layers/EditorLayer.h +++ b/editor/src/Layers/EditorLayer.h @@ -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 context); private: void setupDockspace(); void renderAllPanels(); - std::string m_projectPath; + std::shared_ptr m_context; std::unique_ptr m_menuBar; std::unique_ptr m_hierarchyPanel; diff --git a/editor/src/panels/Panel.cpp b/editor/src/panels/Panel.cpp index 8bb8aaa3..327b065f 100644 --- a/editor/src/panels/Panel.cpp +++ b/editor/src/panels/Panel.cpp @@ -3,5 +3,11 @@ namespace XCEngine { namespace Editor { +Panel::Panel(const std::string& name) + : m_name(name), m_isOpen(true) { +} + +Panel::~Panel() = default; + } } diff --git a/editor/src/panels/Panel.h b/editor/src/panels/Panel.h index ba3a987c..e0483dd2 100644 --- a/editor/src/panels/Panel.h +++ b/editor/src/panels/Panel.h @@ -1,25 +1,28 @@ #pragma once -#include #include 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; };