Files
FlaxEngine/Source/Engine/UI/UIControl.cpp
Wojtek Figat 8938f13a0b Add support for compiling and running engine without C# scripting
(configurable via `EngineConfiguration.UseCSharp` in Flax.Build)
2021-10-23 16:43:15 +02:00

215 lines
5.9 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "UIControl.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Serialization/Serialization.h"
#if USE_MONO
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
#endif
#if COMPILE_WITHOUT_CSHARP
#define UICONTROL_INVOKE(event)
#else
// Cached methods (FlaxEngine.CSharp.dll is loaded only once)
MMethod* UIControl_Serialize = nullptr;
MMethod* UIControl_SerializeDiff = nullptr;
MMethod* UIControl_Deserialize = nullptr;
MMethod* UIControl_ParentChanged = nullptr;
MMethod* UIControl_TransformChanged = nullptr;
MMethod* UIControl_OrderInParentChanged = nullptr;
MMethod* UIControl_ActiveInTreeChanged = nullptr;
MMethod* UIControl_BeginPlay = nullptr;
MMethod* UIControl_EndPlay = nullptr;
#define UICONTROL_INVOKE(event) \
if (HasManagedInstance()) \
{ \
MObject* exception = nullptr; \
UIControl_##event->Invoke(GetManagedInstance(), nullptr, &exception); \
if (exception) \
{ \
MException ex(exception); \
ex.Log(LogType::Error, TEXT("UICanvas::" #event)); \
} \
}
#endif
UIControl::UIControl(const SpawnParams& params)
: Actor(params)
{
#if !COMPILE_WITHOUT_CSHARP
if (UIControl_Serialize == nullptr)
{
MClass* mclass = GetClass();
UIControl_Serialize = mclass->GetMethod("Serialize", 1);
UIControl_SerializeDiff = mclass->GetMethod("SerializeDiff", 2);
UIControl_Deserialize = mclass->GetMethod("Deserialize", 2);
UIControl_ParentChanged = mclass->GetMethod("ParentChanged");
UIControl_TransformChanged = mclass->GetMethod("TransformChanged");
UIControl_OrderInParentChanged = mclass->GetMethod("OrderInParentChanged");
UIControl_ActiveInTreeChanged = mclass->GetMethod("ActiveInTreeChanged");
UIControl_BeginPlay = mclass->GetMethod("BeginPlay");
UIControl_EndPlay = mclass->GetMethod("EndPlay");
}
#endif
}
#if USE_EDITOR
BoundingBox UIControl::GetEditorBox() const
{
const Vector3 size(50);
return BoundingBox(_transform.Translation - size, _transform.Translation + size);
}
#endif
void UIControl::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
Actor::Serialize(stream, otherObj);
SERIALIZE_GET_OTHER_OBJ(UIControl);
#if !COMPILE_WITHOUT_CSHARP
void* params[2];
MonoString* controlType = nullptr;
params[0] = &controlType;
params[1] = other ? other->GetOrCreateManagedInstance() : nullptr;
MObject* exception = nullptr;
const auto method = other ? UIControl_SerializeDiff : UIControl_Serialize;
const auto invokeResultStr = (MonoString*)method->Invoke(GetOrCreateManagedInstance(), params, &exception);
if (exception)
{
MException ex(exception);
ex.Log(LogType::Error, TEXT("UIControl::Serialize"));
return;
}
// No control
if (!controlType)
{
if (!other)
{
stream.JKEY("Control");
stream.String("", 0);
stream.JKEY("Data");
stream.RawValue("{}", 2);
}
return;
}
const auto controlTypeLength = mono_string_length(controlType);
if (controlTypeLength != 0)
{
stream.JKEY("Control");
const auto controlTypeChars = mono_string_to_utf8(controlType);
stream.String(controlTypeChars);
mono_free(controlTypeChars);
}
stream.JKEY("Data");
const auto invokeResultChars = mono_string_to_utf8(invokeResultStr);
stream.RawValue(invokeResultChars);
mono_free(invokeResultChars);
#endif
}
void UIControl::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
// Base
Actor::Deserialize(stream, modifier);
#if !COMPILE_WITHOUT_CSHARP
MonoReflectionType* typeObj = nullptr;
const auto controlMember = stream.FindMember("Control");
if (controlMember != stream.MemberEnd())
{
const StringAnsiView controlType(controlMember->value.GetStringAnsiView());
const auto type = Scripting::FindClass(controlType);
if (type != nullptr)
{
typeObj = mono_type_get_object(mono_domain_get(), mono_class_get_type(type->GetNative()));
}
else
{
LOG(Warning, "Unknown UIControl type: {0}", String(controlType));
}
}
const auto dataMember = stream.FindMember("Data");
if (dataMember != stream.MemberEnd())
{
rapidjson_flax::StringBuffer buffer;
rapidjson_flax::Writer<rapidjson_flax::StringBuffer> writer(buffer);
dataMember->value.Accept(writer);
const auto str = buffer.GetString();
void* args[2];
args[0] = mono_string_new(mono_domain_get(), str);
args[1] = typeObj;
MObject* exception = nullptr;
UIControl_Deserialize->Invoke(GetOrCreateManagedInstance(), args, &exception);
if (exception)
{
MException ex(exception);
ex.Log(LogType::Error, TEXT("UIControl::Deserialize"));
}
}
#endif
}
void UIControl::OnParentChanged()
{
// Base
Actor::OnParentChanged();
UICONTROL_INVOKE(ParentChanged);
}
void UIControl::OnTransformChanged()
{
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
UICONTROL_INVOKE(TransformChanged);
}
void UIControl::OnBeginPlay()
{
UICONTROL_INVOKE(BeginPlay);
// Base
Actor::OnBeginPlay();
}
void UIControl::OnEndPlay()
{
UICONTROL_INVOKE(EndPlay);
// Base
Actor::OnEndPlay();
}
void UIControl::OnOrderInParentChanged()
{
// Base
Actor::OnOrderInParentChanged();
UICONTROL_INVOKE(OrderInParentChanged);
}
void UIControl::OnActiveInTreeChanged()
{
UICONTROL_INVOKE(ActiveInTreeChanged);
// Base
Actor::OnActiveInTreeChanged();
}