Refactor editor shell host layers
This commit is contained in:
@@ -103,6 +103,8 @@
|
||||
- `MenuBar Edit` 已开始跟随 `Hierarchy / Project` 切换动作目标
|
||||
- `Rename` 这类依赖 panel 内联状态的动作,开始通过 `EventBus` 请求而不是直接耦合 panel 实现
|
||||
- `Edit` 动作解析与菜单绘制 / shortcut 分发已开始从 `MenuBar` 抽成共享 router
|
||||
- `Hierarchy / Project` 的上下文菜单与创建弹窗也开始下沉到 shared action router
|
||||
- `Project` 右键菜单目标已不再依赖 panel 内裸索引字段,而是改成 targeted popup state
|
||||
|
||||
### 5. Dock / Layout 层
|
||||
|
||||
@@ -131,6 +133,7 @@
|
||||
- `EditorLayer` 不再保存一串分散的 panel 生命周期样板代码
|
||||
- panel 的 attach / detach / render 顺序有了统一入口
|
||||
- 后续继续拆 panel 或补 panel 时,不需要再改一大片壳层代码
|
||||
- startup scene / dock attach / panel tree 组装已继续从 `EditorLayer` 收口到 `EditorWorkspace`
|
||||
|
||||
### 7. Application / ImGui Session 层
|
||||
|
||||
@@ -145,6 +148,9 @@
|
||||
- ImGui 生命周期不再继续堆在 `Application.cpp`
|
||||
- Win32 / DX12 backend API 不再散落在 `Application.cpp` 与 `main.cpp`
|
||||
- 后续继续清理 backend 初始化边界时,有稳定落点
|
||||
- Win32 window/message pump 已抽成 `Platform/Win32EditorHost.h`
|
||||
- DX12 swapchain / render target / present / resize 已抽成 `Platform/D3D12WindowRenderer.h`
|
||||
- scene title 拼装已抽成 `Core/EditorWindowTitle.h`
|
||||
|
||||
## 主要面板状态
|
||||
|
||||
|
||||
77
editor/src/Actions/HierarchyActionRouter.h
Normal file
77
editor/src/Actions/HierarchyActionRouter.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "EditorActions.h"
|
||||
#include "Commands/EntityCommands.h"
|
||||
#include "Core/EditorEvents.h"
|
||||
#include "Core/EventBus.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Actions {
|
||||
|
||||
inline void RequestEntityRename(IEditorContext& context, const ::XCEngine::Components::GameObject* gameObject) {
|
||||
if (!gameObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.GetEventBus().Publish(EntityRenameRequestedEvent{ gameObject->GetID() });
|
||||
}
|
||||
|
||||
inline void DrawHierarchyCreateActions(IEditorContext& context, ::XCEngine::Components::GameObject* parent) {
|
||||
DrawMenuAction(MakeCreateEmptyEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(context, parent, "Create Entity", "GameObject");
|
||||
});
|
||||
DrawMenuSeparator();
|
||||
DrawMenuAction(MakeCreateCameraEntityAction(), [&]() {
|
||||
Commands::CreateCameraEntity(context, parent);
|
||||
});
|
||||
DrawMenuAction(MakeCreateLightEntityAction(), [&]() {
|
||||
Commands::CreateLightEntity(context, parent);
|
||||
});
|
||||
DrawMenuSeparator();
|
||||
DrawMenuAction(MakeCreateCubeEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(context, parent, "Create Cube", "Cube");
|
||||
});
|
||||
DrawMenuAction(MakeCreateSphereEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(context, parent, "Create Sphere", "Sphere");
|
||||
});
|
||||
DrawMenuAction(MakeCreatePlaneEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(context, parent, "Create Plane", "Plane");
|
||||
});
|
||||
}
|
||||
|
||||
inline void DrawHierarchyContextActions(IEditorContext& context, ::XCEngine::Components::GameObject* gameObject) {
|
||||
if (UI::DrawMenuScope("Create", [&]() {
|
||||
DrawHierarchyCreateActions(context, gameObject);
|
||||
})) {
|
||||
}
|
||||
|
||||
DrawMenuAction(MakeCreateChildEntityAction(gameObject), [&]() {
|
||||
Commands::CreateEmptyEntity(context, gameObject, "Create Child", "GameObject");
|
||||
});
|
||||
DrawMenuSeparator();
|
||||
DrawMenuAction(MakeDetachEntityAction(gameObject), [&]() {
|
||||
Commands::DetachEntity(context, gameObject);
|
||||
});
|
||||
DrawMenuAction(MakeRenameEntityAction(gameObject), [&]() {
|
||||
RequestEntityRename(context, gameObject);
|
||||
});
|
||||
DrawMenuAction(MakeDeleteEntityAction(gameObject), [&]() {
|
||||
Commands::DeleteEntity(context, gameObject->GetID());
|
||||
});
|
||||
DrawMenuSeparator();
|
||||
DrawMenuAction(MakeCopyEntityAction(gameObject), [&]() {
|
||||
Commands::CopyEntity(context, gameObject->GetID());
|
||||
});
|
||||
DrawMenuAction(MakePasteEntityAction(context), [&]() {
|
||||
Commands::PasteEntity(context, gameObject->GetID());
|
||||
});
|
||||
DrawMenuAction(MakeDuplicateEntityAction(gameObject), [&]() {
|
||||
Commands::DuplicateEntity(context, gameObject->GetID());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
83
editor/src/Actions/ProjectActionRouter.h
Normal file
83
editor/src/Actions/ProjectActionRouter.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "EditorActions.h"
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/IProjectManager.h"
|
||||
#include "UI/PopupState.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Actions {
|
||||
|
||||
inline int FindProjectItemIndex(IProjectManager& projectManager, const AssetItemPtr& item) {
|
||||
if (!item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& items = projectManager.GetCurrentItems();
|
||||
for (size_t i = 0; i < items.size(); ++i) {
|
||||
if (items[i] == item) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
if (items[i] && items[i]->fullPath == item->fullPath) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline void DrawProjectAssetContextActions(IEditorContext& context, const AssetItemPtr& item) {
|
||||
auto& projectManager = context.GetProjectManager();
|
||||
const int itemIndex = FindProjectItemIndex(projectManager, item);
|
||||
|
||||
DrawMenuAction(MakeOpenAssetAction(Commands::CanOpenAsset(item)), [&]() {
|
||||
Commands::OpenAsset(context, item);
|
||||
});
|
||||
DrawMenuSeparator();
|
||||
DrawMenuAction(MakeDeleteAssetAction(itemIndex >= 0), [&]() {
|
||||
Commands::DeleteAsset(projectManager, itemIndex);
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t BufferCapacity>
|
||||
inline void DrawProjectEmptyContextActions(UI::TextInputPopupState<BufferCapacity>& createFolderDialog) {
|
||||
DrawMenuAction(MakeCreateFolderAction(), [&]() {
|
||||
createFolderDialog.RequestOpen("NewFolder");
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t BufferCapacity>
|
||||
inline void DrawProjectCreateFolderDialog(IEditorContext& context, UI::TextInputPopupState<BufferCapacity>& createFolderDialog) {
|
||||
createFolderDialog.ConsumeOpenRequest("Create Folder");
|
||||
|
||||
if (!UI::BeginModalPopup("Create Folder")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::InputText("Name", createFolderDialog.Buffer(), createFolderDialog.BufferSize());
|
||||
ImGui::Separator();
|
||||
|
||||
switch (UI::DrawDialogActionRow("Create", "Cancel", !createFolderDialog.Empty())) {
|
||||
case UI::DialogActionResult::Primary:
|
||||
if (Commands::CreateFolder(context.GetProjectManager(), createFolderDialog.Buffer())) {
|
||||
createFolderDialog.Clear();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
break;
|
||||
case UI::DialogActionResult::Secondary:
|
||||
createFolderDialog.Clear();
|
||||
ImGui::CloseCurrentPopup();
|
||||
break;
|
||||
case UI::DialogActionResult::None:
|
||||
break;
|
||||
}
|
||||
|
||||
UI::EndPopup();
|
||||
}
|
||||
|
||||
} // namespace Actions
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -1,65 +1,20 @@
|
||||
#include "Application.h"
|
||||
#include "Core/EditorWindowTitle.h"
|
||||
#include "Layers/EditorLayer.h"
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Core/EditorConsoleSink.h"
|
||||
#include "Core/EditorEvents.h"
|
||||
#include "Core/EventBus.h"
|
||||
#include "Platform/Win32Utf8.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Debug/FileLogSink.h>
|
||||
#include <XCEngine/Debug/ConsoleLogSink.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <filesystem>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
std::string WideToUtf8(const std::wstring& value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, nullptr, 0, nullptr, nullptr);
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result(len - 1, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, &result[0], len, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring Utf8ToWide(const std::string& value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0);
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring result(len - 1, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, &result[0], len);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetExecutableDirectoryUtf8() {
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, exePath, MAX_PATH);
|
||||
|
||||
std::wstring exeDirW(exePath);
|
||||
const size_t pos = exeDirW.find_last_of(L"\\/");
|
||||
if (pos != std::wstring::npos) {
|
||||
exeDirW = exeDirW.substr(0, pos);
|
||||
}
|
||||
|
||||
return WideToUtf8(exeDirW);
|
||||
}
|
||||
|
||||
std::string GetExecutableLogPath(const char* fileName) {
|
||||
return GetExecutableDirectoryUtf8() + "\\" + fileName;
|
||||
return XCEngine::Editor::Platform::GetExecutableDirectoryUtf8() + "\\" + fileName;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -92,24 +47,17 @@ Application& Application::Get() {
|
||||
}
|
||||
|
||||
bool Application::Initialize(HWND hwnd) {
|
||||
// Set global exception filter first to catch any crashes
|
||||
SetUnhandledExceptionFilter(GlobalExceptionFilter);
|
||||
|
||||
// Redirect stderr to log file to capture ImGui errors
|
||||
|
||||
{
|
||||
const std::string stderrPath = GetExecutableLogPath("stderr.log");
|
||||
freopen(stderrPath.c_str(), "w", stderr);
|
||||
|
||||
fprintf(stderr, "[TEST] stderr redirection test - this should appear in stderr.log\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// Initialize logging first
|
||||
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::ConsoleLogSink>());
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::EditorConsoleSink>());
|
||||
|
||||
// Get exe directory for log file path
|
||||
const std::string exeDir = GetExecutableDirectoryUtf8();
|
||||
|
||||
const std::string exeDir = Platform::GetExecutableDirectoryUtf8();
|
||||
std::string logPath = exeDir + "\\editor.log";
|
||||
Debug::Logger::Get().AddSink(std::make_unique<Debug::FileLogSink>(logPath.c_str()));
|
||||
Debug::Logger::Get().Info(Debug::LogCategory::General, "Editor Application starting...");
|
||||
@@ -117,14 +65,10 @@ bool Application::Initialize(HWND hwnd) {
|
||||
|
||||
m_hwnd = hwnd;
|
||||
|
||||
if (!CreateDevice()) {
|
||||
if (!m_windowRenderer.Initialize(hwnd, 1280, 720)) {
|
||||
MessageBoxW(hwnd, L"Failed to create D3D12 device", L"Error", MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
if (!CreateRenderTarget()) {
|
||||
MessageBoxW(hwnd, L"Failed to create render target", L"Error", MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_editorContext = std::make_shared<EditorContext>();
|
||||
m_editorContext->SetProjectPath(exeDir);
|
||||
@@ -136,7 +80,7 @@ bool Application::Initialize(HWND hwnd) {
|
||||
});
|
||||
m_imguiSession.Initialize(m_editorContext->GetProjectPath());
|
||||
|
||||
m_imguiBackend.Initialize(hwnd, m_device, m_srvHeap);
|
||||
m_imguiBackend.Initialize(hwnd, m_windowRenderer.GetDevice(), m_windowRenderer.GetSrvHeap());
|
||||
|
||||
m_editorLayer = new EditorLayer();
|
||||
m_editorLayer->SetContext(m_editorContext);
|
||||
@@ -156,17 +100,7 @@ void Application::Shutdown() {
|
||||
|
||||
m_imguiBackend.Shutdown();
|
||||
m_imguiSession.Shutdown();
|
||||
|
||||
CleanupRenderTarget();
|
||||
|
||||
if (m_fence) m_fence->Release();
|
||||
if (m_commandList) m_commandList->Release();
|
||||
if (m_commandAllocator) m_commandAllocator->Release();
|
||||
if (m_commandQueue) m_commandQueue->Release();
|
||||
if (m_rtvHeap) m_rtvHeap->Release();
|
||||
if (m_srvHeap) m_srvHeap->Release();
|
||||
if (m_swapChain) m_swapChain->Release();
|
||||
if (m_device) m_device->Release();
|
||||
m_windowRenderer.Shutdown();
|
||||
}
|
||||
|
||||
void Application::Render() {
|
||||
@@ -176,46 +110,9 @@ void Application::Render() {
|
||||
UpdateWindowTitle();
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
m_commandAllocator->Reset();
|
||||
m_commandList->Reset(m_commandAllocator, nullptr);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = m_renderTargets[m_frameIndex];
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
|
||||
float clearColor[4] = { 0.22f, 0.22f, 0.22f, 1.0f };
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
rtvHandle.ptr += m_frameIndex * m_rtvDescriptorSize;
|
||||
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
|
||||
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
|
||||
|
||||
ID3D12DescriptorHeap* heaps[] = { m_srvHeap };
|
||||
m_commandList->SetDescriptorHeaps(1, heaps);
|
||||
|
||||
m_imguiBackend.RenderDrawData(m_commandList);
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
m_commandList->Close();
|
||||
ID3D12CommandList* cmdLists[] = { m_commandList };
|
||||
m_commandQueue->ExecuteCommandLists(1, cmdLists);
|
||||
|
||||
m_swapChain->Present(1, 0);
|
||||
|
||||
m_fenceValue++;
|
||||
m_commandQueue->Signal(m_fence, m_fenceValue);
|
||||
if (m_fence->GetCompletedValue() < m_fenceValue) {
|
||||
m_fence->SetEventOnCompletion(m_fenceValue, nullptr);
|
||||
}
|
||||
m_windowRenderer.Render(m_imguiBackend, clearColor);
|
||||
}
|
||||
|
||||
void Application::UpdateWindowTitle() {
|
||||
@@ -223,23 +120,7 @@ void Application::UpdateWindowTitle() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sceneManager = m_editorContext->GetSceneManager();
|
||||
std::string sceneName = sceneManager.HasActiveScene() ? sceneManager.GetCurrentSceneName() : "No Scene";
|
||||
if (sceneName.empty()) {
|
||||
sceneName = "Untitled Scene";
|
||||
}
|
||||
|
||||
if (sceneManager.IsSceneDirty()) {
|
||||
sceneName += " *";
|
||||
}
|
||||
if (sceneManager.GetCurrentScenePath().empty()) {
|
||||
sceneName += " (Unsaved)";
|
||||
} else {
|
||||
sceneName += " - ";
|
||||
sceneName += std::filesystem::path(sceneManager.GetCurrentScenePath()).filename().string();
|
||||
}
|
||||
|
||||
const std::wstring title = Utf8ToWide(sceneName + " - XCEngine Editor");
|
||||
const std::wstring title = Platform::Utf8ToWide(BuildEditorWindowTitle(*m_editorContext));
|
||||
if (title != m_lastWindowTitle) {
|
||||
SetWindowTextW(m_hwnd, title.c_str());
|
||||
m_lastWindowTitle = title;
|
||||
@@ -247,107 +128,7 @@ void Application::UpdateWindowTitle() {
|
||||
}
|
||||
|
||||
void Application::OnResize(int width, int height) {
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
CleanupRenderTarget();
|
||||
|
||||
if (m_swapChain) {
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
m_swapChain->GetDesc(&desc);
|
||||
m_swapChain->ResizeBuffers(3, width, height, desc.BufferDesc.Format, desc.Flags);
|
||||
}
|
||||
|
||||
CreateRenderTarget();
|
||||
}
|
||||
|
||||
bool Application::CreateDevice() {
|
||||
HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Priority = 0;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.NodeMask = 0;
|
||||
hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator, nullptr, IID_PPV_ARGS(&m_commandList));
|
||||
if (FAILED(hr)) return false;
|
||||
m_commandList->Close();
|
||||
|
||||
hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
IDXGIFactory4* factory = nullptr;
|
||||
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
||||
swapChainDesc.BufferCount = 3;
|
||||
swapChainDesc.Width = m_width;
|
||||
swapChainDesc.Height = m_height;
|
||||
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
|
||||
IDXGISwapChain1* swapChain1 = nullptr;
|
||||
hr = factory->CreateSwapChainForHwnd(m_commandQueue, m_hwnd, &swapChainDesc, nullptr, nullptr, &swapChain1);
|
||||
factory->Release();
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = swapChain1->QueryInterface(IID_PPV_ARGS(&m_swapChain));
|
||||
swapChain1->Release();
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC rtvDesc = {};
|
||||
rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||
rtvDesc.NumDescriptors = 3;
|
||||
rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
hr = m_device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&m_rtvHeap));
|
||||
if (FAILED(hr)) return false;
|
||||
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC srvDesc = {};
|
||||
srvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||
srvDesc.NumDescriptors = 1;
|
||||
srvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
hr = m_device->CreateDescriptorHeap(&srvDesc, IID_PPV_ARGS(&m_srvHeap));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::CreateRenderTarget() {
|
||||
if (!m_swapChain || !m_device || !m_rtvHeap) return false;
|
||||
|
||||
for (UINT i = 0; i < 3; i++) {
|
||||
HRESULT hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTargets[i]));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
rtvHandle.ptr += i * m_rtvDescriptorSize;
|
||||
m_device->CreateRenderTargetView(m_renderTargets[i], nullptr, rtvHandle);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::CleanupRenderTarget() {
|
||||
for (UINT i = 0; i < 3; i++) {
|
||||
if (m_renderTargets[i]) {
|
||||
m_renderTargets[i]->Release();
|
||||
m_renderTargets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
m_windowRenderer.Resize(width, height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Platform/D3D12WindowRenderer.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
#include "UI/ImGuiSession.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <XCEngine/Core/LayerStack.h>
|
||||
|
||||
@@ -32,32 +31,14 @@ public:
|
||||
private:
|
||||
Application() = default;
|
||||
~Application() = default;
|
||||
|
||||
bool CreateDevice();
|
||||
bool CreateRenderTarget();
|
||||
void CleanupRenderTarget();
|
||||
void UpdateWindowTitle();
|
||||
|
||||
HWND m_hwnd = nullptr;
|
||||
int m_width = 1280;
|
||||
int m_height = 720;
|
||||
|
||||
ID3D12Device* m_device = nullptr;
|
||||
ID3D12CommandQueue* m_commandQueue = nullptr;
|
||||
ID3D12CommandAllocator* m_commandAllocator = nullptr;
|
||||
ID3D12GraphicsCommandList* m_commandList = nullptr;
|
||||
IDXGISwapChain3* m_swapChain = nullptr;
|
||||
ID3D12DescriptorHeap* m_rtvHeap = nullptr;
|
||||
ID3D12DescriptorHeap* m_srvHeap = nullptr;
|
||||
ID3D12Resource* m_renderTargets[3] = {};
|
||||
ID3D12Fence* m_fence = nullptr;
|
||||
UINT64 m_fenceValue = 0;
|
||||
UINT m_rtvDescriptorSize = 0;
|
||||
UINT m_frameIndex = 0;
|
||||
|
||||
Core::LayerStack m_layerStack;
|
||||
EditorLayer* m_editorLayer = nullptr;
|
||||
std::shared_ptr<IEditorContext> m_editorContext;
|
||||
Platform::D3D12WindowRenderer m_windowRenderer;
|
||||
UI::ImGuiBackendBridge m_imguiBackend;
|
||||
UI::ImGuiSession m_imguiSession;
|
||||
uint64_t m_exitRequestedHandlerId = 0;
|
||||
|
||||
33
editor/src/Core/EditorWindowTitle.h
Normal file
33
editor/src/Core/EditorWindowTitle.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
inline std::string BuildEditorWindowTitle(IEditorContext& context) {
|
||||
auto& sceneManager = context.GetSceneManager();
|
||||
std::string sceneName = sceneManager.HasActiveScene() ? sceneManager.GetCurrentSceneName() : "No Scene";
|
||||
if (sceneName.empty()) {
|
||||
sceneName = "Untitled Scene";
|
||||
}
|
||||
|
||||
if (sceneManager.IsSceneDirty()) {
|
||||
sceneName += " *";
|
||||
}
|
||||
if (sceneManager.GetCurrentScenePath().empty()) {
|
||||
sceneName += " (Unsaved)";
|
||||
} else {
|
||||
sceneName += " - ";
|
||||
sceneName += std::filesystem::path(sceneManager.GetCurrentScenePath()).filename().string();
|
||||
}
|
||||
|
||||
return sceneName + " - XCEngine Editor";
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
82
editor/src/Core/EditorWorkspace.h
Normal file
82
editor/src/Core/EditorWorkspace.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include "Commands/SceneCommands.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Layout/DockLayoutController.h"
|
||||
#include "panels/ConsolePanel.h"
|
||||
#include "panels/GameViewPanel.h"
|
||||
#include "panels/HierarchyPanel.h"
|
||||
#include "panels/InspectorPanel.h"
|
||||
#include "panels/MenuBar.h"
|
||||
#include "panels/PanelCollection.h"
|
||||
#include "panels/ProjectPanel.h"
|
||||
#include "panels/SceneViewPanel.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class EditorWorkspace {
|
||||
public:
|
||||
void Attach(IEditorContext& context) {
|
||||
m_panels.Clear();
|
||||
m_panels.SetContext(&context);
|
||||
m_panels.Emplace<MenuBar>();
|
||||
m_panels.Emplace<HierarchyPanel>();
|
||||
m_panels.Emplace<SceneViewPanel>();
|
||||
m_panels.Emplace<GameViewPanel>();
|
||||
m_panels.Emplace<InspectorPanel>();
|
||||
m_panels.Emplace<ConsolePanel>();
|
||||
m_projectPanel = &m_panels.Emplace<ProjectPanel>();
|
||||
m_dockLayoutController = std::make_unique<DockLayoutController>();
|
||||
|
||||
m_projectPanel->Initialize(context.GetProjectPath());
|
||||
Commands::LoadStartupScene(context);
|
||||
m_dockLayoutController->Attach(context);
|
||||
m_panels.AttachAll();
|
||||
}
|
||||
|
||||
void Detach(IEditorContext& context) {
|
||||
Commands::SaveDirtySceneWithFallback(context, BuildFallbackScenePath(context));
|
||||
|
||||
if (m_dockLayoutController) {
|
||||
m_dockLayoutController->Detach();
|
||||
m_dockLayoutController.reset();
|
||||
}
|
||||
|
||||
m_panels.DetachAll();
|
||||
m_panels.Clear();
|
||||
m_projectPanel = nullptr;
|
||||
}
|
||||
|
||||
void Update(float dt) {
|
||||
m_panels.UpdateAll(dt);
|
||||
}
|
||||
|
||||
void DispatchEvent(void* event) {
|
||||
m_panels.DispatchEvent(event);
|
||||
}
|
||||
|
||||
void Render() {
|
||||
if (m_dockLayoutController) {
|
||||
m_dockLayoutController->RenderDockspace();
|
||||
}
|
||||
|
||||
m_panels.RenderAll();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string BuildFallbackScenePath(const IEditorContext& context) {
|
||||
return (std::filesystem::path(context.GetProjectPath()) / "Assets" / "Scenes" / "Main.xc").string();
|
||||
}
|
||||
|
||||
PanelCollection m_panels;
|
||||
ProjectPanel* m_projectPanel = nullptr;
|
||||
std::unique_ptr<DockLayoutController> m_dockLayoutController;
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -1,30 +1,9 @@
|
||||
#include "Commands/SceneCommands.h"
|
||||
#include "EditorLayer.h"
|
||||
#include "Layout/DockLayoutController.h"
|
||||
#include "panels/MenuBar.h"
|
||||
#include "panels/HierarchyPanel.h"
|
||||
#include "panels/SceneViewPanel.h"
|
||||
#include "panels/GameViewPanel.h"
|
||||
#include "panels/InspectorPanel.h"
|
||||
#include "panels/ConsolePanel.h"
|
||||
#include "panels/ProjectPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/EditorContext.h"
|
||||
#include "Core/IUndoManager.h"
|
||||
#include <filesystem>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string BuildFallbackScenePath(const IEditorContext& context) {
|
||||
return (std::filesystem::path(context.GetProjectPath()) / "Assets" / "Scenes" / "Main.xc").string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EditorLayer::EditorLayer() : Layer("Editor") {}
|
||||
|
||||
void EditorLayer::SetContext(std::shared_ptr<IEditorContext> context) {
|
||||
@@ -36,48 +15,25 @@ void EditorLayer::onAttach() {
|
||||
m_context = std::make_shared<EditorContext>();
|
||||
}
|
||||
|
||||
m_panels.Clear();
|
||||
m_panels.SetContext(m_context.get());
|
||||
m_panels.Emplace<MenuBar>();
|
||||
m_panels.Emplace<HierarchyPanel>();
|
||||
m_panels.Emplace<SceneViewPanel>();
|
||||
m_panels.Emplace<GameViewPanel>();
|
||||
m_panels.Emplace<InspectorPanel>();
|
||||
m_panels.Emplace<ConsolePanel>();
|
||||
m_projectPanel = &m_panels.Emplace<ProjectPanel>();
|
||||
m_dockLayoutController = std::make_unique<DockLayoutController>();
|
||||
|
||||
m_projectPanel->Initialize(m_context->GetProjectPath());
|
||||
Commands::LoadStartupScene(*m_context);
|
||||
m_dockLayoutController->Attach(*m_context);
|
||||
m_panels.AttachAll();
|
||||
m_workspace.Attach(*m_context);
|
||||
}
|
||||
|
||||
void EditorLayer::onDetach() {
|
||||
if (m_context) {
|
||||
Commands::SaveDirtySceneWithFallback(*m_context, BuildFallbackScenePath(*m_context));
|
||||
m_workspace.Detach(*m_context);
|
||||
}
|
||||
|
||||
if (m_dockLayoutController) {
|
||||
m_dockLayoutController->Detach();
|
||||
}
|
||||
|
||||
m_panels.DetachAll();
|
||||
m_panels.Clear();
|
||||
m_projectPanel = nullptr;
|
||||
}
|
||||
|
||||
void EditorLayer::onUpdate(float dt) {
|
||||
m_panels.UpdateAll(dt);
|
||||
m_workspace.Update(dt);
|
||||
}
|
||||
|
||||
void EditorLayer::onEvent(void* event) {
|
||||
m_panels.DispatchEvent(event);
|
||||
m_workspace.DispatchEvent(event);
|
||||
}
|
||||
|
||||
void EditorLayer::onImGuiRender() {
|
||||
m_dockLayoutController->RenderDockspace();
|
||||
m_panels.RenderAll();
|
||||
m_workspace.Render();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "panels/PanelCollection.h"
|
||||
#include "Core/EditorWorkspace.h"
|
||||
|
||||
#include <XCEngine/Core/Layer.h>
|
||||
#include <XCEngine/Core/LayerStack.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class IEditorContext;
|
||||
class MenuBar;
|
||||
class HierarchyPanel;
|
||||
class SceneViewPanel;
|
||||
class GameViewPanel;
|
||||
class InspectorPanel;
|
||||
class ConsolePanel;
|
||||
class ProjectPanel;
|
||||
class DockLayoutController;
|
||||
|
||||
class EditorLayer : public Core::Layer {
|
||||
public:
|
||||
@@ -35,9 +26,7 @@ public:
|
||||
|
||||
private:
|
||||
std::shared_ptr<IEditorContext> m_context;
|
||||
std::unique_ptr<DockLayoutController> m_dockLayoutController;
|
||||
PanelCollection m_panels;
|
||||
ProjectPanel* m_projectPanel = nullptr;
|
||||
EditorWorkspace m_workspace;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
218
editor/src/Platform/D3D12WindowRenderer.h
Normal file
218
editor/src/Platform/D3D12WindowRenderer.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Platform {
|
||||
|
||||
class D3D12WindowRenderer {
|
||||
public:
|
||||
bool Initialize(HWND hwnd, int width, int height) {
|
||||
m_hwnd = hwnd;
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
return CreateDevice() && CreateRenderTarget();
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
CleanupRenderTarget();
|
||||
|
||||
if (m_fence) m_fence->Release();
|
||||
if (m_commandList) m_commandList->Release();
|
||||
if (m_commandAllocator) m_commandAllocator->Release();
|
||||
if (m_commandQueue) m_commandQueue->Release();
|
||||
if (m_rtvHeap) m_rtvHeap->Release();
|
||||
if (m_srvHeap) m_srvHeap->Release();
|
||||
if (m_swapChain) m_swapChain->Release();
|
||||
if (m_device) m_device->Release();
|
||||
|
||||
m_hwnd = nullptr;
|
||||
m_width = 1280;
|
||||
m_height = 720;
|
||||
m_fenceValue = 0;
|
||||
m_rtvDescriptorSize = 0;
|
||||
m_frameIndex = 0;
|
||||
}
|
||||
|
||||
void Resize(int width, int height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
CleanupRenderTarget();
|
||||
|
||||
if (m_swapChain) {
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
m_swapChain->GetDesc(&desc);
|
||||
m_swapChain->ResizeBuffers(3, width, height, desc.BufferDesc.Format, desc.Flags);
|
||||
}
|
||||
|
||||
CreateRenderTarget();
|
||||
}
|
||||
|
||||
void Render(UI::ImGuiBackendBridge& imguiBackend, const float clearColor[4]) {
|
||||
m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
|
||||
m_commandAllocator->Reset();
|
||||
m_commandList->Reset(m_commandAllocator, nullptr);
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = m_renderTargets[m_frameIndex];
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
rtvHandle.ptr += m_frameIndex * m_rtvDescriptorSize;
|
||||
|
||||
m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
|
||||
m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
|
||||
|
||||
ID3D12DescriptorHeap* heaps[] = { m_srvHeap };
|
||||
m_commandList->SetDescriptorHeaps(1, heaps);
|
||||
imguiBackend.RenderDrawData(m_commandList);
|
||||
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
|
||||
m_commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
m_commandList->Close();
|
||||
ID3D12CommandList* commandLists[] = { m_commandList };
|
||||
m_commandQueue->ExecuteCommandLists(1, commandLists);
|
||||
|
||||
m_swapChain->Present(1, 0);
|
||||
|
||||
++m_fenceValue;
|
||||
m_commandQueue->Signal(m_fence, m_fenceValue);
|
||||
if (m_fence->GetCompletedValue() < m_fenceValue) {
|
||||
m_fence->SetEventOnCompletion(m_fenceValue, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ID3D12Device* GetDevice() const {
|
||||
return m_device;
|
||||
}
|
||||
|
||||
ID3D12DescriptorHeap* GetSrvHeap() const {
|
||||
return m_srvHeap;
|
||||
}
|
||||
|
||||
private:
|
||||
bool CreateDevice() {
|
||||
HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Priority = 0;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.NodeMask = 0;
|
||||
hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator, nullptr, IID_PPV_ARGS(&m_commandList));
|
||||
if (FAILED(hr)) return false;
|
||||
m_commandList->Close();
|
||||
|
||||
hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
IDXGIFactory4* factory = nullptr;
|
||||
hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
|
||||
swapChainDesc.BufferCount = 3;
|
||||
swapChainDesc.Width = m_width;
|
||||
swapChainDesc.Height = m_height;
|
||||
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swapChainDesc.SampleDesc.Count = 1;
|
||||
|
||||
IDXGISwapChain1* swapChain1 = nullptr;
|
||||
hr = factory->CreateSwapChainForHwnd(m_commandQueue, m_hwnd, &swapChainDesc, nullptr, nullptr, &swapChain1);
|
||||
factory->Release();
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
hr = swapChain1->QueryInterface(IID_PPV_ARGS(&m_swapChain));
|
||||
swapChain1->Release();
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC rtvDesc = {};
|
||||
rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
|
||||
rtvDesc.NumDescriptors = 3;
|
||||
rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
|
||||
hr = m_device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&m_rtvHeap));
|
||||
if (FAILED(hr)) return false;
|
||||
m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC srvDesc = {};
|
||||
srvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
|
||||
srvDesc.NumDescriptors = 1;
|
||||
srvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
hr = m_device->CreateDescriptorHeap(&srvDesc, IID_PPV_ARGS(&m_srvHeap));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateRenderTarget() {
|
||||
if (!m_swapChain || !m_device || !m_rtvHeap) return false;
|
||||
|
||||
for (UINT i = 0; i < 3; ++i) {
|
||||
HRESULT hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTargets[i]));
|
||||
if (FAILED(hr)) return false;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
rtvHandle.ptr += i * m_rtvDescriptorSize;
|
||||
m_device->CreateRenderTargetView(m_renderTargets[i], nullptr, rtvHandle);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CleanupRenderTarget() {
|
||||
for (UINT i = 0; i < 3; ++i) {
|
||||
if (m_renderTargets[i]) {
|
||||
m_renderTargets[i]->Release();
|
||||
m_renderTargets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HWND m_hwnd = nullptr;
|
||||
int m_width = 1280;
|
||||
int m_height = 720;
|
||||
|
||||
ID3D12Device* m_device = nullptr;
|
||||
ID3D12CommandQueue* m_commandQueue = nullptr;
|
||||
ID3D12CommandAllocator* m_commandAllocator = nullptr;
|
||||
ID3D12GraphicsCommandList* m_commandList = nullptr;
|
||||
IDXGISwapChain3* m_swapChain = nullptr;
|
||||
ID3D12DescriptorHeap* m_rtvHeap = nullptr;
|
||||
ID3D12DescriptorHeap* m_srvHeap = nullptr;
|
||||
ID3D12Resource* m_renderTargets[3] = {};
|
||||
ID3D12Fence* m_fence = nullptr;
|
||||
UINT64 m_fenceValue = 0;
|
||||
UINT m_rtvDescriptorSize = 0;
|
||||
UINT m_frameIndex = 0;
|
||||
};
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
94
editor/src/Platform/Win32EditorHost.h
Normal file
94
editor/src/Platform/Win32EditorHost.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "Application.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Platform {
|
||||
|
||||
inline LRESULT WINAPI EditorWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
if (UI::ImGuiBackendBridge::HandleWindowMessage(hWnd, msg, wParam, lParam)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (msg) {
|
||||
case WM_SIZE:
|
||||
if (wParam != SIZE_MINIMIZED) {
|
||||
Application::Get().OnResize(static_cast<int>(LOWORD(lParam)), static_cast<int>(HIWORD(lParam)));
|
||||
}
|
||||
return 0;
|
||||
case WM_SYSCOMMAND:
|
||||
if ((wParam & 0xfff0) == SC_KEYMENU) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
inline int RunEditor(HINSTANCE hInstance, int nCmdShow) {
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_CLASSDC;
|
||||
wc.lpfnWndProc = EditorWndProc;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = L"XCVolumeRendererUI2";
|
||||
|
||||
if (!RegisterClassExW(&wc)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
HWND hwnd = CreateWindowExW(
|
||||
0,
|
||||
wc.lpszClassName,
|
||||
L"XCEngine Editor",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
100,
|
||||
100,
|
||||
1280,
|
||||
720,
|
||||
nullptr,
|
||||
nullptr,
|
||||
hInstance,
|
||||
nullptr);
|
||||
|
||||
if (!hwnd) {
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ShowWindow(hwnd, nCmdShow);
|
||||
UpdateWindow(hwnd);
|
||||
|
||||
if (!Application::Get().Initialize(hwnd)) {
|
||||
DestroyWindow(hwnd);
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
MSG msg = {};
|
||||
while (msg.message != WM_QUIT) {
|
||||
if (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
continue;
|
||||
}
|
||||
|
||||
Application::Get().Render();
|
||||
}
|
||||
|
||||
Application::Get().Shutdown();
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
return static_cast<int>(msg.wParam);
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
55
editor/src/Platform/Win32Utf8.h
Normal file
55
editor/src/Platform/Win32Utf8.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace Platform {
|
||||
|
||||
inline std::string WideToUtf8(const std::wstring& value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int len = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, nullptr, 0, nullptr, nullptr);
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string result(len - 1, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, value.c_str(), -1, &result[0], len, nullptr, nullptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::wstring Utf8ToWide(const std::string& value) {
|
||||
if (value.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const int len = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, nullptr, 0);
|
||||
if (len <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::wstring result(len - 1, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, value.c_str(), -1, &result[0], len);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string GetExecutableDirectoryUtf8() {
|
||||
wchar_t exePath[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, exePath, MAX_PATH);
|
||||
|
||||
std::wstring exeDir(exePath);
|
||||
const size_t pos = exeDir.find_last_of(L"\\/");
|
||||
if (pos != std::wstring::npos) {
|
||||
exeDir = exeDir.substr(0, pos);
|
||||
}
|
||||
|
||||
return WideToUtf8(exeDir);
|
||||
}
|
||||
|
||||
} // namespace Platform
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
@@ -35,6 +36,47 @@ private:
|
||||
bool m_openRequested = false;
|
||||
};
|
||||
|
||||
template <typename Target>
|
||||
class TargetedPopupState {
|
||||
public:
|
||||
void RequestOpen(Target target) {
|
||||
m_target = std::move(target);
|
||||
m_hasTarget = true;
|
||||
m_popup.RequestOpen();
|
||||
}
|
||||
|
||||
void ConsumeOpenRequest(const char* popupId) {
|
||||
m_popup.ConsumeOpenRequest(popupId);
|
||||
}
|
||||
|
||||
bool HasPendingOpenRequest() const {
|
||||
return m_popup.HasPendingOpenRequest();
|
||||
}
|
||||
|
||||
bool HasTarget() const {
|
||||
return m_hasTarget;
|
||||
}
|
||||
|
||||
Target& TargetValue() {
|
||||
return m_target;
|
||||
}
|
||||
|
||||
const Target& TargetValue() const {
|
||||
return m_target;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
m_popup.Clear();
|
||||
m_target = Target{};
|
||||
m_hasTarget = false;
|
||||
}
|
||||
|
||||
private:
|
||||
DeferredPopupState m_popup;
|
||||
Target m_target{};
|
||||
bool m_hasTarget = false;
|
||||
};
|
||||
|
||||
template <size_t BufferCapacity>
|
||||
class TextInputPopupState {
|
||||
public:
|
||||
|
||||
@@ -1,95 +1,5 @@
|
||||
#include "Application.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
#include <imgui.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
#include "Platform/Win32EditorHost.h"
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) {
|
||||
AllocConsole();
|
||||
freopen("CONOUT$", "w", stdout);
|
||||
printf("Starting UI application...\n");
|
||||
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_CLASSDC;
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.lpszClassName = L"XCVolumeRendererUI2";
|
||||
|
||||
if (!RegisterClassExW(&wc)) {
|
||||
printf("Failed to register window class, error: %lu\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
printf("Window class registered.\n");
|
||||
|
||||
HWND hwnd = CreateWindowExW(
|
||||
0, wc.lpszClassName, L"XCVolumeRenderer - Unity Style Editor",
|
||||
WS_OVERLAPPEDWINDOW, 100, 100, 1280, 720,
|
||||
nullptr, nullptr, wc.hInstance, nullptr
|
||||
);
|
||||
|
||||
if (!hwnd) {
|
||||
printf("Failed to create window, error: %lu\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
printf("Window created.\n");
|
||||
|
||||
ShowWindow(hwnd, nCmdShow);
|
||||
UpdateWindow(hwnd);
|
||||
printf("Window shown.\n");
|
||||
|
||||
printf("Initializing application...\n");
|
||||
if (!XCEngine::Editor::Application::Get().Initialize(hwnd)) {
|
||||
printf("Failed to initialize application!\n");
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
system("pause");
|
||||
return 1;
|
||||
}
|
||||
printf("Application initialized successfully.\n");
|
||||
|
||||
MSG msg = {};
|
||||
int frameCount = 0;
|
||||
while (msg.message != WM_QUIT) {
|
||||
if (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
} else {
|
||||
XCEngine::Editor::Application::Get().Render();
|
||||
frameCount++;
|
||||
if (frameCount % 100 == 0) {
|
||||
printf("Frame %d\n", frameCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Shutting down...\n");
|
||||
XCEngine::Editor::Application::Get().Shutdown();
|
||||
UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
||||
|
||||
printf("Press any key to exit...\n");
|
||||
system("pause");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
if (XCEngine::Editor::UI::ImGuiBackendBridge::HandleWindowMessage(hWnd, msg, wParam, lParam))
|
||||
return true;
|
||||
|
||||
switch (msg) {
|
||||
case WM_SIZE:
|
||||
if (wParam != SIZE_MINIMIZED) {
|
||||
XCEngine::Editor::Application::Get().OnResize((int)LOWORD(lParam), (int)HIWORD(lParam));
|
||||
}
|
||||
return 0;
|
||||
case WM_SYSCOMMAND:
|
||||
if ((wParam & 0xfff0) == SC_KEYMENU)
|
||||
return 0;
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcW(hWnd, msg, wParam, lParam);
|
||||
return XCEngine::Editor::Platform::RunEditor(hInstance, nCmdShow);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Actions/HierarchyActionRouter.h"
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Commands/EntityCommands.h"
|
||||
#include "HierarchyPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include "Core/ISelectionManager.h"
|
||||
#include "Core/IUndoManager.h"
|
||||
#include "Core/EditorEvents.h"
|
||||
#include "Core/EventBus.h"
|
||||
#include "UI/UI.h"
|
||||
#include <imgui.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
@@ -85,7 +83,7 @@ void HierarchyPanel::Render() {
|
||||
}
|
||||
|
||||
if (UI::BeginPopupContextWindow("HierarchyContextMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
RenderCreateMenu(nullptr);
|
||||
Actions::DrawHierarchyCreateActions(*m_context, nullptr);
|
||||
UI::EndPopup();
|
||||
}
|
||||
|
||||
@@ -199,7 +197,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
HandleDragDrop(gameObject);
|
||||
|
||||
if (UI::BeginPopupContextItem("EntityContextMenu")) {
|
||||
RenderContextMenu(gameObject);
|
||||
Actions::DrawHierarchyContextActions(*m_context, gameObject);
|
||||
UI::EndPopup();
|
||||
}
|
||||
|
||||
@@ -214,60 +212,6 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameObject) {
|
||||
if (UI::DrawMenuScope("Create", [&]() {
|
||||
RenderCreateMenu(gameObject);
|
||||
})) {
|
||||
}
|
||||
|
||||
Actions::DrawMenuAction(Actions::MakeCreateChildEntityAction(gameObject), [&]() {
|
||||
Commands::CreateEmptyEntity(*m_context, gameObject, "Create Child", "GameObject");
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeDetachEntityAction(gameObject), [&]() {
|
||||
Commands::DetachEntity(*m_context, gameObject);
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeRenameEntityAction(gameObject), [&]() {
|
||||
BeginRename(gameObject);
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDeleteEntityAction(gameObject), [&]() {
|
||||
Commands::DeleteEntity(*m_context, gameObject->GetID());
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeCopyEntityAction(gameObject), [&]() {
|
||||
Commands::CopyEntity(*m_context, gameObject->GetID());
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakePasteEntityAction(*m_context), [&]() {
|
||||
Commands::PasteEntity(*m_context, gameObject->GetID());
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeDuplicateEntityAction(gameObject), [&]() {
|
||||
Commands::DuplicateEntity(*m_context, gameObject->GetID());
|
||||
});
|
||||
}
|
||||
|
||||
void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent) {
|
||||
Actions::DrawMenuAction(Actions::MakeCreateEmptyEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(*m_context, parent, "Create Entity", "GameObject");
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeCreateCameraEntityAction(), [&]() {
|
||||
Commands::CreateCameraEntity(*m_context, parent);
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeCreateLightEntityAction(), [&]() {
|
||||
Commands::CreateLightEntity(*m_context, parent);
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeCreateCubeEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(*m_context, parent, "Create Cube", "Cube");
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeCreateSphereEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(*m_context, parent, "Create Sphere", "Sphere");
|
||||
});
|
||||
Actions::DrawMenuAction(Actions::MakeCreatePlaneEntityAction(), [&]() {
|
||||
Commands::CreateEmptyEntity(*m_context, parent, "Create Plane", "Plane");
|
||||
});
|
||||
}
|
||||
|
||||
void HierarchyPanel::BeginRename(::XCEngine::Components::GameObject* gameObject) {
|
||||
if (!gameObject) {
|
||||
CancelRename();
|
||||
|
||||
@@ -23,8 +23,6 @@ private:
|
||||
void OnRenameRequested(const struct EntityRenameRequestedEvent& event);
|
||||
void RenderSearchBar();
|
||||
void RenderEntity(::XCEngine::Components::GameObject* gameObject, const std::string& filter);
|
||||
void RenderContextMenu(::XCEngine::Components::GameObject* gameObject);
|
||||
void RenderCreateMenu(::XCEngine::Components::GameObject* parent);
|
||||
void BeginRename(::XCEngine::Components::GameObject* gameObject);
|
||||
void CommitRename();
|
||||
void CancelRename();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "Actions/ActionRouting.h"
|
||||
#include "Actions/EditorActions.h"
|
||||
#include "Actions/ProjectActionRouter.h"
|
||||
#include "Commands/ProjectCommands.h"
|
||||
#include "ProjectPanel.h"
|
||||
#include "Core/IEditorContext.h"
|
||||
@@ -7,12 +7,13 @@
|
||||
#include "Core/AssetItem.h"
|
||||
#include "UI/UI.h"
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
const char* DRAG_DROP_TYPE = "ASSET_ITEM";
|
||||
namespace {
|
||||
constexpr const char* kAssetDragDropType = "ASSET_ITEM";
|
||||
}
|
||||
|
||||
ProjectPanel::ProjectPanel() : Panel("Project") {
|
||||
}
|
||||
@@ -23,7 +24,7 @@ void ProjectPanel::Initialize(const std::string& projectPath) {
|
||||
|
||||
void ProjectPanel::Render() {
|
||||
const ImGuiPayload* payload = ImGui::GetDragDropPayload();
|
||||
if (payload && payload->IsDataType(DRAG_DROP_TYPE)) {
|
||||
if (payload && payload->IsDataType(kAssetDragDropType)) {
|
||||
m_draggingPath = (const char*)payload->Data;
|
||||
} else if (!ImGui::IsMouseDown(0)) {
|
||||
m_draggingPath.clear();
|
||||
@@ -101,52 +102,28 @@ void ProjectPanel::Render() {
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(0) && !ImGui::IsAnyItemHovered()) {
|
||||
manager.SetSelectedIndex(-1);
|
||||
}
|
||||
|
||||
|
||||
m_itemContextMenu.ConsumeOpenRequest("ItemContextMenu");
|
||||
if (UI::BeginPopup("ItemContextMenu")) {
|
||||
if (m_contextMenuIndex >= 0 && m_contextMenuIndex < (int)items.size()) {
|
||||
auto& item = items[m_contextMenuIndex];
|
||||
const bool canOpen = item->isFolder || item->type == "Scene";
|
||||
Actions::DrawMenuAction(Actions::MakeOpenAssetAction(canOpen), [&]() {
|
||||
Commands::OpenAsset(*m_context, item);
|
||||
});
|
||||
Actions::DrawMenuSeparator();
|
||||
Actions::DrawMenuAction(Actions::MakeDeleteAssetAction(), [&]() {
|
||||
Commands::DeleteAsset(manager, m_contextMenuIndex);
|
||||
m_contextMenuIndex = -1;
|
||||
});
|
||||
if (m_itemContextMenu.HasTarget()) {
|
||||
Actions::DrawProjectAssetContextActions(*m_context, m_itemContextMenu.TargetValue());
|
||||
}
|
||||
UI::EndPopup();
|
||||
}
|
||||
if (!ImGui::IsPopupOpen("ItemContextMenu") && !m_itemContextMenu.HasPendingOpenRequest()) {
|
||||
m_itemContextMenu.Clear();
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseClicked(1) && !ImGui::IsAnyItemHovered()) {
|
||||
ImGui::OpenPopup("EmptyContextMenu");
|
||||
}
|
||||
|
||||
if (UI::BeginPopup("EmptyContextMenu")) {
|
||||
Actions::DrawMenuAction(Actions::MakeCreateFolderAction(), [&]() {
|
||||
m_createFolderDialog.RequestOpen("NewFolder");
|
||||
});
|
||||
UI::EndPopup();
|
||||
}
|
||||
|
||||
m_createFolderDialog.ConsumeOpenRequest("Create Folder");
|
||||
|
||||
if (UI::BeginModalPopup("Create Folder")) {
|
||||
ImGui::InputText("Name", m_createFolderDialog.Buffer(), m_createFolderDialog.BufferSize());
|
||||
ImGui::Separator();
|
||||
const Actions::ActionBinding createAction = Actions::MakeConfirmCreateAction(!m_createFolderDialog.Empty());
|
||||
const Actions::ActionBinding cancelAction = Actions::MakeCancelAction();
|
||||
if (Actions::DrawButtonAction(createAction, UI::DialogActionButtonSize())) {
|
||||
if (Commands::CreateFolder(manager, m_createFolderDialog.Buffer())) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (Actions::DrawButtonAction(cancelAction, UI::DialogActionButtonSize())) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
Actions::DrawProjectEmptyContextActions(m_createFolderDialog);
|
||||
UI::EndPopup();
|
||||
}
|
||||
|
||||
Actions::DrawProjectCreateFolderDialog(*m_context, m_createFolderDialog);
|
||||
}
|
||||
|
||||
void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
|
||||
@@ -169,16 +146,14 @@ void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
|
||||
manager.SetSelectedIndex(index);
|
||||
}
|
||||
|
||||
bool openContextMenu = false;
|
||||
if (tile.contextRequested) {
|
||||
manager.SetSelectedIndex(index);
|
||||
m_contextMenuIndex = index;
|
||||
openContextMenu = true;
|
||||
m_itemContextMenu.RequestOpen(item);
|
||||
}
|
||||
|
||||
if (item->isFolder) {
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(DRAG_DROP_TYPE)) {
|
||||
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(kAssetDragDropType)) {
|
||||
const char* draggedPath = (const char*)payload->Data;
|
||||
Commands::MoveAssetToFolder(manager, draggedPath, item);
|
||||
}
|
||||
@@ -188,7 +163,7 @@ void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
|
||||
|
||||
if (!item->fullPath.empty()) {
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
|
||||
ImGui::SetDragDropPayload(DRAG_DROP_TYPE, item->fullPath.c_str(), item->fullPath.length() + 1);
|
||||
ImGui::SetDragDropPayload(kAssetDragDropType, item->fullPath.c_str(), item->fullPath.length() + 1);
|
||||
|
||||
ImVec2 previewMin = ImGui::GetMousePos();
|
||||
const ImVec2 previewSize = UI::AssetDragPreviewSize();
|
||||
@@ -204,10 +179,6 @@ void ProjectPanel::RenderAssetItem(const AssetItemPtr& item, int index) {
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (openContextMenu) {
|
||||
ImGui::OpenPopup("ItemContextMenu");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ private:
|
||||
|
||||
char m_searchBuffer[256] = "";
|
||||
UI::TextInputPopupState<256> m_createFolderDialog;
|
||||
int m_contextMenuIndex = -1;
|
||||
UI::TargetedPopupState<AssetItemPtr> m_itemContextMenu;
|
||||
std::string m_draggingPath;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user