Editor: UI panels and GameObject updates

This commit is contained in:
2026-03-25 01:23:08 +08:00
parent dc970d215b
commit c9e459c179
21 changed files with 651 additions and 184 deletions

View File

@@ -1,6 +1,8 @@
#include "HierarchyPanel.h"
#include "Managers/SceneManager.h"
#include "Managers/SelectionManager.h"
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Core/Math/Quaternion.h>
#include <imgui.h>
#include <cstring>
@@ -31,8 +33,8 @@ void HierarchyPanel::Render() {
ImGui::BeginChild("EntityList");
for (auto* entity : EditorSceneManager::Get().GetRootEntities()) {
RenderEntity(entity, filter);
for (auto* gameObject : EditorSceneManager::Get().GetRootEntities()) {
RenderEntity(gameObject, filter);
}
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
@@ -49,9 +51,9 @@ void HierarchyPanel::Render() {
ImGui::InvisibleButton("##DragTarget", ImVec2(-1, -1));
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
::XCEngine::Components::GameObject* sourceEntity = *(::XCEngine::Components::GameObject**)payload->Data;
if (sourceEntity && sourceEntity->GetParent() != nullptr) {
EditorSceneManager::Get().MoveEntity(sourceEntity->GetID(), 0);
::XCEngine::Components::GameObject* sourceGameObject = *(::XCEngine::Components::GameObject**)payload->Data;
if (sourceGameObject && sourceGameObject->GetParent() != nullptr) {
EditorSceneManager::Get().MoveEntity(sourceGameObject->GetID(), 0);
}
}
ImGui::EndDragDropTarget();
@@ -67,26 +69,26 @@ void HierarchyPanel::RenderSearchBar() {
ImGui::InputTextWithHint("##Search", "Search...", m_searchBuffer, sizeof(m_searchBuffer));
}
void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* entity, const std::string& filter) {
if (!entity) return;
void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* gameObject, const std::string& filter) {
if (!gameObject) return;
if (!filter.empty() && !PassesFilter(entity, filter)) {
if (!filter.empty() && !PassesFilter(gameObject, filter)) {
return;
}
ImGui::PushID(static_cast<int>(entity->GetID()));
ImGui::PushID(static_cast<int>(gameObject->GetID()));
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
if (entity->GetChildCount() == 0) {
if (gameObject->GetChildCount() == 0) {
flags |= ImGuiTreeNodeFlags_Leaf;
}
if (SelectionManager::Get().IsSelected(entity)) {
if (SelectionManager::Get().IsSelected(gameObject)) {
flags |= ImGuiTreeNodeFlags_Selected;
}
if (m_renaming && m_renamingEntity == entity) {
if (m_renaming && m_renamingEntity == gameObject) {
if (m_renameJustStarted) {
ImGui::SetKeyboardFocusHere();
m_renameJustStarted = false;
@@ -95,7 +97,7 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* entity, co
ImGui::SetNextItemWidth(-1);
if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
if (strlen(m_renameBuffer) > 0) {
EditorSceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
EditorSceneManager::Get().RenameEntity(gameObject->GetID(), m_renameBuffer);
}
m_renaming = false;
m_renamingEntity = nullptr;
@@ -103,35 +105,42 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* entity, co
if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) {
if (strlen(m_renameBuffer) > 0) {
EditorSceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
EditorSceneManager::Get().RenameEntity(gameObject->GetID(), m_renameBuffer);
}
m_renaming = false;
m_renamingEntity = nullptr;
}
} else {
bool isOpen = ImGui::TreeNodeEx(entity->GetName().c_str(), flags);
bool isOpen = ImGui::TreeNodeEx((void*)gameObject->GetUUID(), flags, "%s", gameObject->GetName().c_str());
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
SelectionManager::Get().SetSelectedEntity(entity);
ImGuiIO& io = ImGui::GetIO();
if (io.KeyCtrl) {
if (!SelectionManager::Get().IsSelected(gameObject)) {
SelectionManager::Get().AddToSelection(gameObject);
}
} else {
SelectionManager::Get().SetSelectedEntity(gameObject);
}
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
m_renaming = true;
m_renamingEntity = entity;
strcpy_s(m_renameBuffer, entity->GetName().c_str());
m_renamingEntity = gameObject;
strcpy_s(m_renameBuffer, gameObject->GetName().c_str());
m_renameJustStarted = true;
}
HandleDragDrop(entity);
HandleDragDrop(gameObject);
if (ImGui::BeginPopupContextItem("EntityContextMenu")) {
RenderContextMenu(entity);
RenderContextMenu(gameObject);
ImGui::EndPopup();
}
if (isOpen) {
for (size_t i = 0; i < entity->GetChildCount(); i++) {
RenderEntity(entity->GetChild(i), filter);
for (size_t i = 0; i < gameObject->GetChildCount(); i++) {
RenderEntity(gameObject->GetChild(i), filter);
}
ImGui::TreePop();
}
@@ -140,44 +149,54 @@ void HierarchyPanel::RenderEntity(::XCEngine::Components::GameObject* entity, co
ImGui::PopID();
}
void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* entity) {
void HierarchyPanel::RenderContextMenu(::XCEngine::Components::GameObject* gameObject) {
auto& sceneManager = EditorSceneManager::Get();
auto& selectionManager = SelectionManager::Get();
if (ImGui::BeginMenu("Create")) {
RenderCreateMenu(entity);
RenderCreateMenu(gameObject);
ImGui::EndMenu();
}
if (gameObject != nullptr && ImGui::MenuItem("Create Child")) {
auto* child = sceneManager.CreateEntity("GameObject", gameObject);
selectionManager.SetSelectedEntity(child);
}
ImGui::Separator();
if (gameObject != nullptr && gameObject->GetParent() != nullptr) {
if (ImGui::MenuItem("Detach from Parent")) {
gameObject->DetachFromParent();
}
}
if (ImGui::MenuItem("Rename", "F2")) {
if (entity) {
if (gameObject) {
m_renaming = true;
m_renamingEntity = entity;
strcpy_s(m_renameBuffer, entity->GetName().c_str());
m_renamingEntity = gameObject;
strcpy_s(m_renameBuffer, gameObject->GetName().c_str());
m_renameJustStarted = true;
}
}
if (ImGui::MenuItem("Delete", "Delete")) {
sceneManager.DeleteEntity(entity->GetID());
sceneManager.DeleteEntity(gameObject->GetID());
}
ImGui::Separator();
if (ImGui::MenuItem("Copy", "Ctrl+C")) {
sceneManager.CopyEntity(entity->GetID());
sceneManager.CopyEntity(gameObject->GetID());
}
if (ImGui::MenuItem("Paste", "Ctrl+V", false, sceneManager.HasClipboardData())) {
sceneManager.PasteEntity(entity->GetID());
sceneManager.PasteEntity(gameObject->GetID());
}
if (ImGui::MenuItem("Duplicate", "Ctrl+D")) {
uint64_t newId = sceneManager.DuplicateEntity(entity->GetID());
uint64_t newId = sceneManager.DuplicateEntity(gameObject->GetID());
if (newId != 0) {
// Selection handled via callback
}
}
}
@@ -225,24 +244,23 @@ void HierarchyPanel::RenderCreateMenu(::XCEngine::Components::GameObject* parent
}
}
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* entity) {
void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* gameObject) {
auto& sceneManager = EditorSceneManager::Get();
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
m_dragSource = entity;
ImGui::SetDragDropPayload("ENTITY_PTR", &entity, sizeof(::XCEngine::Components::GameObject*));
ImGui::Text("%s", entity->GetName().c_str());
ImGui::SetDragDropPayload("ENTITY_PTR", &gameObject, sizeof(::XCEngine::Components::GameObject*));
ImGui::Text("%s", gameObject->GetName().c_str());
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
::XCEngine::Components::GameObject* sourceEntity = *(::XCEngine::Components::GameObject**)payload->Data;
if (sourceEntity != entity && sourceEntity != nullptr) {
::XCEngine::Components::GameObject* sourceGameObject = *(::XCEngine::Components::GameObject**)payload->Data;
if (sourceGameObject != gameObject && sourceGameObject != nullptr) {
bool isValidMove = true;
::XCEngine::Components::GameObject* checkParent = entity;
::XCEngine::Components::GameObject* checkParent = gameObject;
while (checkParent != nullptr) {
if (checkParent == sourceEntity) {
if (checkParent == sourceGameObject) {
isValidMove = false;
break;
}
@@ -250,7 +268,16 @@ void HierarchyPanel::HandleDragDrop(::XCEngine::Components::GameObject* entity)
}
if (isValidMove) {
sceneManager.MoveEntity(sourceEntity->GetID(), entity->GetID());
auto* srcTransform = sourceGameObject->GetTransform();
Math::Vector3 worldPos = srcTransform->GetPosition();
Math::Quaternion worldRot = srcTransform->GetRotation();
Math::Vector3 worldScale = srcTransform->GetScale();
sceneManager.MoveEntity(sourceGameObject->GetID(), gameObject->GetID());
srcTransform->SetPosition(worldPos);
srcTransform->SetRotation(worldRot);
srcTransform->SetScale(worldScale);
}
}
}
@@ -262,20 +289,20 @@ void HierarchyPanel::HandleKeyboardShortcuts() {
auto& sceneManager = EditorSceneManager::Get();
auto& selectionManager = SelectionManager::Get();
::XCEngine::Components::GameObject* selectedEntity = selectionManager.GetSelectedEntity();
::XCEngine::Components::GameObject* selectedGameObject = selectionManager.GetSelectedEntity();
if (ImGui::IsWindowFocused()) {
if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
if (selectedEntity != nullptr) {
sceneManager.DeleteEntity(selectedEntity->GetID());
if (selectedGameObject != nullptr) {
sceneManager.DeleteEntity(selectedGameObject->GetID());
}
}
if (ImGui::IsKeyPressed(ImGuiKey_F2)) {
if (selectedEntity != nullptr) {
if (selectedGameObject != nullptr) {
m_renaming = true;
m_renamingEntity = selectedEntity;
strcpy_s(m_renameBuffer, selectedEntity->GetName().c_str());
m_renamingEntity = selectedGameObject;
strcpy_s(m_renameBuffer, selectedGameObject->GetName().c_str());
m_renameJustStarted = true;
}
}
@@ -283,35 +310,35 @@ void HierarchyPanel::HandleKeyboardShortcuts() {
ImGuiIO& io = ImGui::GetIO();
if (io.KeyCtrl) {
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
if (selectedEntity != nullptr) {
sceneManager.CopyEntity(selectedEntity->GetID());
if (selectedGameObject != nullptr) {
sceneManager.CopyEntity(selectedGameObject->GetID());
}
}
if (ImGui::IsKeyPressed(ImGuiKey_V)) {
if (sceneManager.HasClipboardData()) {
sceneManager.PasteEntity(selectedEntity ? selectedEntity->GetID() : 0);
sceneManager.PasteEntity(selectedGameObject ? selectedGameObject->GetID() : 0);
}
}
if (ImGui::IsKeyPressed(ImGuiKey_D)) {
if (selectedEntity != nullptr) {
sceneManager.DuplicateEntity(selectedEntity->GetID());
if (selectedGameObject != nullptr) {
sceneManager.DuplicateEntity(selectedGameObject->GetID());
}
}
}
}
}
bool HierarchyPanel::PassesFilter(::XCEngine::Components::GameObject* entity, const std::string& filter) {
if (!entity) return false;
bool HierarchyPanel::PassesFilter(::XCEngine::Components::GameObject* gameObject, const std::string& filter) {
if (!gameObject) return false;
if (entity->GetName().find(filter) != std::string::npos) {
if (gameObject->GetName().find(filter) != std::string::npos) {
return true;
}
for (size_t i = 0; i < entity->GetChildCount(); i++) {
if (PassesFilter(entity->GetChild(i), filter)) {
for (size_t i = 0; i < gameObject->GetChildCount(); i++) {
if (PassesFilter(gameObject->GetChild(i), filter)) {
return true;
}
}