Files
UnrealEngine/Engine/Source/Editor/EditorConfig/Public/EditorConfig.h
2025-05-18 13:04:45 +08:00

241 lines
7.4 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Concepts/EqualityComparable.h"
#include "Containers/StringFwd.h"
#include "Containers/StringView.h"
#include "Delegates/Delegate.h"
#include "Dom/JsonObject.h"
#include "HAL/Platform.h"
#include "JsonConfig.h"
#include "Misc/AssertionMacros.h"
#include "Templates/IsPointer.h"
#include "Templates/Models.h"
#include "Templates/SharedPointer.h"
#include "Templates/UnrealTemplate.h"
#include "Templates/UnrealTypeTraits.h"
class FArrayProperty;
class FJsonObject;
class FJsonValue;
class FMapProperty;
class FProperty;
class FSetProperty;
class FString;
class UClass;
class UObject;
class UStruct;
class EDITORCONFIG_API FEditorConfig
{
public:
enum class EPropertyFilter
{
All,
MetadataOnly
};
FEditorConfig();
void SetParent(TSharedPtr<FEditorConfig> InConfig);
bool LoadFromString(FStringView Content);
bool LoadFromFile(FStringView FilePath);
bool SaveToString(FString& OutResult) const;
bool IsValid() const { return JsonConfig.IsValid() && JsonConfig->IsValid(); }
// UStruct & UObject
template <typename T>
bool TryGetStruct(FStringView Key, T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
template <typename T>
bool TryGetUObject(FStringView Key, T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
template <typename T>
bool TryGetRootStruct(T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
template <typename T>
bool TryGetRootUObject(T& OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
template <typename T>
bool TryGetRootUObject(T* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
bool TryGetRootStruct(const UStruct* Class, void* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
bool TryGetRootUObject(const UClass* Class, UObject* OutValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly) const;
template <typename T>
void SetStruct(FStringView Key, const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
template <typename T>
void SetUObject(FStringView Key, const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
template <typename T>
void SetRootStruct(const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
template <typename T>
void SetRootUObject(const T& InValue, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
void SetRootStruct(const UStruct* Class, const void* Instance, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
void SetRootUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter = EPropertyFilter::MetadataOnly);
bool HasOverride(FStringView Key) const;
void OnSaved();
DECLARE_EVENT_OneParam(FEditorConfig, FOnEditorConfigDirtied, const FEditorConfig&);
FOnEditorConfigDirtied& OnEditorConfigDirtied() { return EditorConfigDirtiedEvent; }
private:
friend class UEditorConfigSubsystem; // for access to LoadFromFile and SaveToFile
static void ReadUObject(const TSharedPtr<FJsonObject>& JsonObject, const UClass* Class, UObject* Instance, EPropertyFilter Filter);
static void ReadStruct(const TSharedPtr<FJsonObject>& JsonObject, const UStruct* Struct, void* Instance, UObject* Owner, EPropertyFilter Filter);
static void ReadValue(const TSharedPtr<FJsonValue>& JsonValue, const FProperty* Property, void* DataPtr, UObject* Owner);
static TSharedPtr<FJsonObject> WriteStruct(const UStruct* Struct, const void* Instance, const void* Defaults, EPropertyFilter Filter);
static TSharedPtr<FJsonObject> WriteUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter);
static TSharedPtr<FJsonValue> WriteArray(const FArrayProperty* ArrayProperty, const void* DataPtr);
static TSharedPtr<FJsonValue> WriteSet(const FSetProperty* Property, const void* DataPtr);
static TSharedPtr<FJsonValue> WriteMap(const FMapProperty* Property, const void* DataPtr);
static TSharedPtr<FJsonValue> WriteValue(const FProperty* Property, const void* DataPtr, const void* Defaults);
bool SaveToFile(FStringView FilePath) const;
void SetDirty();
private:
TSharedPtr<UE::FJsonConfig> JsonConfig;
TSharedPtr<FEditorConfig> ParentConfig;
FOnEditorConfigDirtied EditorConfigDirtiedEvent;
bool Dirty { false };
};
template <typename T>
bool FEditorConfig::TryGetStruct(FStringView Key, T& OutValue, EPropertyFilter Filter) const
{
if (!IsValid())
{
return false;
}
TSharedPtr<FJsonObject> StructData;
UE::FJsonPath Path(Key);
if (!JsonConfig->TryGetJsonObject(Path, StructData))
{
return false;
}
if (!StructData.IsValid())
{
return false;
}
const UStruct* Struct = T::StaticStruct();
ReadStruct(StructData, Struct, &OutValue, nullptr, Filter);
return true;
}
template <typename T>
bool FEditorConfig::TryGetUObject(FStringView Key, T& OutValue, EPropertyFilter Filter) const
{
static_assert(TIsDerivedFrom<T, UObject>::Value, "Type is not derived from UObject.");
if (!IsValid())
{
return false;
}
TSharedPtr<FJsonObject> UObjectData;
UE::FJsonPath Path(Key);
if (!JsonConfig->TryGetJsonObject(Path, UObjectData))
{
return false;
}
if (!UObjectData.IsValid())
{
return false;
}
const UClass* Class = T::StaticClass();
ReadUObject(UObjectData, Class, &OutValue, Filter);
return true;
}
template <typename T>
bool FEditorConfig::TryGetRootStruct(T& OutValue, EPropertyFilter Filter) const
{
return TryGetRootStruct(T::StaticStruct(), &OutValue, Filter);
}
template <typename T>
bool FEditorConfig::TryGetRootUObject(T& OutValue, EPropertyFilter Filter) const
{
static_assert(TIsDerivedFrom<T, UObject>::Value, "Type is not derived from UObject.");
return TryGetRootUObject(T::StaticClass(), &OutValue, Filter);
}
template <typename T>
bool FEditorConfig::TryGetRootUObject(T* OutValue, EPropertyFilter Filter) const
{
static_assert(TIsDerivedFrom<T, UObject>::Value, "Type is not derived from UObject.");
checkf(OutValue != nullptr, TEXT("Output value was null."));
return TryGetRootUObject(T::StaticClass(), OutValue, Filter);
}
template <typename T>
void FEditorConfig::SetStruct(FStringView Key, const T& InValue, EPropertyFilter Filter)
{
if (!IsValid())
{
return;
}
TSharedPtr<FJsonObject> JsonObject = WriteStruct(T::StaticStruct(), &InValue, nullptr, Filter);
JsonConfig->SetJsonObject(UE::FJsonPath(Key), JsonObject);
SetDirty();
}
template <typename T>
void FEditorConfig::SetUObject(FStringView Key, const T& InValue, EPropertyFilter Filter)
{
static_assert(TIsDerivedFrom<T, UObject>::Value, "Type is not derived from UObject.");
if (!IsValid())
{
return;
}
TSharedPtr<FJsonObject> JsonObject = WriteUObject(T::StaticClass(), &InValue);
JsonConfig->SetJsonObject(UE::FJsonPath(Key), JsonObject);
SetDirty();
}
template <typename T>
void FEditorConfig::SetRootStruct(const T& InValue, EPropertyFilter Filter)
{
SetRootStruct(T::StaticStruct(), &InValue, Filter);
}
template <typename T>
void FEditorConfig::SetRootUObject(const T& InValue, EPropertyFilter Filter)
{
if constexpr (TIsPointer<T>::Value)
{
static_assert(TIsDerivedFrom<typename TRemovePointer<T>::Type, UObject>::Value, "Type is not derived from UObject.");
checkf(InValue != nullptr, TEXT("Object value was null."));
SetRootUObject(TRemovePointer<T>::Type::StaticClass(), InValue, Filter);
}
else
{
static_assert(TIsDerivedFrom<typename TRemovePointer<T>::Type, UObject>::Value, "Type is not derived from UObject.");
SetRootUObject(T::StaticClass(), &InValue, Filter);
}
}