feat: expand editor scripting asset and viewport flow
This commit is contained in:
515
editor/src/ComponentEditors/ScriptComponentEditor.h
Normal file
515
editor/src/ComponentEditors/ScriptComponentEditor.h
Normal file
@@ -0,0 +1,515 @@
|
||||
#pragma once
|
||||
|
||||
#include "Application.h"
|
||||
#include "IComponentEditor.h"
|
||||
#include "ScriptComponentEditorUtils.h"
|
||||
#include "Core/IUndoManager.h"
|
||||
#include "UI/UI.h"
|
||||
|
||||
#include <XCEngine/Scripting/ScriptComponent.h>
|
||||
#include <XCEngine/Scripting/ScriptEngine.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
class ScriptComponentEditor : public IComponentEditor {
|
||||
public:
|
||||
const char* GetComponentTypeName() const override {
|
||||
return "ScriptComponent";
|
||||
}
|
||||
|
||||
const char* GetDisplayName() const override {
|
||||
return "Script";
|
||||
}
|
||||
|
||||
bool Render(::XCEngine::Components::Component* component, IUndoManager* undoManager) override {
|
||||
auto* scriptComponent = dynamic_cast<::XCEngine::Scripting::ScriptComponent*>(component);
|
||||
if (!scriptComponent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
changed |= RenderScriptClassSelector(*scriptComponent, undoManager);
|
||||
|
||||
::XCEngine::Scripting::ScriptFieldModel model;
|
||||
if (!::XCEngine::Scripting::ScriptEngine::Get().TryGetScriptFieldModel(scriptComponent, model)) {
|
||||
UI::DrawHintText("Failed to query script field metadata.");
|
||||
return changed;
|
||||
}
|
||||
|
||||
switch (model.classStatus) {
|
||||
case ::XCEngine::Scripting::ScriptFieldClassStatus::Unassigned:
|
||||
UI::DrawHintText("Assign a C# script to edit serialized fields.");
|
||||
return changed;
|
||||
case ::XCEngine::Scripting::ScriptFieldClassStatus::Missing:
|
||||
UI::DrawHintText("Assigned script class is not available in the loaded script assemblies.");
|
||||
break;
|
||||
case ::XCEngine::Scripting::ScriptFieldClassStatus::Available:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (model.fields.empty()) {
|
||||
UI::DrawHintText(
|
||||
model.classStatus == ::XCEngine::Scripting::ScriptFieldClassStatus::Available
|
||||
? "Selected script exposes no supported public instance fields."
|
||||
: "No serialized script fields are currently available.");
|
||||
return changed;
|
||||
}
|
||||
|
||||
for (const ::XCEngine::Scripting::ScriptFieldSnapshot& field : model.fields) {
|
||||
changed |= RenderScriptField(*scriptComponent, model.classStatus, field, undoManager);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool CanAddTo(::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return gameObject != nullptr;
|
||||
}
|
||||
|
||||
const char* GetAddDisabledReason(::XCEngine::Components::GameObject* gameObject) const override {
|
||||
return gameObject ? nullptr : "Invalid";
|
||||
}
|
||||
|
||||
bool CanRemove(::XCEngine::Components::Component* component) const override {
|
||||
return CanEdit(component);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t kStringBufferSize = 512;
|
||||
|
||||
struct StringFieldEditState {
|
||||
std::array<char, kStringBufferSize> buffer{};
|
||||
std::string lastSyncedValue;
|
||||
bool initialized = false;
|
||||
bool editing = false;
|
||||
};
|
||||
|
||||
bool RenderScriptClassSelector(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
IUndoManager* undoManager) {
|
||||
std::vector<::XCEngine::Scripting::ScriptClassDescriptor> scriptClasses;
|
||||
const bool hasLoadedClasses =
|
||||
::XCEngine::Scripting::ScriptEngine::Get().TryGetAvailableScriptClasses(scriptClasses);
|
||||
|
||||
const ::XCEngine::Scripting::ScriptClassDescriptor currentDescriptor{
|
||||
scriptComponent.GetAssemblyName(),
|
||||
scriptComponent.GetNamespaceName(),
|
||||
scriptComponent.GetClassName()
|
||||
};
|
||||
|
||||
std::string currentLabel = "None";
|
||||
if (scriptComponent.HasScriptClass()) {
|
||||
const auto currentIt = std::find(scriptClasses.begin(), scriptClasses.end(), currentDescriptor);
|
||||
currentLabel = currentIt != scriptClasses.end()
|
||||
? ScriptComponentEditorUtils::BuildScriptClassDisplayName(*currentIt)
|
||||
: ScriptComponentEditorUtils::BuildScriptClassDisplayName(scriptComponent) + " (Missing)";
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
UI::DrawPropertyRow("Script", UI::InspectorPropertyLayout(), [&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
ImGui::SetNextItemWidth(layout.controlWidth);
|
||||
if (!ImGui::BeginCombo("##ScriptClass", currentLabel.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ImGui::Selectable("None", !scriptComponent.HasScriptClass())) {
|
||||
changed |= ApplyScriptClassSelection(scriptComponent, nullptr, undoManager);
|
||||
}
|
||||
|
||||
if (!scriptClasses.empty()) {
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
for (const ::XCEngine::Scripting::ScriptClassDescriptor& descriptor : scriptClasses) {
|
||||
const bool selected = descriptor == currentDescriptor;
|
||||
const std::string label =
|
||||
ScriptComponentEditorUtils::BuildScriptClassDisplayName(descriptor);
|
||||
if (ImGui::Selectable(label.c_str(), selected)) {
|
||||
changed |= ApplyScriptClassSelection(scriptComponent, &descriptor, undoManager);
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasLoadedClasses) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("No script assemblies are currently loaded.");
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!hasLoadedClasses) {
|
||||
const EditorScriptRuntimeStatus& runtimeStatus = Application::Get().GetScriptRuntimeStatus();
|
||||
const std::string hintText =
|
||||
ScriptComponentEditorUtils::BuildScriptRuntimeUnavailableHint(runtimeStatus);
|
||||
UI::DrawHintText(hintText.c_str());
|
||||
|
||||
if (ScriptComponentEditorUtils::CanRebuildScriptAssemblies(runtimeStatus)) {
|
||||
if (UI::InspectorActionButton("Rebuild Scripts", ImVec2(120.0f, 0.0f))) {
|
||||
Application::Get().RebuildScriptingAssemblies();
|
||||
}
|
||||
}
|
||||
|
||||
if (ScriptComponentEditorUtils::CanReloadScriptRuntime(runtimeStatus)) {
|
||||
if (ScriptComponentEditorUtils::CanRebuildScriptAssemblies(runtimeStatus)) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (UI::InspectorActionButton("Reload Scripts", ImVec2(120.0f, 0.0f))) {
|
||||
Application::Get().ReloadScriptingRuntime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool ApplyScriptClassSelection(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptClassDescriptor* descriptor,
|
||||
IUndoManager* undoManager) const {
|
||||
if (!descriptor) {
|
||||
if (!scriptComponent.HasScriptClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (undoManager) {
|
||||
undoManager->BeginInteractiveChange("Modify Script Component");
|
||||
}
|
||||
scriptComponent.ClearScriptClass();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (scriptComponent.GetAssemblyName() == descriptor->assemblyName &&
|
||||
scriptComponent.GetNamespaceName() == descriptor->namespaceName &&
|
||||
scriptComponent.GetClassName() == descriptor->className) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (undoManager) {
|
||||
undoManager->BeginInteractiveChange("Modify Script Component");
|
||||
}
|
||||
scriptComponent.SetScriptClass(
|
||||
descriptor->assemblyName,
|
||||
descriptor->namespaceName,
|
||||
descriptor->className);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderScriptField(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
::XCEngine::Scripting::ScriptFieldClassStatus classStatus,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
IUndoManager* undoManager) {
|
||||
bool changed = false;
|
||||
const bool canEdit = ScriptComponentEditorUtils::CanEditScriptField(classStatus, field);
|
||||
|
||||
if (canEdit) {
|
||||
changed |= RenderScriptFieldEditor(scriptComponent, field, undoManager);
|
||||
} else {
|
||||
RenderReadOnlyScriptField(field);
|
||||
}
|
||||
|
||||
const std::string issueText = ScriptComponentEditorUtils::BuildScriptFieldIssueText(field);
|
||||
if (!issueText.empty()) {
|
||||
UI::DrawHintText(issueText.c_str());
|
||||
}
|
||||
|
||||
if (ScriptComponentEditorUtils::CanClearScriptFieldOverride(field)) {
|
||||
ImGui::PushID((field.metadata.name + "##Reset").c_str());
|
||||
if (UI::InspectorActionButton("Reset", ImVec2(72.0f, 0.0f))) {
|
||||
changed |= ClearScriptFieldOverride(scriptComponent, field, undoManager);
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool RenderScriptFieldEditor(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
IUndoManager* undoManager) {
|
||||
using namespace ::XCEngine::Scripting;
|
||||
|
||||
switch (field.metadata.type) {
|
||||
case ScriptFieldType::Float: {
|
||||
float value = std::get<float>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyFloat(field.metadata.name.c_str(), value, 0.1f);
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::Double: {
|
||||
double value = std::get<double>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
ImGui::SetNextItemWidth(layout.controlWidth);
|
||||
return ImGui::InputDouble("##Value", &value, 0.0, 0.0, "%.3f");
|
||||
});
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::Bool: {
|
||||
bool value = std::get<bool>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyBool(field.metadata.name.c_str(), value);
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::Int32: {
|
||||
int value = std::get<int32_t>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyInt(field.metadata.name.c_str(), value, 1);
|
||||
return widgetChanged &&
|
||||
ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(static_cast<int32_t>(value)), undoManager);
|
||||
}
|
||||
case ScriptFieldType::UInt64: {
|
||||
uint64_t value = std::get<uint64_t>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
ImGui::SetNextItemWidth(layout.controlWidth);
|
||||
return ImGui::InputScalar("##Value", ImGuiDataType_U64, &value);
|
||||
});
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::String:
|
||||
return RenderStringScriptFieldEditor(scriptComponent, field, undoManager);
|
||||
case ScriptFieldType::Vector2: {
|
||||
::XCEngine::Math::Vector2 value = std::get<::XCEngine::Math::Vector2>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyVec2(field.metadata.name.c_str(), value, 0.0f, 0.1f);
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::Vector3: {
|
||||
::XCEngine::Math::Vector3 value = std::get<::XCEngine::Math::Vector3>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyVec3Input(field.metadata.name.c_str(), value, 0.1f);
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::Vector4: {
|
||||
::XCEngine::Math::Vector4 value = std::get<::XCEngine::Math::Vector4>(field.value);
|
||||
const bool widgetChanged = UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
ImGui::SetNextItemWidth(layout.controlWidth);
|
||||
return ImGui::DragFloat4("##Value", &value.x, 0.1f);
|
||||
});
|
||||
return widgetChanged && ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(value), undoManager);
|
||||
}
|
||||
case ScriptFieldType::GameObject:
|
||||
return RenderGameObjectScriptFieldEditor(scriptComponent, field, undoManager);
|
||||
case ScriptFieldType::None:
|
||||
default:
|
||||
RenderReadOnlyScriptField(field);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderStringScriptFieldEditor(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
IUndoManager* undoManager) {
|
||||
StringFieldEditState& editState =
|
||||
m_stringFieldStates[scriptComponent.GetScriptComponentUUID()][field.metadata.name];
|
||||
const std::string currentValue = std::get<std::string>(field.value);
|
||||
|
||||
if (!editState.initialized || (!editState.editing && editState.lastSyncedValue != currentValue)) {
|
||||
SyncStringFieldEditState(editState, currentValue);
|
||||
}
|
||||
|
||||
bool isEditing = false;
|
||||
const bool widgetChanged = UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
ImGui::SetNextItemWidth(layout.controlWidth);
|
||||
const bool changed = ImGui::InputText("##Value", editState.buffer.data(), editState.buffer.size());
|
||||
isEditing = ImGui::IsItemActive();
|
||||
return changed;
|
||||
});
|
||||
editState.editing = isEditing;
|
||||
|
||||
if (!widgetChanged) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string editedValue(editState.buffer.data());
|
||||
if (!ApplyScriptFieldWrite(
|
||||
scriptComponent,
|
||||
field,
|
||||
::XCEngine::Scripting::ScriptFieldValue(editedValue),
|
||||
undoManager)) {
|
||||
SyncStringFieldEditState(editState, currentValue);
|
||||
return false;
|
||||
}
|
||||
|
||||
editState.lastSyncedValue = editedValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderGameObjectScriptFieldEditor(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
IUndoManager* undoManager) {
|
||||
using namespace ::XCEngine::Scripting;
|
||||
|
||||
const GameObjectReference currentReference = std::get<GameObjectReference>(field.value);
|
||||
const auto& sceneRoots = Application::Get().GetEditorContext().GetSceneManager().GetRootEntities();
|
||||
const ::XCEngine::Components::GameObject::ID currentGameObjectId =
|
||||
ScriptComponentEditorUtils::FindGameObjectIdByUuid(sceneRoots, currentReference.gameObjectUUID);
|
||||
|
||||
UI::ReferencePickerOptions pickerOptions;
|
||||
pickerOptions.popupTitle = "Select GameObject";
|
||||
pickerOptions.emptyHint = "None";
|
||||
pickerOptions.searchHint = "Search";
|
||||
pickerOptions.noSceneText = "No scene objects.";
|
||||
pickerOptions.showAssetsTab = false;
|
||||
pickerOptions.showSceneTab = true;
|
||||
|
||||
GameObjectReference pendingReference = currentReference;
|
||||
const bool widgetChanged = UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetFrameHeight());
|
||||
const UI::ReferencePickerInteraction interaction =
|
||||
UI::DrawReferencePickerControl(
|
||||
std::string(),
|
||||
currentGameObjectId,
|
||||
pickerOptions,
|
||||
layout.controlWidth);
|
||||
|
||||
if (interaction.clearRequested) {
|
||||
pendingReference = GameObjectReference{};
|
||||
return true;
|
||||
}
|
||||
|
||||
if (interaction.assignedSceneObjectId == ::XCEngine::Components::GameObject::INVALID_ID ||
|
||||
interaction.assignedSceneObjectId == currentGameObjectId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
::XCEngine::Components::GameObject* assignedGameObject =
|
||||
Application::Get().GetEditorContext().GetSceneManager().GetEntity(interaction.assignedSceneObjectId);
|
||||
if (!assignedGameObject) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pendingReference = GameObjectReference{assignedGameObject->GetUUID()};
|
||||
return true;
|
||||
});
|
||||
|
||||
return widgetChanged &&
|
||||
ApplyScriptFieldWrite(scriptComponent, field, ScriptFieldValue(pendingReference), undoManager);
|
||||
}
|
||||
|
||||
void RenderReadOnlyScriptField(const ::XCEngine::Scripting::ScriptFieldSnapshot& field) const {
|
||||
const std::string valueText = DescribeScriptFieldValue(field.metadata.type, field.value);
|
||||
UI::DrawPropertyRow(
|
||||
field.metadata.name.c_str(),
|
||||
UI::InspectorPropertyLayout(),
|
||||
[&](const UI::PropertyLayoutMetrics& layout) {
|
||||
UI::AlignPropertyControlVertically(layout, ImGui::GetTextLineHeight());
|
||||
ImGui::TextDisabled("%s", valueText.c_str());
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool ApplyScriptFieldWrite(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
const ::XCEngine::Scripting::ScriptFieldValue& value,
|
||||
IUndoManager* undoManager) const {
|
||||
std::vector<::XCEngine::Scripting::ScriptFieldWriteResult> results;
|
||||
if (undoManager) {
|
||||
undoManager->BeginInteractiveChange("Modify Script Field");
|
||||
}
|
||||
|
||||
const bool applied = ::XCEngine::Scripting::ScriptEngine::Get().ApplyScriptFieldWrites(
|
||||
&scriptComponent,
|
||||
{ ::XCEngine::Scripting::ScriptFieldWriteRequest{field.metadata.name, field.metadata.type, value} },
|
||||
results);
|
||||
|
||||
if (!applied || results.empty() ||
|
||||
results.front().status != ::XCEngine::Scripting::ScriptFieldWriteStatus::Applied) {
|
||||
if (undoManager && undoManager->HasPendingInteractiveChange()) {
|
||||
undoManager->CancelInteractiveChange();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ClearScriptFieldOverride(
|
||||
::XCEngine::Scripting::ScriptComponent& scriptComponent,
|
||||
const ::XCEngine::Scripting::ScriptFieldSnapshot& field,
|
||||
IUndoManager* undoManager) const {
|
||||
std::vector<::XCEngine::Scripting::ScriptFieldClearResult> results;
|
||||
if (undoManager) {
|
||||
undoManager->BeginInteractiveChange("Clear Script Field Override");
|
||||
}
|
||||
|
||||
const bool cleared = ::XCEngine::Scripting::ScriptEngine::Get().ClearScriptFieldOverrides(
|
||||
&scriptComponent,
|
||||
{ ::XCEngine::Scripting::ScriptFieldClearRequest{field.metadata.name} },
|
||||
results);
|
||||
|
||||
if (!cleared || results.empty() ||
|
||||
results.front().status != ::XCEngine::Scripting::ScriptFieldClearStatus::Applied) {
|
||||
if (undoManager && undoManager->HasPendingInteractiveChange()) {
|
||||
undoManager->CancelInteractiveChange();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SyncStringFieldEditState(StringFieldEditState& editState, const std::string& value) {
|
||||
editState.buffer.fill('\0');
|
||||
const size_t copyLength = (std::min)(value.size(), editState.buffer.size() - 1);
|
||||
if (copyLength > 0) {
|
||||
std::memcpy(editState.buffer.data(), value.data(), copyLength);
|
||||
}
|
||||
editState.buffer[copyLength] = '\0';
|
||||
editState.lastSyncedValue = value;
|
||||
editState.initialized = true;
|
||||
}
|
||||
|
||||
static std::string DescribeScriptFieldValue(
|
||||
::XCEngine::Scripting::ScriptFieldType type,
|
||||
const ::XCEngine::Scripting::ScriptFieldValue& value) {
|
||||
if (type == ::XCEngine::Scripting::ScriptFieldType::GameObject) {
|
||||
const auto reference = std::get<::XCEngine::Scripting::GameObjectReference>(value);
|
||||
if (reference.gameObjectUUID == 0) {
|
||||
return "None";
|
||||
}
|
||||
|
||||
return "GameObject (" + std::to_string(reference.gameObjectUUID) + ")";
|
||||
}
|
||||
|
||||
return ::XCEngine::Scripting::SerializeScriptFieldValue(type, value);
|
||||
}
|
||||
|
||||
std::unordered_map<uint64_t, std::unordered_map<std::string, StringFieldEditState>> m_stringFieldStates;
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user