Route editor actions by active target
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## 当前判断
|
||||
|
||||
截至 2026-03-26,Editor 的 UI 架构重构如果只计算“架构层整理”,不计算 Viewport/RHI 和 Scene/Game 真正内容开发,整体进度大约在 **90%**。
|
||||
截至 2026-03-26,Editor 的 UI 架构重构如果只计算“架构层整理”,不计算 Viewport/RHI 和 Scene/Game 真正内容开发,整体进度大约在 **92%**。
|
||||
|
||||
如果把“编辑器整体可用度”也算进去,则大约在 **65%** 左右,因为 `Scene` / `Game` 面板目前仍然只是占位壳,Viewport 仍未回归,且部分编辑语义与回归验证还未补齐。
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
- shortcut 文本
|
||||
- enabled 状态
|
||||
- 文本输入时是否允许触发
|
||||
- shortcut 触发上下文
|
||||
- menu 与 shortcut 的统一绑定
|
||||
- button / toolbar / inspector action 的统一接线
|
||||
|
||||
@@ -93,6 +94,15 @@
|
||||
|
||||
这一层已经不再只是 `MenuBar + Hierarchy` 的试点,而是开始成为 editor 范围内的统一动作定义入口。
|
||||
|
||||
当前新增结果:
|
||||
|
||||
- 已补 shared shortcut context
|
||||
- `Global` 与 `FocusedWindow` 两种快捷键路由开始统一
|
||||
- `Hierarchy` 与 `Project` 已接入同一套 shortcut 分发
|
||||
- 已补 active action route
|
||||
- `MenuBar Edit` 已开始跟随 `Hierarchy / Project` 切换动作目标
|
||||
- `Rename` 这类依赖 panel 内联状态的动作,开始通过 `EventBus` 请求而不是直接耦合 panel 实现
|
||||
|
||||
### 5. Dock / Layout 层
|
||||
|
||||
`Dock / Layout` 已从 `EditorLayer::setupDockspace()` 中抽出,形成独立 layout controller:
|
||||
@@ -121,6 +131,20 @@
|
||||
- panel 的 attach / detach / render 顺序有了统一入口
|
||||
- 后续继续拆 panel 或补 panel 时,不需要再改一大片壳层代码
|
||||
|
||||
### 7. Application / ImGui Session 层
|
||||
|
||||
`Application` 中原先混在一起的 ImGui context 创建、字体加载、ini 路径配置、layout 持久化,已经开始抽成独立 session:
|
||||
|
||||
- `editor/src/UI/ImGuiSession.h`
|
||||
- `editor/src/UI/ImGuiBackendBridge.h`
|
||||
|
||||
这一层的意义是:
|
||||
|
||||
- `Application` 更接近窗口 / 设备 / layer host
|
||||
- ImGui 生命周期不再继续堆在 `Application.cpp`
|
||||
- Win32 / DX12 backend API 不再散落在 `Application.cpp` 与 `main.cpp`
|
||||
- 后续继续清理 backend 初始化边界时,有稳定落点
|
||||
|
||||
## 主要面板状态
|
||||
|
||||
### MenuBar
|
||||
@@ -134,6 +158,7 @@
|
||||
- `Reset Layout` 已接入事件驱动行为
|
||||
- `About` 已接入真实 modal popup
|
||||
- `Exit` 已通过事件驱动关闭 editor
|
||||
- `Edit` 菜单已开始跟随 active action route 在 `Hierarchy / Project` 之间切换
|
||||
|
||||
仍待完成:
|
||||
|
||||
@@ -148,6 +173,7 @@
|
||||
- 快捷键已接 action 层
|
||||
- 重命名状态已收成 `Begin / Commit / Cancel`
|
||||
- 重命名交互已从 panel 局部字段收口到 shared inline edit state
|
||||
- `Rename` 请求已能从 `MenuBar -> EventBus -> Hierarchy inline edit` 触发
|
||||
|
||||
仍待完成:
|
||||
|
||||
@@ -162,6 +188,7 @@
|
||||
- 上下文菜单、返回按钮、创建文件夹对话框已接 action 层
|
||||
- 资源图标绘制与图标配色已下沉到 shared UI token / widget
|
||||
- 创建文件夹弹窗已改成 shared popup state 驱动
|
||||
- `Back / Open / Delete` 已接 panel-focused keyboard action
|
||||
|
||||
仍待完成:
|
||||
|
||||
@@ -209,13 +236,12 @@
|
||||
### 高优先级
|
||||
|
||||
1. 整理 `Application.cpp` 和 `EditorLayer.cpp` 中剩余的 UI 壳逻辑
|
||||
包括字体初始化、主 dock host 初始化、部分样式 push / pop 的收口。
|
||||
包括 backend 初始化边界、主循环中剩余 ImGui / render 壳逻辑、部分样式 push / pop 的收口。
|
||||
|
||||
### 中优先级
|
||||
|
||||
2. 继续将 panel 的本地状态机抽离
|
||||
包括:
|
||||
- inspector component popup state
|
||||
- console filter state
|
||||
- 其他仍散落在 panel 内的临时交互状态
|
||||
|
||||
@@ -224,9 +250,10 @@
|
||||
|
||||
4. 统一 editor 范围内的编辑语义
|
||||
例如:
|
||||
- 哪些 panel 消费 Copy / Paste / Delete
|
||||
- 哪些 panel 消费 Copy / Paste / Delete / Open / Back
|
||||
- 哪些动作受 selection 驱动
|
||||
- 哪些动作在文本输入时必须屏蔽
|
||||
- 哪些动作通过 command,哪些动作通过 event 请求 panel 内联状态
|
||||
|
||||
### 低优先级
|
||||
|
||||
|
||||
@@ -17,6 +17,16 @@ struct ShortcutChord {
|
||||
bool alt = false;
|
||||
};
|
||||
|
||||
enum class ShortcutRoute {
|
||||
Global,
|
||||
FocusedWindow
|
||||
};
|
||||
|
||||
struct ShortcutContext {
|
||||
ShortcutRoute route = ShortcutRoute::Global;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
struct ActionBinding {
|
||||
std::string label;
|
||||
std::string shortcutLabel;
|
||||
@@ -31,6 +41,14 @@ inline ShortcutChord Shortcut(ImGuiKey key, bool ctrl = false, bool shift = fals
|
||||
return ShortcutChord{ key, ctrl, shift, alt };
|
||||
}
|
||||
|
||||
inline ShortcutContext GlobalShortcutContext() {
|
||||
return ShortcutContext{ ShortcutRoute::Global, true };
|
||||
}
|
||||
|
||||
inline ShortcutContext FocusedWindowShortcutContext() {
|
||||
return ShortcutContext{ ShortcutRoute::FocusedWindow, true };
|
||||
}
|
||||
|
||||
inline ActionBinding MakeAction(
|
||||
std::string label,
|
||||
const char* shortcutLabel = nullptr,
|
||||
@@ -118,11 +136,30 @@ inline bool IsShortcutPressed(const ShortcutChord& shortcut, const ImGuiIO& io)
|
||||
return ImGui::IsKeyPressed(shortcut.key, false);
|
||||
}
|
||||
|
||||
inline bool IsShortcutPressed(const ActionBinding& action) {
|
||||
inline bool IsShortcutContextActive(const ShortcutContext& context) {
|
||||
if (!context.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (context.route) {
|
||||
case ShortcutRoute::Global:
|
||||
return true;
|
||||
case ShortcutRoute::FocusedWindow:
|
||||
return ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsShortcutPressed(const ActionBinding& action, const ShortcutContext& context) {
|
||||
if (!action.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsShortcutContextActive(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
if (!action.allowWhenTextInput && io.WantTextInput) {
|
||||
return false;
|
||||
@@ -137,9 +174,13 @@ inline bool IsShortcutPressed(const ActionBinding& action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsShortcutPressed(const ActionBinding& action) {
|
||||
return IsShortcutPressed(action, GlobalShortcutContext());
|
||||
}
|
||||
|
||||
template <typename ExecuteFn>
|
||||
inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
|
||||
if (!IsShortcutPressed(action)) {
|
||||
inline bool HandleShortcut(const ActionBinding& action, const ShortcutContext& context, ExecuteFn&& execute) {
|
||||
if (!IsShortcutPressed(action, context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -147,6 +188,11 @@ inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ExecuteFn>
|
||||
inline bool HandleShortcut(const ActionBinding& action, ExecuteFn&& execute) {
|
||||
return HandleShortcut(action, GlobalShortcutContext(), std::forward<ExecuteFn>(execute));
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
|
||||
24
editor/src/Actions/ActionRouting.h
Normal file
24
editor/src/Actions/ActionRouting.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/EditorActionRoute.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Actions {
|
||||
|
||||
inline void ObserveFocusedActionRoute(IEditorContext& context, EditorActionRoute route) {
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)) {
|
||||
context.SetActiveActionRoute(route);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsActionRouteActive(const IEditorContext& context, EditorActionRoute route) {
|
||||
return context.GetActiveActionRoute() == route;
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#include "ActionBinding.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/IProjectManager.h"
|
||||
#include "Core/ISelectionManager.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include "Core/IUndoManager.h"
|
||||
#include "Core/AssetItem.h"
|
||||
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
@@ -128,15 +130,15 @@ inline ActionBinding MakeExitAction() {
|
||||
}
|
||||
|
||||
inline ActionBinding MakeNavigateBackAction(bool enabled) {
|
||||
return MakeAction("<", nullptr, false, enabled);
|
||||
return MakeAction("<", "Alt+Left", false, enabled, false, Shortcut(ImGuiKey_LeftArrow, false, false, true));
|
||||
}
|
||||
|
||||
inline ActionBinding MakeOpenAssetAction(bool enabled) {
|
||||
return MakeAction("Open", nullptr, false, enabled);
|
||||
return MakeAction("Open", "Enter", false, enabled, false, Shortcut(ImGuiKey_Enter), Shortcut(ImGuiKey_KeypadEnter));
|
||||
}
|
||||
|
||||
inline ActionBinding MakeDeleteAssetAction(bool enabled = true) {
|
||||
return MakeAction("Delete", nullptr, false, enabled);
|
||||
return MakeAction("Delete", "Delete", false, enabled, false, Shortcut(ImGuiKey_Delete));
|
||||
}
|
||||
|
||||
inline ActionBinding MakeCreateFolderAction(bool enabled = true) {
|
||||
@@ -179,6 +181,17 @@ inline ::XCEngine::Components::GameObject* GetSelectedGameObject(IEditorContext&
|
||||
return context.GetSceneManager().GetEntity(context.GetSelectionManager().GetSelectedEntity());
|
||||
}
|
||||
|
||||
inline AssetItemPtr GetSelectedAssetItem(IEditorContext& context) {
|
||||
auto& projectManager = context.GetProjectManager();
|
||||
const int selectedIndex = projectManager.GetSelectedIndex();
|
||||
auto& items = projectManager.GetCurrentItems();
|
||||
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(items.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return items[selectedIndex];
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -7,16 +7,12 @@
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Debug/FileLogSink.h>
|
||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||
#include <imgui_impl_win32.h>
|
||||
#include <imgui_impl_dx12.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <filesystem>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
namespace {
|
||||
|
||||
std::string WideToUtf8(const std::wstring& value) {
|
||||
@@ -66,10 +62,6 @@ std::string GetExecutableLogPath(const char* fileName) {
|
||||
return GetExecutableDirectoryUtf8() + "\\" + fileName;
|
||||
}
|
||||
|
||||
std::string BuildEditorConfigDirectory(const std::string& projectPath) {
|
||||
return (std::filesystem::path(projectPath) / ".xceditor").string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static LONG WINAPI GlobalExceptionFilter(EXCEPTION_POINTERS* exceptionPointers) {
|
||||
@@ -142,12 +134,9 @@ bool Application::Initialize(HWND hwnd) {
|
||||
PostMessageW(m_hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
});
|
||||
ConfigureImGui(m_editorContext->GetProjectPath());
|
||||
m_imguiSession.Initialize(m_editorContext->GetProjectPath());
|
||||
|
||||
ImGui_ImplWin32_Init(hwnd);
|
||||
ImGui_ImplDX12_Init(m_device, 3, DXGI_FORMAT_R8G8B8A8_UNORM, m_srvHeap,
|
||||
m_srvHeap->GetCPUDescriptorHandleForHeapStart(),
|
||||
m_srvHeap->GetGPUDescriptorHandleForHeapStart());
|
||||
m_imguiBackend.Initialize(hwnd, m_device, m_srvHeap);
|
||||
|
||||
m_editorLayer = new EditorLayer();
|
||||
m_editorLayer->SetContext(m_editorContext);
|
||||
@@ -159,16 +148,14 @@ bool Application::Initialize(HWND hwnd) {
|
||||
|
||||
void Application::Shutdown() {
|
||||
m_layerStack.onDetach();
|
||||
SaveImGuiSettings();
|
||||
|
||||
if (m_editorContext && m_exitRequestedHandlerId) {
|
||||
m_editorContext->GetEventBus().Unsubscribe<EditorExitRequestedEvent>(m_exitRequestedHandlerId);
|
||||
m_exitRequestedHandlerId = 0;
|
||||
}
|
||||
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
m_imguiBackend.Shutdown();
|
||||
m_imguiSession.Shutdown();
|
||||
|
||||
CleanupRenderTarget();
|
||||
|
||||
@@ -183,9 +170,7 @@ void Application::Shutdown() {
|
||||
}
|
||||
|
||||
void Application::Render() {
|
||||
ImGui_ImplDX12_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
m_imguiBackend.BeginFrame();
|
||||
|
||||
m_layerStack.onImGuiRender();
|
||||
UpdateWindowTitle();
|
||||
@@ -214,7 +199,7 @@ void Application::Render() {
|
||||
ID3D12DescriptorHeap* heaps[] = { m_srvHeap };
|
||||
m_commandList->SetDescriptorHeaps(1, heaps);
|
||||
|
||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList);
|
||||
m_imguiBackend.RenderDrawData(m_commandList);
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
@@ -233,45 +218,6 @@ void Application::Render() {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::ConfigureImGui(const std::string& projectPath) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ConfigureImGuiIniFile(projectPath, io);
|
||||
|
||||
if (ImFont* uiFont = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 15.0f)) {
|
||||
io.FontDefault = uiFont;
|
||||
} else {
|
||||
io.FontDefault = io.Fonts->AddFontDefault();
|
||||
}
|
||||
|
||||
unsigned char* pixels = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
ApplyUnityDarkTheme();
|
||||
}
|
||||
|
||||
void Application::ConfigureImGuiIniFile(const std::string& projectPath, ImGuiIO& io) {
|
||||
const std::filesystem::path configDir = BuildEditorConfigDirectory(projectPath);
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(configDir, ec);
|
||||
|
||||
m_imguiIniPath = (configDir / "imgui_layout.ini").string();
|
||||
io.IniFilename = m_imguiIniPath.c_str();
|
||||
}
|
||||
|
||||
void Application::SaveImGuiSettings() {
|
||||
if (m_imguiIniPath.empty() || ImGui::GetCurrentContext() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SaveIniSettingsToDisk(m_imguiIniPath.c_str());
|
||||
}
|
||||
|
||||
void Application::UpdateWindowTitle() {
|
||||
if (!m_hwnd || !m_editorContext) {
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
#include "UI/ImGuiSession.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
@@ -8,8 +11,6 @@
|
||||
|
||||
#include <XCEngine/Core/LayerStack.h>
|
||||
|
||||
#include "Theme.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
@@ -32,9 +33,6 @@ private:
|
||||
Application() = default;
|
||||
~Application() = default;
|
||||
|
||||
void ConfigureImGui(const std::string& projectPath);
|
||||
void ConfigureImGuiIniFile(const std::string& projectPath, ImGuiIO& io);
|
||||
void SaveImGuiSettings();
|
||||
bool CreateDevice();
|
||||
bool CreateRenderTarget();
|
||||
void CleanupRenderTarget();
|
||||
@@ -60,8 +58,9 @@ private:
|
||||
Core::LayerStack m_layerStack;
|
||||
EditorLayer* m_editorLayer = nullptr;
|
||||
std::shared_ptr<IEditorContext> m_editorContext;
|
||||
UI::ImGuiBackendBridge m_imguiBackend;
|
||||
UI::ImGuiSession m_imguiSession;
|
||||
uint64_t m_exitRequestedHandlerId = 0;
|
||||
std::string m_imguiIniPath;
|
||||
std::wstring m_lastWindowTitle;
|
||||
};
|
||||
|
||||
|
||||
13
editor/src/Core/EditorActionRoute.h
Normal file
13
editor/src/Core/EditorActionRoute.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
enum class EditorActionRoute {
|
||||
None,
|
||||
Hierarchy,
|
||||
Project
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -55,6 +55,14 @@ public:
|
||||
IUndoManager& GetUndoManager() override {
|
||||
return *m_undoManager;
|
||||
}
|
||||
|
||||
void SetActiveActionRoute(EditorActionRoute route) override {
|
||||
m_activeActionRoute = route;
|
||||
}
|
||||
|
||||
EditorActionRoute GetActiveActionRoute() const override {
|
||||
return m_activeActionRoute;
|
||||
}
|
||||
|
||||
void SetProjectPath(const std::string& path) override {
|
||||
m_projectPath = path;
|
||||
@@ -70,6 +78,7 @@ private:
|
||||
std::unique_ptr<SceneManager> m_sceneManager;
|
||||
std::unique_ptr<UndoManager> m_undoManager;
|
||||
std::unique_ptr<ProjectManager> m_projectManager;
|
||||
EditorActionRoute m_activeActionRoute = EditorActionRoute::None;
|
||||
std::string m_projectPath;
|
||||
uint64_t m_entityDeletedHandlerId;
|
||||
};
|
||||
|
||||
@@ -25,6 +25,10 @@ struct EntityChangedEvent {
|
||||
GameObjectID entityId;
|
||||
};
|
||||
|
||||
struct EntityRenameRequestedEvent {
|
||||
GameObjectID entityId;
|
||||
};
|
||||
|
||||
struct EntityParentChangedEvent {
|
||||
GameObjectID entityId;
|
||||
GameObjectID oldParentId;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "EditorActionRoute.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -21,6 +23,8 @@ public:
|
||||
virtual ISceneManager& GetSceneManager() = 0;
|
||||
virtual IProjectManager& GetProjectManager() = 0;
|
||||
virtual IUndoManager& GetUndoManager() = 0;
|
||||
virtual void SetActiveActionRoute(EditorActionRoute route) = 0;
|
||||
virtual EditorActionRoute GetActiveActionRoute() const = 0;
|
||||
|
||||
virtual void SetProjectPath(const std::string& path) = 0;
|
||||
virtual const std::string& GetProjectPath() const = 0;
|
||||
|
||||
65
editor/src/UI/ImGuiBackendBridge.h
Normal file
65
editor/src/UI/ImGuiBackendBridge.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_dx12.h>
|
||||
#include <imgui_impl_win32.h>
|
||||
#include <windows.h>
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace UI {
|
||||
|
||||
class ImGuiBackendBridge {
|
||||
public:
|
||||
void Initialize(
|
||||
HWND hwnd,
|
||||
ID3D12Device* device,
|
||||
ID3D12DescriptorHeap* srvHeap,
|
||||
int frameCount = 3,
|
||||
DXGI_FORMAT backBufferFormat = DXGI_FORMAT_R8G8B8A8_UNORM) {
|
||||
ImGui_ImplWin32_Init(hwnd);
|
||||
ImGui_ImplDX12_Init(
|
||||
device,
|
||||
frameCount,
|
||||
backBufferFormat,
|
||||
srvHeap,
|
||||
srvHeap->GetCPUDescriptorHandleForHeapStart(),
|
||||
srvHeap->GetGPUDescriptorHandleForHeapStart());
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
if (!m_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui_ImplDX12_Shutdown();
|
||||
ImGui_ImplWin32_Shutdown();
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
void BeginFrame() const {
|
||||
ImGui_ImplDX12_NewFrame();
|
||||
ImGui_ImplWin32_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void RenderDrawData(ID3D12GraphicsCommandList* commandList) const {
|
||||
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList);
|
||||
}
|
||||
|
||||
static bool HandleWindowMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
77
editor/src/UI/ImGuiSession.h
Normal file
77
editor/src/UI/ImGuiSession.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseTheme.h"
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace UI {
|
||||
|
||||
class ImGuiSession {
|
||||
public:
|
||||
void Initialize(const std::string& projectPath) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
|
||||
ConfigureIniFile(projectPath, io);
|
||||
ConfigureFonts(io);
|
||||
ApplyBaseTheme(ImGui::GetStyle());
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
SaveSettings();
|
||||
if (ImGui::GetCurrentContext() != nullptr) {
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
m_iniPath.clear();
|
||||
}
|
||||
|
||||
void SaveSettings() const {
|
||||
if (m_iniPath.empty() || ImGui::GetCurrentContext() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SaveIniSettingsToDisk(m_iniPath.c_str());
|
||||
}
|
||||
|
||||
const std::string& GetIniPath() const {
|
||||
return m_iniPath;
|
||||
}
|
||||
|
||||
private:
|
||||
void ConfigureIniFile(const std::string& projectPath, ImGuiIO& io) {
|
||||
const std::filesystem::path configDir = std::filesystem::path(projectPath) / ".xceditor";
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(configDir, ec);
|
||||
|
||||
m_iniPath = (configDir / "imgui_layout.ini").string();
|
||||
io.IniFilename = m_iniPath.c_str();
|
||||
}
|
||||
|
||||
void ConfigureFonts(ImGuiIO& io) const {
|
||||
if (ImFont* uiFont = io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 15.0f)) {
|
||||
io.FontDefault = uiFont;
|
||||
} else {
|
||||
io.FontDefault = io.Fonts->AddFontDefault();
|
||||
}
|
||||
|
||||
unsigned char* pixels = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
}
|
||||
|
||||
std::string m_iniPath;
|
||||
};
|
||||
|
||||
} // namespace UI
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Application.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
#include <imgui.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
@@ -72,10 +73,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
||||
if (XCEngine::Editor::UI::ImGuiBackendBridge::HandleWindowMessage(hWnd, msg, wParam, lParam))
|
||||
return true;
|
||||
|
||||
switch (msg) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Commands/EntityCommands.h"
|
||||
#include "HierarchyPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
@@ -20,6 +21,7 @@ HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
|
||||
HierarchyPanel::~HierarchyPanel() {
|
||||
if (m_context) {
|
||||
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
||||
m_context->GetEventBus().Unsubscribe<EntityRenameRequestedEvent>(m_renameRequestHandlerId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +31,11 @@ void HierarchyPanel::OnAttach() {
|
||||
OnSelectionChanged(event);
|
||||
}
|
||||
);
|
||||
m_renameRequestHandlerId = m_context->GetEventBus().Subscribe<EntityRenameRequestedEvent>(
|
||||
[this](const EntityRenameRequestedEvent& event) {
|
||||
OnRenameRequested(event);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
||||
@@ -37,12 +44,24 @@ void HierarchyPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::OnRenameRequested(const EntityRenameRequestedEvent& event) {
|
||||
if (!m_context || event.entityId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* gameObject = m_context->GetSceneManager().GetEntity(event.entityId)) {
|
||||
BeginRename(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
void HierarchyPanel::Render() {
|
||||
UI::PanelWindowScope panel(m_name.c_str());
|
||||
if (!panel.IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Hierarchy);
|
||||
|
||||
RenderSearchBar();
|
||||
|
||||
HandleKeyboardShortcuts();
|
||||
@@ -299,26 +318,24 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObje
|
||||
void HierarchyPanel::HandleKeyboardShortcuts() {
|
||||
auto& sceneManager = m_context->GetSceneManager();
|
||||
auto& selectionManager = m_context->GetSelectionManager();
|
||||
|
||||
::XCEngine::Components::GameObject* selectedGameObject = sceneManager.GetEntity(selectionManager.GetSelectedEntity());
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
Actions::HandleShortcut(Actions::MakeDeleteEntityAction(selectedGameObject), [&]() {
|
||||
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeRenameEntityAction(selectedGameObject), [&]() {
|
||||
BeginRename(selectedGameObject);
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeCopyEntityAction(selectedGameObject), [&]() {
|
||||
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakePasteEntityAction(*m_context), [&]() {
|
||||
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeDuplicateEntityAction(selectedGameObject), [&]() {
|
||||
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
}
|
||||
const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext();
|
||||
|
||||
Actions::HandleShortcut(Actions::MakeDeleteEntityAction(selectedGameObject), shortcutContext, [&]() {
|
||||
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeRenameEntityAction(selectedGameObject), shortcutContext, [&]() {
|
||||
BeginRename(selectedGameObject);
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeCopyEntityAction(selectedGameObject), shortcutContext, [&]() {
|
||||
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakePasteEntityAction(*m_context), shortcutContext, [&]() {
|
||||
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeDuplicateEntityAction(selectedGameObject), shortcutContext, [&]() {
|
||||
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
}
|
||||
|
||||
bool HierarchyPanel::PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter) {
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
|
||||
private:
|
||||
void OnSelectionChanged(const struct SelectionChangedEvent& event);
|
||||
void OnRenameRequested(const struct EntityRenameRequestedEvent& event);
|
||||
void RenderSearchBar();
|
||||
void RenderEntity(::XCEngine::Components::GameObject* gameObject, const std::string& filter);
|
||||
void RenderContextMenu(::XCEngine::Components::GameObject* gameObject);
|
||||
@@ -36,6 +37,7 @@ private:
|
||||
UI::InlineTextEditState<uint64_t, 256> m_renameState;
|
||||
SortMode m_sortMode = SortMode::Name;
|
||||
uint64_t m_selectionHandlerId = 0;
|
||||
uint64_t m_renameRequestHandlerId = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Commands/EntityCommands.h"
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "Commands/SceneCommands.h"
|
||||
#include "MenuBar.h"
|
||||
#include "Core/EditorEvents.h"
|
||||
#include "Core/EventBus.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/IProjectManager.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include "Core/IUndoManager.h"
|
||||
#include "Core/ISelectionManager.h"
|
||||
@@ -37,12 +40,14 @@ void MenuBar::HandleShortcuts() {
|
||||
return;
|
||||
}
|
||||
|
||||
Actions::HandleShortcut(Actions::MakeNewSceneAction(), [&]() { Commands::NewScene(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeOpenSceneAction(), [&]() { Commands::OpenSceneWithDialog(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeSaveSceneAction(), [&]() { Commands::SaveCurrentScene(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
|
||||
Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
|
||||
const Actions::ShortcutContext shortcutContext = Actions::GlobalShortcutContext();
|
||||
|
||||
Actions::HandleShortcut(Actions::MakeNewSceneAction(), shortcutContext, [&]() { Commands::NewScene(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeOpenSceneAction(), shortcutContext, [&]() { Commands::OpenSceneWithDialog(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeSaveSceneAction(), shortcutContext, [&]() { Commands::SaveCurrentScene(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeSaveSceneAsAction(), shortcutContext, [&]() { Commands::SaveSceneAsWithDialog(*m_context); });
|
||||
Actions::HandleShortcut(Actions::MakeUndoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Undo(); });
|
||||
Actions::HandleShortcut(Actions::MakeRedoAction(*m_context), shortcutContext, [&]() { m_context->GetUndoManager().Redo(); });
|
||||
}
|
||||
|
||||
void MenuBar::ShowFileMenu() {
|
||||
@@ -60,11 +65,29 @@ void MenuBar::ShowFileMenu() {
|
||||
|
||||
void MenuBar::ShowEditMenu() {
|
||||
::XCEngine::Components::GameObject* selectedGameObject = Actions::GetSelectedGameObject(*m_context);
|
||||
const AssetItemPtr selectedAssetItem = Actions::GetSelectedAssetItem(*m_context);
|
||||
auto& projectManager = m_context->GetProjectManager();
|
||||
const EditorActionRoute actionRoute = m_context->GetActiveActionRoute();
|
||||
|
||||
UI::DrawMenuScope("Edit", [&]() {
|
||||
Actions::DrawMenuAction(Actions::MakeUndoAction(*m_context), [&]() { m_context->GetUndoManager().Undo(); });
|
||||
Actions::DrawMenuAction(Actions::MakeRedoAction(*m_context), [&]() { m_context->GetUndoManager().Redo(); });
|
||||
Actions::DrawMenuSeparator();
|
||||
|
||||
if (actionRoute == EditorActionRoute::Project) {
|
||||
Actions::DrawMenuAction(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedAssetItem)), [&]() {
|
||||
Commands::OpenAsset(*m_context, selectedAssetItem);
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDeleteAssetAction(selectedAssetItem != nullptr), [&]() {
|
||||
Commands::DeleteAsset(projectManager, projectManager.GetSelectedIndex());
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeNavigateBackAction(projectManager.CanNavigateBack()), [&]() {
|
||||
projectManager.NavigateBack();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Actions::DrawMenuAction(Actions::MakeCutAction(false), []() {});
|
||||
Actions::DrawMenuAction(Actions::MakeCopyEntityAction(selectedGameObject), [&]() {
|
||||
Commands::CopyEntity(*m_context, selectedGameObject->GetID());
|
||||
@@ -72,6 +95,15 @@ void MenuBar::ShowEditMenu() {
|
||||
Actions::DrawMenuAction(Actions::MakePasteEntityAction(*m_context), [&]() {
|
||||
Commands::PasteEntity(*m_context, selectedGameObject ? selectedGameObject->GetID() : 0);
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDuplicateEntityAction(selectedGameObject), [&]() {
|
||||
Commands::DuplicateEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDeleteEntityAction(selectedGameObject), [&]() {
|
||||
Commands::DeleteEntity(*m_context, selectedGameObject->GetID());
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeRenameEntityAction(selectedGameObject), [&]() {
|
||||
m_context->GetEventBus().Publish(EntityRenameRequestedEvent{ selectedGameObject->GetID() });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "ProjectPanel.h"
|
||||
@@ -32,6 +33,9 @@ void ProjectPanel::Render() {
|
||||
if (!panel.IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Actions::ObserveFocusedActionRoute(*m_context, EditorActionRoute::Project);
|
||||
HandleKeyboardShortcuts();
|
||||
|
||||
auto& manager = m_context->GetProjectManager();
|
||||
|
||||
@@ -146,6 +150,30 @@ void ProjectPanel::Render() {
|
||||
}
|
||||
}
|
||||
|
||||
AssetItemPtr ProjectPanel::GetSelectedItem() const {
|
||||
return m_context ? Actions::GetSelectedAssetItem(*m_context) : nullptr;
|
||||
}
|
||||
|
||||
void ProjectPanel::HandleKeyboardShortcuts() {
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& manager = m_context->GetProjectManager();
|
||||
const AssetItemPtr selectedItem = GetSelectedItem();
|
||||
const Actions::ShortcutContext shortcutContext = Actions::FocusedWindowShortcutContext();
|
||||
|
||||
Actions::HandleShortcut(Actions::MakeNavigateBackAction(manager.CanNavigateBack()), shortcutContext, [&]() {
|
||||
manager.NavigateBack();
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeOpenAssetAction(Commands::CanOpenAsset(selectedItem)), shortcutContext, [&]() {
|
||||
Commands::OpenAsset(*m_context, selectedItem);
|
||||
});
|
||||
Actions::HandleShortcut(Actions::MakeDeleteAssetAction(selectedItem != nullptr), shortcutContext, [&]() {
|
||||
Commands::DeleteAsset(manager, manager.GetSelectedIndex());
|
||||
});
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
|
||||
auto& manager = m_context->GetProjectManager();
|
||||
bool isSelected = (manager.GetSelectedIndex() == index);
|
||||
|
||||
@@ -14,6 +14,8 @@ public:
|
||||
void Initialize(const std::string& projectPath);
|
||||
|
||||
private:
|
||||
AssetItemPtr GetSelectedItem() const;
|
||||
void HandleKeyboardShortcuts();
|
||||
void RenderAssetItem(const AssetItemPtr& item, int index);
|
||||
|
||||
char m_searchBuffer[256] = "";
|
||||
|
||||
116
managed/CMakeLists.txt
Normal file
116
managed/CMakeLists.txt
Normal file
@@ -0,0 +1,116 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(XCEngineManaged NONE)
|
||||
|
||||
if(NOT XCENGINE_ENABLE_MONO_SCRIPTING)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
message(FATAL_ERROR "XCENGINE_ENABLE_MONO_SCRIPTING currently requires Windows.")
|
||||
endif()
|
||||
|
||||
find_program(XCENGINE_DOTNET_EXECUTABLE dotnet REQUIRED)
|
||||
|
||||
set(
|
||||
XCENGINE_MANAGED_OUTPUT_DIR
|
||||
"${CMAKE_BINARY_DIR}/managed"
|
||||
CACHE PATH
|
||||
"Output directory for generated managed assemblies")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${XCENGINE_DOTNET_EXECUTABLE} --list-sdks
|
||||
OUTPUT_VARIABLE XCENGINE_DOTNET_SDK_LIST
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
string(REGEX MATCHALL "[0-9]+\\.[0-9]+\\.[0-9]+" XCENGINE_DOTNET_SDK_VERSIONS "${XCENGINE_DOTNET_SDK_LIST}")
|
||||
if(NOT XCENGINE_DOTNET_SDK_VERSIONS)
|
||||
message(FATAL_ERROR "Failed to locate a .NET SDK for managed scripting assemblies.")
|
||||
endif()
|
||||
|
||||
list(GET XCENGINE_DOTNET_SDK_VERSIONS -1 XCENGINE_DOTNET_SDK_VERSION)
|
||||
|
||||
set(XCENGINE_CSC_DLL "C:/Program Files/dotnet/sdk/${XCENGINE_DOTNET_SDK_VERSION}/Roslyn/bincore/csc.dll")
|
||||
set(XCENGINE_NET472_REFERENCE_DIR "C:/Program Files (x86)/Reference Assemblies/Microsoft/Framework/.NETFramework/v4.7.2")
|
||||
set(XCENGINE_MONO_CORLIB_DIR "${XCENGINE_MONO_ROOT_DIR}/binary" CACHE PATH "Directory containing the bundled Mono corlib")
|
||||
set(XCENGINE_MONO_MSCORLIB_PATH "${XCENGINE_MONO_CORLIB_DIR}/mscorlib.dll")
|
||||
|
||||
set(XCENGINE_SCRIPT_CORE_DLL "${XCENGINE_MANAGED_OUTPUT_DIR}/XCEngine.ScriptCore.dll" CACHE FILEPATH "Generated XCEngine.ScriptCore assembly")
|
||||
set(XCENGINE_GAME_SCRIPTS_DLL "${XCENGINE_MANAGED_OUTPUT_DIR}/GameScripts.dll" CACHE FILEPATH "Generated GameScripts assembly")
|
||||
|
||||
foreach(XCENGINE_REQUIRED_PATH
|
||||
"${XCENGINE_CSC_DLL}"
|
||||
"${XCENGINE_NET472_REFERENCE_DIR}/mscorlib.dll"
|
||||
"${XCENGINE_NET472_REFERENCE_DIR}/System.dll"
|
||||
"${XCENGINE_NET472_REFERENCE_DIR}/System.Core.dll"
|
||||
"${XCENGINE_MONO_MSCORLIB_PATH}")
|
||||
if(NOT EXISTS "${XCENGINE_REQUIRED_PATH}")
|
||||
message(FATAL_ERROR "Required managed scripting dependency is missing: ${XCENGINE_REQUIRED_PATH}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(XCENGINE_SCRIPT_CORE_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector2.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector3.cs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Vector4.cs
|
||||
)
|
||||
|
||||
set(XCENGINE_GAME_SCRIPT_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GameScripts/LifecycleProbe.cs
|
||||
)
|
||||
|
||||
set(XCENGINE_MANAGED_FRAMEWORK_REFERENCES
|
||||
/reference:${XCENGINE_NET472_REFERENCE_DIR}/mscorlib.dll
|
||||
/reference:${XCENGINE_NET472_REFERENCE_DIR}/System.dll
|
||||
/reference:${XCENGINE_NET472_REFERENCE_DIR}/System.Core.dll
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${XCENGINE_SCRIPT_CORE_DLL}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${XCENGINE_MANAGED_OUTPUT_DIR}
|
||||
COMMAND ${XCENGINE_DOTNET_EXECUTABLE} ${XCENGINE_CSC_DLL}
|
||||
/nologo
|
||||
/target:library
|
||||
/langversion:latest
|
||||
/nostdlib+
|
||||
/out:${XCENGINE_SCRIPT_CORE_DLL}
|
||||
${XCENGINE_MANAGED_FRAMEWORK_REFERENCES}
|
||||
${XCENGINE_SCRIPT_CORE_SOURCES}
|
||||
DEPENDS ${XCENGINE_SCRIPT_CORE_SOURCES}
|
||||
VERBATIM
|
||||
COMMENT "Building XCEngine.ScriptCore.dll")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${XCENGINE_GAME_SCRIPTS_DLL}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${XCENGINE_MANAGED_OUTPUT_DIR}
|
||||
COMMAND ${XCENGINE_DOTNET_EXECUTABLE} ${XCENGINE_CSC_DLL}
|
||||
/nologo
|
||||
/target:library
|
||||
/langversion:latest
|
||||
/nostdlib+
|
||||
/out:${XCENGINE_GAME_SCRIPTS_DLL}
|
||||
${XCENGINE_MANAGED_FRAMEWORK_REFERENCES}
|
||||
/reference:${XCENGINE_SCRIPT_CORE_DLL}
|
||||
${XCENGINE_GAME_SCRIPT_SOURCES}
|
||||
DEPENDS ${XCENGINE_GAME_SCRIPT_SOURCES} ${XCENGINE_SCRIPT_CORE_DLL}
|
||||
VERBATIM
|
||||
COMMENT "Building GameScripts.dll")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${XCENGINE_MANAGED_OUTPUT_DIR}/mscorlib.dll
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${XCENGINE_MANAGED_OUTPUT_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${XCENGINE_MONO_MSCORLIB_PATH}
|
||||
${XCENGINE_MANAGED_OUTPUT_DIR}/mscorlib.dll
|
||||
DEPENDS ${XCENGINE_MONO_MSCORLIB_PATH}
|
||||
VERBATIM
|
||||
COMMENT "Copying mscorlib.dll for Mono runtime resolution")
|
||||
|
||||
add_custom_target(
|
||||
xcengine_managed_assemblies
|
||||
DEPENDS
|
||||
${XCENGINE_SCRIPT_CORE_DLL}
|
||||
${XCENGINE_GAME_SCRIPTS_DLL}
|
||||
${XCENGINE_MANAGED_OUTPUT_DIR}/mscorlib.dll
|
||||
)
|
||||
74
managed/GameScripts/LifecycleProbe.cs
Normal file
74
managed/GameScripts/LifecycleProbe.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using XCEngine;
|
||||
|
||||
namespace Gameplay
|
||||
{
|
||||
public abstract class AbstractLifecycleProbe : MonoBehaviour
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class LifecycleProbe : MonoBehaviour
|
||||
{
|
||||
public int AwakeCount;
|
||||
public int EnableCount;
|
||||
public int StartCount;
|
||||
public int FixedUpdateCount;
|
||||
public int UpdateCount;
|
||||
public int LateUpdateCount;
|
||||
public int DisableCount;
|
||||
public int DestroyCount;
|
||||
|
||||
public float Speed;
|
||||
public string Label = string.Empty;
|
||||
public bool WasAwakened;
|
||||
public Vector3 SpawnPoint;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
AwakeCount += 1;
|
||||
WasAwakened = true;
|
||||
Label = Label + "|Awake";
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
EnableCount += 1;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
StartCount += 1;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
FixedUpdateCount += 1;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
UpdateCount += 1;
|
||||
Speed += 1.0f;
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
LateUpdateCount += 1;
|
||||
SpawnPoint = new Vector3(SpawnPoint.X + 1.0f, SpawnPoint.Y, SpawnPoint.Z);
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
DisableCount += 1;
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
DestroyCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UtilityHelper
|
||||
{
|
||||
public int Value;
|
||||
}
|
||||
}
|
||||
8
managed/XCEngine.ScriptCore/MonoBehaviour.cs
Normal file
8
managed/XCEngine.ScriptCore/MonoBehaviour.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace XCEngine
|
||||
{
|
||||
public class MonoBehaviour
|
||||
{
|
||||
public ulong GameObjectUUID;
|
||||
public ulong ScriptComponentUUID;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user