feat(new_editor): wire project, inspector, and viewport runtime
This commit is contained in:
237
new_editor/app/Features/Inspector/InspectorPresentationModel.cpp
Normal file
237
new_editor/app/Features/Inspector/InspectorPresentationModel.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "Features/Inspector/InspectorPresentationModel.h"
|
||||
|
||||
#include "Features/Inspector/Components/IInspectorComponentEditor.h"
|
||||
#include "Features/Inspector/Components/InspectorComponentEditorRegistry.h"
|
||||
#include "Scene/EditorSceneRuntime.h"
|
||||
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace XCEngine::UI::Editor::App {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::Components::GameObject;
|
||||
using Widgets::UIEditorPropertyGridField;
|
||||
using Widgets::UIEditorPropertyGridFieldKind;
|
||||
using Widgets::UIEditorPropertyGridSection;
|
||||
|
||||
std::string PathToUtf8String(const std::filesystem::path& path) {
|
||||
const std::u8string value = path.u8string();
|
||||
return std::string(value.begin(), value.end());
|
||||
}
|
||||
|
||||
std::string ResolveSceneObjectDisplayName(
|
||||
const InspectorSceneObjectSubject& sceneObject) {
|
||||
return sceneObject.displayName.empty()
|
||||
? std::string("GameObject")
|
||||
: sceneObject.displayName;
|
||||
}
|
||||
|
||||
std::string ResolveProjectSelectionTitle(
|
||||
const EditorSelectionState& selection) {
|
||||
if (!selection.displayName.empty()) {
|
||||
return selection.displayName;
|
||||
}
|
||||
|
||||
return selection.directory
|
||||
? std::string("Folder")
|
||||
: std::string("Asset");
|
||||
}
|
||||
|
||||
UIEditorPropertyGridField BuildReadOnlyTextField(
|
||||
std::string fieldId,
|
||||
std::string label,
|
||||
std::string value) {
|
||||
UIEditorPropertyGridField field = {};
|
||||
field.fieldId = std::move(fieldId);
|
||||
field.label = std::move(label);
|
||||
field.kind = UIEditorPropertyGridFieldKind::Text;
|
||||
field.valueText = std::move(value);
|
||||
field.readOnly = true;
|
||||
return field;
|
||||
}
|
||||
|
||||
void AppendReadOnlySection(
|
||||
InspectorPresentationModel& model,
|
||||
std::string sectionId,
|
||||
std::string title,
|
||||
std::vector<UIEditorPropertyGridField> fields) {
|
||||
UIEditorPropertyGridSection section = {};
|
||||
section.sectionId = std::move(sectionId);
|
||||
section.title = std::move(title);
|
||||
section.fields = std::move(fields);
|
||||
model.sections.push_back(std::move(section));
|
||||
}
|
||||
|
||||
void AppendFallbackComponentSection(
|
||||
InspectorPresentationModel& model,
|
||||
const EditorSceneComponentDescriptor& descriptor) {
|
||||
std::vector<UIEditorPropertyGridField> fields = {};
|
||||
fields.push_back(BuildReadOnlyTextField(
|
||||
"component." + descriptor.componentId + ".type",
|
||||
"Type",
|
||||
descriptor.typeName));
|
||||
fields.push_back(BuildReadOnlyTextField(
|
||||
"component." + descriptor.componentId + ".status",
|
||||
"Status",
|
||||
"Inspector not implemented"));
|
||||
|
||||
AppendReadOnlySection(
|
||||
model,
|
||||
"component." + descriptor.componentId,
|
||||
descriptor.typeName,
|
||||
std::move(fields));
|
||||
}
|
||||
|
||||
void AppendComponentPresentation(
|
||||
InspectorPresentationModel& model,
|
||||
const EditorSceneComponentDescriptor& descriptor,
|
||||
const GameObject* gameObject,
|
||||
const InspectorComponentEditorRegistry& componentEditorRegistry) {
|
||||
InspectorPresentationComponentBinding binding = {};
|
||||
binding.componentId = descriptor.componentId;
|
||||
binding.typeName = descriptor.typeName;
|
||||
binding.removable = descriptor.removable;
|
||||
|
||||
InspectorComponentEditorContext context = {};
|
||||
context.gameObject = gameObject;
|
||||
context.component = descriptor.component;
|
||||
context.componentId = descriptor.componentId;
|
||||
context.typeName = descriptor.typeName;
|
||||
context.removable = descriptor.removable;
|
||||
|
||||
const IInspectorComponentEditor* editor =
|
||||
componentEditorRegistry.FindEditor(descriptor.typeName);
|
||||
if (editor != nullptr) {
|
||||
context.displayName = std::string(editor->GetDisplayName());
|
||||
binding.displayName = context.displayName;
|
||||
|
||||
const std::size_t sectionCountBefore = model.sections.size();
|
||||
editor->BuildSections(context, model.sections);
|
||||
for (std::size_t sectionIndex = sectionCountBefore;
|
||||
sectionIndex < model.sections.size();
|
||||
++sectionIndex) {
|
||||
for (const UIEditorPropertyGridField& field :
|
||||
model.sections[sectionIndex].fields) {
|
||||
binding.fieldIds.push_back(field.fieldId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.displayName = descriptor.typeName;
|
||||
binding.displayName = descriptor.typeName;
|
||||
AppendFallbackComponentSection(model, descriptor);
|
||||
if (!model.sections.empty()) {
|
||||
for (const UIEditorPropertyGridField& field :
|
||||
model.sections.back().fields) {
|
||||
binding.fieldIds.push_back(field.fieldId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.componentBindings.push_back(std::move(binding));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
InspectorPresentationModel BuildInspectorPresentationModel(
|
||||
const InspectorSubject& subject,
|
||||
const EditorSceneRuntime& sceneRuntime,
|
||||
const InspectorComponentEditorRegistry& componentEditorRegistry) {
|
||||
InspectorPresentationModel model = {};
|
||||
model.hasSelection = subject.HasSelection();
|
||||
|
||||
switch (subject.kind) {
|
||||
case InspectorSubjectKind::ProjectAsset: {
|
||||
const EditorSelectionState& selection = subject.projectAsset.selection;
|
||||
const std::string title = ResolveProjectSelectionTitle(selection);
|
||||
const std::string typeLabel =
|
||||
selection.directory ? std::string("Folder") : std::string("Asset");
|
||||
|
||||
model.title = title;
|
||||
model.subtitle = typeLabel;
|
||||
|
||||
AppendReadOnlySection(
|
||||
model,
|
||||
"project.identity",
|
||||
"Identity",
|
||||
{
|
||||
BuildReadOnlyTextField("project.identity.type", "Type", typeLabel),
|
||||
BuildReadOnlyTextField("project.identity.name", "Name", title),
|
||||
BuildReadOnlyTextField("project.identity.id", "Id", selection.itemId)
|
||||
});
|
||||
AppendReadOnlySection(
|
||||
model,
|
||||
"project.location",
|
||||
"Location",
|
||||
{
|
||||
BuildReadOnlyTextField(
|
||||
"project.location.path",
|
||||
"Path",
|
||||
PathToUtf8String(selection.absolutePath))
|
||||
});
|
||||
return model;
|
||||
}
|
||||
|
||||
case InspectorSubjectKind::SceneObject: {
|
||||
const InspectorSceneObjectSubject& sceneObject = subject.sceneObject;
|
||||
const std::string title = ResolveSceneObjectDisplayName(sceneObject);
|
||||
|
||||
model.title = title;
|
||||
model.subtitle = "GameObject";
|
||||
|
||||
AppendReadOnlySection(
|
||||
model,
|
||||
"scene.identity",
|
||||
"Identity",
|
||||
{
|
||||
BuildReadOnlyTextField("scene.identity.type", "Type", "GameObject"),
|
||||
BuildReadOnlyTextField("scene.identity.name", "Name", title),
|
||||
BuildReadOnlyTextField("scene.identity.id", "Id", sceneObject.itemId)
|
||||
});
|
||||
|
||||
if (sceneObject.gameObject != nullptr) {
|
||||
const GameObject* parent = sceneObject.gameObject->GetParent();
|
||||
const std::string parentName =
|
||||
parent != nullptr
|
||||
? (parent->GetName().empty()
|
||||
? std::string("GameObject")
|
||||
: parent->GetName())
|
||||
: std::string("Scene Root");
|
||||
AppendReadOnlySection(
|
||||
model,
|
||||
"scene.hierarchy",
|
||||
"Hierarchy",
|
||||
{
|
||||
BuildReadOnlyTextField(
|
||||
"scene.hierarchy.children",
|
||||
"Children",
|
||||
std::to_string(sceneObject.gameObject->GetChildCount())),
|
||||
BuildReadOnlyTextField(
|
||||
"scene.hierarchy.parent",
|
||||
"Parent",
|
||||
parentName)
|
||||
});
|
||||
}
|
||||
|
||||
for (const EditorSceneComponentDescriptor& descriptor :
|
||||
sceneRuntime.GetSelectedComponents()) {
|
||||
AppendComponentPresentation(
|
||||
model,
|
||||
descriptor,
|
||||
sceneObject.gameObject,
|
||||
componentEditorRegistry);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
case InspectorSubjectKind::None:
|
||||
default:
|
||||
model.title = "Nothing selected";
|
||||
model.subtitle = "Select a hierarchy item or project asset.";
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::App
|
||||
Reference in New Issue
Block a user