#include "InspectorPanel.h" #include "Core/IEditorContext.h" #include "Core/ISceneManager.h" #include "Core/ISelectionManager.h" #include "Core/IUndoManager.h" #include "Core/EventBus.h" #include "Core/EditorEvents.h" #include "ComponentEditors/ComponentEditorRegistry.h" #include "ComponentEditors/IComponentEditor.h" #include "Utils/UndoUtils.h" #include #include namespace XCEngine { namespace Editor { InspectorPanel::InspectorPanel() : Panel("Inspector") {} InspectorPanel::~InspectorPanel() { if (m_context) { m_context->GetEventBus().Unsubscribe(m_selectionHandlerId); } } void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) { if (m_context && m_context->GetUndoManager().HasPendingInteractiveChange()) { m_context->GetUndoManager().FinalizeInteractiveChange(); } m_selectedEntityId = event.primarySelection; } void InspectorPanel::Render() { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None); ImGui::PopStyleVar(); if (!m_selectionHandlerId && m_context) { m_selectionHandlerId = m_context->GetEventBus().Subscribe( [this](const SelectionChangedEvent& event) { OnSelectionChanged(event); } ); } m_selectedEntityId = m_context->GetSelectionManager().GetSelectedEntity(); if (m_selectedEntityId) { auto& sceneManager = m_context->GetSceneManager(); auto* gameObject = sceneManager.GetEntity(m_selectedEntityId); if (gameObject) { RenderGameObject(gameObject); } else { ImGui::SetCursorPos(ImVec2(10.0f, 10.0f)); ImGui::Text("Object not found"); } } else { ImGui::SetCursorPos(ImVec2(10.0f, 10.0f)); ImGui::Text("No Selection"); ImGui::SetCursorPos(ImVec2(10.0f, 30.0f)); ImGui::TextColored(ImVec4(0.55f, 0.55f, 0.55f, 1.0f), "Select an object in Hierarchy"); } ImGui::End(); } void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameObject) { ImGuiStyle& style = ImGui::GetStyle(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 0.0f)); ImGui::BeginChild("InspectorContent", ImVec2(0.0f, 0.0f), false, ImGuiWindowFlags_None); ImGui::PopStyleVar(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, 0.0f)); auto components = gameObject->GetComponents<::XCEngine::Components::Component>(); for (auto* component : components) { RenderComponent(component, gameObject); } if (ImGui::Button("Add Component", ImVec2(-1.0f, 0.0f))) { ImGui::OpenPopup("AddComponent"); } RenderAddComponentPopup(gameObject); ImGui::PopStyleVar(); if (m_context->GetUndoManager().HasPendingInteractiveChange() && !ImGui::IsAnyItemActive()) { m_context->GetUndoManager().FinalizeInteractiveChange(); } ImGui::EndChild(); } void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) { if (!ImGui::BeginPopup("AddComponent")) { return; } ImGui::Text("Components"); ImGui::Separator(); bool drewAnyEntry = false; for (const auto& editor : ComponentEditorRegistry::Get().GetEditors()) { if (!editor || !editor->ShowInAddComponentMenu()) { continue; } drewAnyEntry = true; const bool canAdd = editor->CanAddTo(gameObject); std::string label = editor->GetDisplayName(); if (!canAdd) { const char* reason = editor->GetAddDisabledReason(gameObject); if (reason && reason[0] != '\0') { label += " ("; label += reason; label += ")"; } } if (ImGui::MenuItem(label.c_str(), nullptr, false, canAdd)) { bool added = false; UndoUtils::ExecuteSceneCommand(*m_context, std::string("Add ") + editor->GetDisplayName() + " Component", [&]() { added = editor->AddTo(gameObject) != nullptr; if (added) { m_context->GetSceneManager().MarkSceneDirty(); } }); if (added) { ImGui::CloseCurrentPopup(); } } } if (!drewAnyEntry) { ImGui::TextDisabled("No registered component editors"); } ImGui::EndPopup(); } void InspectorPanel::RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) { if (!component) return; IComponentEditor* editor = ComponentEditorRegistry::Get().FindEditor(component); ImGuiStyle& style = ImGui::GetStyle(); const std::string name = component->GetName(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{6, 4}); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, 0.0f)); ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_AllowOverlap; bool open = ImGui::TreeNodeEx((void*)typeid(*component).hash_code(), flags, "%s", name.c_str()); ImGui::PopStyleVar(2); bool removeComponent = false; const bool canRemoveComponent = editor ? editor->CanRemove(component) : false; if (ImGui::BeginPopupContextItem("ComponentSettings")) { if (ImGui::MenuItem("Remove Component", nullptr, false, canRemoveComponent)) { removeComponent = true; } ImGui::EndPopup(); } if (removeComponent) { RemoveComponentByType(component, gameObject); return; } if (open) { if (editor) { if (editor->Render(component, &m_context->GetUndoManager())) { m_context->GetSceneManager().MarkSceneDirty(); } } else { ImGui::TextDisabled("No registered editor for this component"); } ImGui::TreePop(); } } void InspectorPanel::RemoveComponentByType(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) { if (!component || !gameObject) return; if (dynamic_cast<::XCEngine::Components::TransformComponent*>(component)) { return; } UndoUtils::ExecuteSceneCommand(*m_context, std::string("Remove ") + component->GetName() + " Component", [&]() { gameObject->RemoveComponent(component); m_context->GetSceneManager().MarkSceneDirty(); }); } } }