#include "InspectorPanel.h" #include "Core/IEditorContext.h" #include "Core/ISceneManager.h" #include "Core/ISelectionManager.h" #include "Core/EventBus.h" #include "Core/EditorEvents.h" #include "ComponentEditors/CameraComponentEditor.h" #include "ComponentEditors/IComponentEditor.h" #include "ComponentEditors/LightComponentEditor.h" #include "ComponentEditors/TransformComponentEditor.h" #include #include namespace XCEngine { namespace Editor { InspectorPanel::InspectorPanel() : Panel("Inspector") { RegisterDefaultComponentEditors(); } InspectorPanel::~InspectorPanel() { if (m_context) { m_context->GetEventBus().Unsubscribe(m_selectionHandlerId); } } void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) { m_selectedEntityId = event.primarySelection; } void InspectorPanel::RegisterDefaultComponentEditors() { RegisterComponentEditor(std::make_unique()); RegisterComponentEditor(std::make_unique()); RegisterComponentEditor(std::make_unique()); } void InspectorPanel::RegisterComponentEditor(std::unique_ptr editor) { if (!editor) { return; } m_componentEditors.push_back(std::move(editor)); } IComponentEditor* InspectorPanel::GetEditorFor(::XCEngine::Components::Component* component) const { if (!component) { return nullptr; } for (const auto& editor : m_componentEditors) { if (editor && editor->CanEdit(component)) { return editor.get(); } } return nullptr; } void InspectorPanel::Render() { ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None); 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::Text("Object not found"); } } else { ImGui::Text("No object selected"); ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Select an object in Hierarchy"); } ImGui::End(); } void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameObject) { char nameBuffer[256]; strcpy_s(nameBuffer, gameObject->GetName().c_str()); ImGui::InputText("##Name", nameBuffer, sizeof(nameBuffer)); if (ImGui::IsItemDeactivatedAfterEdit()) { m_context->GetSceneManager().RenameEntity(gameObject->GetID(), nameBuffer); } ImGui::SameLine(); if (ImGui::Button("Add Component")) { ImGui::OpenPopup("AddComponent"); } RenderAddComponentPopup(gameObject); auto components = gameObject->GetComponents<::XCEngine::Components::Component>(); for (auto* component : components) { RenderComponent(component, gameObject); } } void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) { if (!ImGui::BeginPopup("AddComponent")) { return; } ImGui::Text("Components"); ImGui::Separator(); bool drewAnyEntry = false; for (const auto& editor : m_componentEditors) { 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)) { if (editor->AddTo(gameObject)) { m_context->GetSceneManager().MarkSceneDirty(); 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 = GetEditorFor(component); const char* name = component->GetName().c_str(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{4, 2}); ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_FramePadding | ImGuiTreeNodeFlags_AllowOverlap; bool open = ImGui::TreeNodeEx((void*)typeid(*component).hash_code(), flags, "%s", name); ImGui::PopStyleVar(); 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->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; } gameObject->RemoveComponent(component); m_context->GetSceneManager().MarkSceneDirty(); } } }