Files
XCEngine/editor/src/panels/InspectorPanel.cpp

203 lines
6.6 KiB
C++
Raw Normal View History

#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 <imgui.h>
#include <string>
namespace XCEngine {
namespace Editor {
InspectorPanel::InspectorPanel() : Panel("Inspector") {}
InspectorPanel::~InspectorPanel() {
if (m_context) {
m_context->GetEventBus().Unsubscribe<SelectionChangedEvent>(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<SelectionChangedEvent>(
[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();
});
}
}
}