#include "Actions/EditorActions.h" #include "Commands/ComponentCommands.h" #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/ComponentEditorRegistry.h" #include "ComponentEditors/IComponentEditor.h" #include "UI/UI.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; m_addComponentPopup.Clear(); } void InspectorPanel::Render() { UI::PanelWindowScope panel(m_name.c_str()); if (!panel.IsOpen()) { return; } 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 { RenderEmptyState("Object not found"); } } else { RenderEmptyState("No Selection", "Select an object in Hierarchy"); } } void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameObject) { ImGuiStyle& style = ImGui::GetStyle(); UI::PanelContentScope content( "InspectorContent", UI::InspectorPanelContentPadding(), ImGuiWindowFlags_None, true, ImVec2(style.ItemSpacing.x, 0.0f)); if (!content.IsOpen()) { return; } auto components = gameObject->GetComponents<::XCEngine::Components::Component>(); for (auto* component : components) { RenderComponent(component, gameObject); } if (Actions::DrawInspectorAction(Actions::MakeAddComponentButtonAction(gameObject != nullptr))) { m_addComponentPopup.RequestOpen(); } RenderAddComponentPopup(gameObject); if (m_context->GetUndoManager().HasPendingInteractiveChange() && !ImGui::IsAnyItemActive()) { m_context->GetUndoManager().FinalizeInteractiveChange(); } } void InspectorPanel::RenderEmptyState(const char* title, const char* subtitle) { UI::PanelContentScope content("InspectorEmptyState", UI::InspectorPanelContentPadding()); if (!content.IsOpen()) { return; } UI::DrawEmptyState(title, subtitle); } void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) { m_addComponentPopup.ConsumeOpenRequest("AddComponent"); if (!UI::BeginTitledPopup("AddComponent", "Components")) { return; } bool drewAnyEntry = false; for (const auto& editor : ComponentEditorRegistry::Get().GetEditors()) { if (!editor || !editor->ShowInAddComponentMenu()) { continue; } drewAnyEntry = true; const bool canAdd = Commands::CanAddComponent(*editor, gameObject); std::string label = editor->GetDisplayName(); if (!canAdd) { const char* reason = editor->GetAddDisabledReason(gameObject); if (reason && reason[0] != '\0') { label += " ("; label += reason; label += ")"; } } Actions::DrawMenuAction(Actions::MakeAddComponentMenuAction(label, canAdd), [&]() { if (Commands::AddComponent(*m_context, *editor, gameObject)) { ImGui::CloseCurrentPopup(); } }); } if (!drewAnyEntry) { UI::DrawHintText("No registered component editors"); } UI::EndTitledPopup(); } void InspectorPanel::RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) { if (!component) return; IComponentEditor* editor = ComponentEditorRegistry::Get().FindEditor(component); const std::string name = component->GetName(); const UI::ComponentSectionResult section = UI::BeginComponentSection( (void*)typeid(*component).hash_code(), name.c_str(), Commands::CanRemoveComponent(component, editor)); if (section.removeRequested) { Commands::RemoveComponent(*m_context, component, gameObject, editor); return; } if (section.open) { if (editor) { if (editor->Render(component, &m_context->GetUndoManager())) { m_context->GetSceneManager().MarkSceneDirty(); } } else { UI::DrawHintText("No registered editor for this component"); } UI::EndComponentSection(); } } } }