#include "HierarchyPanel.h" #include "Managers/SceneManager.h" #include "Managers/SelectionManager.h" #include #include namespace UI { HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") { SceneManager::Get().CreateDemoScene(); m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](EntityID) { }); } HierarchyPanel::~HierarchyPanel() { SelectionManager::Get().OnSelectionChanged.Unsubscribe(m_selectionHandlerId); } void HierarchyPanel::Render() { ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None); RenderSearchBar(); ImGui::Separator(); HandleKeyboardShortcuts(); std::string filter = m_searchBuffer; ImGui::BeginChild("EntityList"); for (EntityID id : SceneManager::Get().GetRootEntities()) { RenderEntity(id, filter); } if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) { if (!m_renaming) { SelectionManager::Get().ClearSelection(); } } if (ImGui::BeginPopupContextWindow("HierarchyContextMenu", ImGuiPopupFlags_MouseButtonRight)) { RenderCreateMenu(INVALID_ENTITY_ID); ImGui::EndPopup(); } ImGui::InvisibleButton("##DragTarget", ImVec2(-1, -1)); if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) { EntityID sourceId = *(const EntityID*)payload->Data; if (sourceId != INVALID_ENTITY_ID) { const GameObject* sourceEntity = SceneManager::Get().GetEntity(sourceId); if (sourceEntity && sourceEntity->parent != INVALID_ENTITY_ID) { SceneManager::Get().MoveEntity(sourceId, INVALID_ENTITY_ID); } } } ImGui::EndDragDropTarget(); } ImGui::EndChild(); ImGui::End(); } void HierarchyPanel::RenderSearchBar() { ImGui::SetNextItemWidth(-1); ImGui::InputTextWithHint("##Search", "Search...", m_searchBuffer, sizeof(m_searchBuffer)); } void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) { auto& sceneManager = SceneManager::Get(); GameObject* entity = sceneManager.GetEntity(id); if (!entity) return; if (!filter.empty() && !PassesFilter(id, filter)) { return; } ImGui::PushID(static_cast(id)); ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth; if (entity->children.empty()) { flags |= ImGuiTreeNodeFlags_Leaf; } if (SelectionManager::Get().IsSelected(id)) { flags |= ImGuiTreeNodeFlags_Selected; } if (m_renaming && m_renamingEntity == id) { if (m_renameJustStarted) { ImGui::SetKeyboardFocusHere(); m_renameJustStarted = false; } ImGui::SetNextItemWidth(-1); if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) { if (strlen(m_renameBuffer) > 0) { sceneManager.RenameEntity(id, m_renameBuffer); } m_renaming = false; m_renamingEntity = INVALID_ENTITY_ID; } if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) { if (strlen(m_renameBuffer) > 0) { sceneManager.RenameEntity(id, m_renameBuffer); } m_renaming = false; m_renamingEntity = INVALID_ENTITY_ID; } } else { bool isOpen = ImGui::TreeNodeEx(entity->name.c_str(), flags); if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) { SelectionManager::Get().SetSelectedEntity(id); } if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) { m_renaming = true; m_renamingEntity = id; strcpy_s(m_renameBuffer, entity->name.c_str()); m_renameJustStarted = true; } HandleDragDrop(id); if (ImGui::BeginPopupContextItem("EntityContextMenu")) { RenderContextMenu(id); ImGui::EndPopup(); } if (isOpen) { for (EntityID childId : entity->children) { RenderEntity(childId, filter); } ImGui::TreePop(); } } ImGui::PopID(); } void HierarchyPanel::RenderContextMenu(EntityID id) { auto& sceneManager = SceneManager::Get(); auto& selectionManager = SelectionManager::Get(); if (ImGui::BeginMenu("Create")) { RenderCreateMenu(id); ImGui::EndMenu(); } ImGui::Separator(); if (ImGui::MenuItem("Rename", "F2")) { const GameObject* entity = sceneManager.GetEntity(id); if (entity) { m_renaming = true; m_renamingEntity = id; strcpy_s(m_renameBuffer, entity->name.c_str()); m_renameJustStarted = true; } } if (ImGui::MenuItem("Delete", "Delete")) { sceneManager.DeleteEntity(id); } ImGui::Separator(); if (ImGui::MenuItem("Copy", "Ctrl+C")) { sceneManager.CopyEntity(id); } if (ImGui::MenuItem("Paste", "Ctrl+V", false, sceneManager.HasClipboardData())) { sceneManager.PasteEntity(id); } if (ImGui::MenuItem("Duplicate", "Ctrl+D")) { EntityID newId = sceneManager.DuplicateEntity(id); if (newId != INVALID_ENTITY_ID) { selectionManager.SetSelectedEntity(newId); } } } void HierarchyPanel::RenderCreateMenu(EntityID parent) { auto& sceneManager = SceneManager::Get(); auto& selectionManager = SelectionManager::Get(); if (ImGui::MenuItem("Empty Object")) { EntityID newId = sceneManager.CreateEntity("GameObject", parent); selectionManager.SetSelectedEntity(newId); } ImGui::Separator(); if (ImGui::MenuItem("Camera")) { EntityID newId = sceneManager.CreateEntity("Camera", parent); sceneManager.GetEntity(newId)->AddComponent(); selectionManager.SetSelectedEntity(newId); } if (ImGui::MenuItem("Light")) { EntityID newId = sceneManager.CreateEntity("Light", parent); selectionManager.SetSelectedEntity(newId); } ImGui::Separator(); if (ImGui::MenuItem("Cube")) { EntityID newId = sceneManager.CreateEntity("Cube", parent); sceneManager.GetEntity(newId)->AddComponent(); sceneManager.GetEntity(newId)->AddComponent()->meshName = "Cube"; selectionManager.SetSelectedEntity(newId); } if (ImGui::MenuItem("Sphere")) { EntityID newId = sceneManager.CreateEntity("Sphere", parent); sceneManager.GetEntity(newId)->AddComponent(); sceneManager.GetEntity(newId)->AddComponent()->meshName = "Sphere"; selectionManager.SetSelectedEntity(newId); } if (ImGui::MenuItem("Plane")) { EntityID newId = sceneManager.CreateEntity("Plane", parent); sceneManager.GetEntity(newId)->AddComponent(); sceneManager.GetEntity(newId)->AddComponent()->meshName = "Plane"; selectionManager.SetSelectedEntity(newId); } } void HierarchyPanel::HandleDragDrop(EntityID id) { auto& sceneManager = SceneManager::Get(); if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) { m_dragSource = id; ImGui::SetDragDropPayload("ENTITY_ID", &id, sizeof(EntityID)); const GameObject* entity = sceneManager.GetEntity(id); if (entity) { ImGui::Text("%s", entity->name.c_str()); } ImGui::EndDragDropSource(); } if (ImGui::BeginDragDropTarget()) { if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) { EntityID sourceId = *(const EntityID*)payload->Data; if (sourceId != id && sourceId != INVALID_ENTITY_ID) { const GameObject* targetEntity = sceneManager.GetEntity(id); const GameObject* sourceEntity = sceneManager.GetEntity(sourceId); bool isValidMove = true; EntityID checkParent = targetEntity ? targetEntity->parent : INVALID_ENTITY_ID; while (checkParent != INVALID_ENTITY_ID) { if (checkParent == sourceId) { isValidMove = false; break; } const GameObject* parentEntity = sceneManager.GetEntity(checkParent); checkParent = parentEntity ? parentEntity->parent : INVALID_ENTITY_ID; } if (isValidMove && sourceEntity && sourceEntity->parent != id) { sceneManager.MoveEntity(sourceId, id); } } } ImGui::EndDragDropTarget(); } } void HierarchyPanel::HandleKeyboardShortcuts() { auto& sceneManager = SceneManager::Get(); auto& selectionManager = SelectionManager::Get(); EntityID selectedId = selectionManager.GetSelectedEntity(); if (ImGui::IsWindowFocused()) { if (ImGui::IsKeyPressed(ImGuiKey_Delete)) { if (selectedId != INVALID_ENTITY_ID) { sceneManager.DeleteEntity(selectedId); } } if (ImGui::IsKeyPressed(ImGuiKey_F2)) { if (selectedId != INVALID_ENTITY_ID) { const GameObject* entity = sceneManager.GetEntity(selectedId); if (entity) { m_renaming = true; m_renamingEntity = selectedId; strcpy_s(m_renameBuffer, entity->name.c_str()); m_renameJustStarted = true; } } } ImGuiIO& io = ImGui::GetIO(); if (io.KeyCtrl) { if (ImGui::IsKeyPressed(ImGuiKey_C)) { if (selectedId != INVALID_ENTITY_ID) { sceneManager.CopyEntity(selectedId); } } if (ImGui::IsKeyPressed(ImGuiKey_V)) { if (sceneManager.HasClipboardData()) { sceneManager.PasteEntity(selectedId); } } if (ImGui::IsKeyPressed(ImGuiKey_D)) { if (selectedId != INVALID_ENTITY_ID) { EntityID newId = sceneManager.DuplicateEntity(selectedId); if (newId != INVALID_ENTITY_ID) { selectionManager.SetSelectedEntity(newId); } } } } } } bool HierarchyPanel::PassesFilter(EntityID id, const std::string& filter) { auto& sceneManager = SceneManager::Get(); const GameObject* entity = sceneManager.GetEntity(id); if (!entity) return false; if (entity->name.find(filter) != std::string::npos) { return true; } for (EntityID childId : entity->children) { if (PassesFilter(childId, filter)) { return true; } } return false; } }