2026-03-20 17:08:06 +08:00
|
|
|
#include "InspectorPanel.h"
|
2026-03-25 16:20:21 +08:00
|
|
|
#include "Core/EditorContext.h"
|
2026-03-20 17:08:06 +08:00
|
|
|
#include "Managers/SceneManager.h"
|
2026-03-24 20:02:38 +08:00
|
|
|
#include "UI/UI.h"
|
2026-03-25 12:30:05 +08:00
|
|
|
#include <XCEngine/Debug/Logger.h>
|
2026-03-20 17:08:06 +08:00
|
|
|
#include <imgui.h>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
namespace XCEngine {
|
|
|
|
|
namespace Editor {
|
2026-03-20 17:08:06 +08:00
|
|
|
|
|
|
|
|
InspectorPanel::InspectorPanel() : Panel("Inspector") {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "InspectorPanel constructed");
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InspectorPanel::~InspectorPanel() {
|
2026-03-25 15:54:22 +08:00
|
|
|
if (m_context) {
|
|
|
|
|
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(m_selectionHandlerId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InspectorPanel::OnSelectionChanged(const SelectionChangedEvent& event) {
|
|
|
|
|
m_selectedEntityId = event.primarySelection;
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InspectorPanel::Render() {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "InspectorPanel::Render START");
|
2026-03-20 17:08:06 +08:00
|
|
|
ImGui::Begin(m_name.c_str(), nullptr, ImGuiWindowFlags_None);
|
|
|
|
|
|
2026-03-25 15:54:22 +08:00
|
|
|
if (!m_selectionHandlerId && m_context) {
|
|
|
|
|
m_selectionHandlerId = m_context->GetEventBus().Subscribe<SelectionChangedEvent>(
|
|
|
|
|
[this](const SelectionChangedEvent& event) {
|
|
|
|
|
OnSelectionChanged(event);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_selectedEntityId = m_context->GetSelectionManager().GetSelectedEntity();
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-25 15:54:22 +08:00
|
|
|
if (m_selectedEntityId) {
|
|
|
|
|
auto* sceneManager = static_cast<SceneManager*>(m_context->GetSceneManager());
|
|
|
|
|
auto* gameObject = sceneManager->GetEntity(m_selectedEntityId);
|
|
|
|
|
if (gameObject) {
|
|
|
|
|
RenderGameObject(gameObject);
|
|
|
|
|
} else {
|
|
|
|
|
ImGui::Text("Object not found");
|
|
|
|
|
}
|
2026-03-20 17:08:06 +08:00
|
|
|
} else {
|
|
|
|
|
ImGui::Text("No object selected");
|
|
|
|
|
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Select an object in Hierarchy");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::End();
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "InspectorPanel::Render END");
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
void InspectorPanel::RenderGameObject(::XCEngine::Components::GameObject* gameObject) {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "RenderGameObject START");
|
2026-03-25 01:23:08 +08:00
|
|
|
char nameBuffer[256];
|
|
|
|
|
strcpy_s(nameBuffer, gameObject->GetName().c_str());
|
|
|
|
|
ImGui::InputText("##Name", nameBuffer, sizeof(nameBuffer));
|
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
|
gameObject->SetName(nameBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImGui::SameLine();
|
|
|
|
|
if (ImGui::Button("Add Component")) {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "Add Component BUTTON CLICKED");
|
2026-03-25 01:23:08 +08:00
|
|
|
ImGui::OpenPopup("AddComponent");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RenderAddComponentPopup(gameObject);
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
auto components = gameObject->GetComponents<::XCEngine::Components::Component>();
|
2026-03-24 20:02:38 +08:00
|
|
|
for (auto* component : components) {
|
2026-03-25 01:23:08 +08:00
|
|
|
RenderComponent(component, gameObject);
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "RenderGameObject END");
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
void InspectorPanel::RenderAddComponentPopup(::XCEngine::Components::GameObject* gameObject) {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "RenderAddComponentPopup called");
|
|
|
|
|
|
|
|
|
|
if (!gameObject) {
|
|
|
|
|
Debug::Logger::Get().Error(Debug::LogCategory::General, "ERROR: gameObject is nullptr!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
if (!ImGui::BeginPopup("AddComponent")) {
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "BeginPopup returned false");
|
2026-03-25 01:23:08 +08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "BeginPopup succeeded");
|
2026-03-25 01:23:08 +08:00
|
|
|
|
2026-03-25 12:56:51 +08:00
|
|
|
ImGui::Text("Components");
|
|
|
|
|
ImGui::Separator();
|
2026-03-25 01:23:08 +08:00
|
|
|
|
2026-03-25 12:30:05 +08:00
|
|
|
bool hasTransform = gameObject->GetComponent<::XCEngine::Components::TransformComponent>() != nullptr;
|
|
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, hasTransform ? "Has Transform: yes" : "Has Transform: no");
|
|
|
|
|
|
|
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "About to check MenuItem condition");
|
|
|
|
|
if (ImGui::MenuItem("Transform", nullptr, false, !hasTransform)) {
|
|
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "MenuItem CLICKED! Before AddComponent");
|
2026-03-25 12:30:35 +08:00
|
|
|
|
2026-03-25 12:56:51 +08:00
|
|
|
auto* newComp = gameObject->AddComponent<::XCEngine::Components::TransformComponent>();
|
|
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, newComp ? "AddComponent SUCCEEDED" : "AddComponent FAILED");
|
2026-03-25 12:30:35 +08:00
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
ImGui::CloseCurrentPopup();
|
2026-03-25 12:30:05 +08:00
|
|
|
} else {
|
|
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "MenuItem not clicked (disabled or condition false)");
|
2026-03-25 01:23:08 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-25 12:56:51 +08:00
|
|
|
ImGui::Separator();
|
2026-03-25 01:23:08 +08:00
|
|
|
ImGui::TextDisabled("No more components available");
|
|
|
|
|
|
2026-03-25 12:56:51 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "About to EndPopup - calling EndPopup");
|
2026-03-25 01:23:08 +08:00
|
|
|
ImGui::EndPopup();
|
2026-03-25 12:30:05 +08:00
|
|
|
Debug::Logger::Get().Debug(Debug::LogCategory::General, "Popup closed");
|
2026-03-25 01:23:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InspectorPanel::RenderComponent(::XCEngine::Components::Component* component, ::XCEngine::Components::GameObject* gameObject) {
|
2026-03-20 17:08:06 +08:00
|
|
|
if (!component) return;
|
|
|
|
|
|
|
|
|
|
const char* name = component->GetName().c_str();
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{4, 2});
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
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;
|
|
|
|
|
if (ImGui::BeginPopupContextItem("ComponentSettings")) {
|
|
|
|
|
if (ImGui::MenuItem("Remove Component")) {
|
|
|
|
|
removeComponent = true;
|
|
|
|
|
}
|
|
|
|
|
ImGui::EndPopup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (removeComponent) {
|
|
|
|
|
RemoveComponentByType(component, gameObject);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (open) {
|
2026-03-24 20:02:38 +08:00
|
|
|
if (auto* transform = dynamic_cast<::XCEngine::Components::TransformComponent*>(component)) {
|
|
|
|
|
::XCEngine::Math::Vector3 position = transform->GetLocalPosition();
|
|
|
|
|
::XCEngine::Math::Vector3 rotation = transform->GetLocalEulerAngles();
|
|
|
|
|
::XCEngine::Math::Vector3 scale = transform->GetLocalScale();
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
if (UI::DrawVec3("Position", position, 0.0f, 80.0f, 0.1f)) {
|
2026-03-24 18:38:01 +08:00
|
|
|
transform->SetLocalPosition(position);
|
|
|
|
|
}
|
2026-03-20 17:08:06 +08:00
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
if (UI::DrawVec3("Rotation", rotation, 0.0f, 80.0f, 1.0f)) {
|
2026-03-24 18:38:01 +08:00
|
|
|
transform->SetLocalEulerAngles(rotation);
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-24 20:02:38 +08:00
|
|
|
if (UI::DrawVec3("Scale", scale, 1.0f, 80.0f, 0.1f)) {
|
2026-03-24 18:38:01 +08:00
|
|
|
transform->SetLocalScale(scale);
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto components = gameObject->GetComponents<::XCEngine::Components::Component>();
|
|
|
|
|
for (auto* comp : components) {
|
|
|
|
|
if (comp == component) {
|
|
|
|
|
gameObject->RemoveComponent<::XCEngine::Components::Component>();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-03-20 17:08:06 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-24 18:38:01 +08:00
|
|
|
}
|
2026-03-24 20:02:38 +08:00
|
|
|
}
|