diff --git a/Core/GDCore/Events/Instruction.cpp b/Core/GDCore/Events/Instruction.cpp index fb5ed40f3a..c09117a7c4 100644 --- a/Core/GDCore/Events/Instruction.cpp +++ b/Core/GDCore/Events/Instruction.cpp @@ -55,6 +55,10 @@ void Instruction::SetParameter(std::size_t nb, const gd::Expression& val) { parameters[nb] = val; } +void Instruction::AddParameter(const gd::Expression& val) { + parameters.push_back(val); +} + std::shared_ptr GD_CORE_API CloneRememberingOriginalElement(std::shared_ptr instruction) { std::shared_ptr copy = diff --git a/Core/GDCore/Events/Instruction.h b/Core/GDCore/Events/Instruction.h index d908790a4b..ea8a23472e 100644 --- a/Core/GDCore/Events/Instruction.h +++ b/Core/GDCore/Events/Instruction.h @@ -123,6 +123,11 @@ class GD_CORE_API Instruction { */ void SetParameter(std::size_t nb, const gd::Expression& val); + /** Add a parameter at the end + * \param val The new value of the parameter + */ + void AddParameter(const gd::Expression& val); + /** \brief Get a reference to the std::vector containing the parameters. * \return A std::vector containing the parameters */ diff --git a/Core/GDCore/IDE/PropertyFunctionGenerator.cpp b/Core/GDCore/IDE/PropertyFunctionGenerator.cpp index cf3bbe5bd8..b611dc9065 100644 --- a/Core/GDCore/IDE/PropertyFunctionGenerator.cpp +++ b/Core/GDCore/IDE/PropertyFunctionGenerator.cpp @@ -10,6 +10,7 @@ #include "GDCore/Extensions/Metadata/ValueTypeMetadata.h" #include "GDCore/Extensions/PlatformExtension.h" #include "GDCore/Project/EventsBasedBehavior.h" +#include "GDCore/Project/EventsBasedObject.h" #include "GDCore/Project/EventsFunctionsExtension.h" #include "GDCore/Project/Project.h" #include "GDCore/Project/PropertyDescriptor.h" @@ -17,19 +18,37 @@ namespace gd { -void PropertyFunctionGenerator::GenerateGetterAndSetter( +void PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( gd::Project &project, gd::EventsFunctionsExtension &extension, gd::EventsBasedBehavior &eventsBasedBehavior, const gd::NamedPropertyDescriptor &property, bool isSharedProperties) { + GenerateGetterAndSetter(project, extension, eventsBasedBehavior, property, + eventsBasedBehavior.GetObjectType(), true, + isSharedProperties); +} + +void PropertyFunctionGenerator::GenerateObjectGetterAndSetter( + gd::Project &project, gd::EventsFunctionsExtension &extension, + gd::EventsBasedObject &eventsBasedObject, + const gd::NamedPropertyDescriptor &property) { + GenerateGetterAndSetter(project, extension, eventsBasedObject, property, "", + false, false); +} + +void PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::Project &project, gd::EventsFunctionsExtension &extension, + gd::AbstractEventsBasedEntity &eventsBasedEntity, + const gd::NamedPropertyDescriptor &property, const gd::String &objectType, + bool isBehavior, bool isSharedProperties) { auto &propertyName = property.GetName(); - auto &functionsContainer = eventsBasedBehavior.GetEventsFunctions(); + auto &functionsContainer = eventsBasedEntity.GetEventsFunctions(); gd::String capitalizedName = CapitalizeFirstLetter(property.GetName()); gd::String setterName = "Set" + capitalizedName; gd::String functionGroupName = - (eventsBasedBehavior.GetFullName().empty() - ? eventsBasedBehavior.GetName() - : eventsBasedBehavior.GetFullName()) + + (eventsBasedEntity.GetFullName().empty() + ? eventsBasedEntity.GetName() + : eventsBasedEntity.GetFullName()) + (property.GetGroup().empty() ? "" : " " + UnCapitalizeFirstLetter(property.GetGroup())) + @@ -51,17 +70,14 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( "objects using the behavior." : ""); - gd::String behaviorFullType = gd::PlatformExtension::GetBehaviorFullType( - extension.GetName(), eventsBasedBehavior.GetName()); gd::String propertyGetterName = (isSharedProperties ? "SharedProperty" : "Property") + property.GetName(); gd::String getterType = gd::PlatformExtension::GetBehaviorEventsFunctionFullType( - extension.GetName(), eventsBasedBehavior.GetName(), - propertyGetterName); + extension.GetName(), eventsBasedEntity.GetName(), propertyGetterName); gd::String setterType = gd::PlatformExtension::GetBehaviorEventsFunctionFullType( - extension.GetName(), eventsBasedBehavior.GetName(), + extension.GetName(), eventsBasedEntity.GetName(), "Set" + propertyGetterName); gd::String getterName = capitalizedName; @@ -98,24 +114,24 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( if (property.GetType() == "Boolean") { gd::Instruction condition; condition.SetType(getterType); - condition.SetParametersCount(2); - condition.SetParameter(0, "Object"); - condition.SetParameter(1, "Behavior"); + condition.AddParameter("Object"); + if (isBehavior) { + condition.AddParameter("Behavior"); + } event.GetConditions().Insert(condition, 0); gd::Instruction action; action.SetType("SetReturnBoolean"); - action.SetParametersCount(1); - action.SetParameter(0, "True"); + action.AddParameter("True"); event.GetActions().Insert(action, 0); } else { gd::Instruction action; action.SetType("SetReturn" + numberOrString); - action.SetParametersCount(1); + gd::String receiver = isBehavior ? "Object.Behavior::" : "Object."; gd::String propertyPrefix = (isSharedProperties ? "SharedProperty" : "Property"); - action.SetParameter(0, "Object.Behavior::" + propertyPrefix + - property.GetName() + "()"); + action.AddParameter(receiver + propertyPrefix + property.GetName() + + "()"); event.GetActions().Insert(action, 0); } } @@ -129,25 +145,35 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( .SetGroup(functionGroupName) .SetDescription("Change " + descriptionSubject) .SetSentence("_PARAM0_ " + UnCapitalizeFirstLetter(propertyLabel) + - ": _PARAM2_"); + (isBehavior ? ": _PARAM2_" : ": _PARAM1_")); gd::ParameterMetadata objectParameter; objectParameter.SetType("object") .SetName("Object") .SetDescription("Object") - .SetExtraInfo(eventsBasedBehavior.GetObjectType()); - gd::ParameterMetadata behaviorParameter; - behaviorParameter.SetType("behavior") - .SetName("Behavior") - .SetDescription("Behavior") - .SetExtraInfo(behaviorFullType); + .SetExtraInfo(objectType); + if (!isBehavior) { + gd::String objectFullType = gd::PlatformExtension::GetObjectFullType( + extension.GetName(), eventsBasedEntity.GetName()); + objectParameter.SetExtraInfo(objectFullType); + } + setter.GetParameters().push_back(objectParameter); + if (isBehavior) { + gd::ParameterMetadata behaviorParameter; + gd::String behaviorFullType = + gd::PlatformExtension::GetBehaviorFullType( + extension.GetName(), eventsBasedEntity.GetName()); + behaviorParameter.SetType("behavior") + .SetName("Behavior") + .SetDescription("Behavior") + .SetExtraInfo(behaviorFullType); + setter.GetParameters().push_back(behaviorParameter); + } gd::ParameterMetadata valueParameter; valueParameter.SetType("yesorno") .SetName("Value") .SetDescription(capitalizedName) .SetOptional(true) .SetDefaultValue("yes"); - setter.GetParameters().push_back(objectParameter); - setter.GetParameters().push_back(behaviorParameter); setter.GetParameters().push_back(valueParameter); } else { setter.SetFunctionType(gd::EventsFunction::ActionWithOperator); @@ -162,16 +188,18 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( gd::Instruction condition; condition.SetType("GetArgumentAsBoolean"); - condition.SetParametersCount(1); - condition.SetParameter(0, "\"Value\""); + condition.AddParameter("\"Value\""); event.GetConditions().Insert(condition, 0); gd::Instruction action; action.SetType(setterType); - action.SetParametersCount(3); - action.SetParameter(0, "Object"); - action.SetParameter(1, "Behavior"); - action.SetParameter(2, "yes"); + action.AddParameter("Object"); + if (isBehavior) { + action.AddParameter("Behavior"); + action.AddParameter("yes"); + } else { + action.AddParameter("yes"); + } event.GetActions().Insert(action, 0); } { @@ -181,17 +209,19 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( gd::Instruction condition; condition.SetType("GetArgumentAsBoolean"); - condition.SetParametersCount(1); - condition.SetParameter(0, "\"Value\""); + condition.AddParameter("\"Value\""); condition.SetInverted(true); event.GetConditions().Insert(condition, 0); gd::Instruction action; action.SetType(setterType); - action.SetParametersCount(3); - action.SetParameter(0, "Object"); - action.SetParameter(1, "Behavior"); - action.SetParameter(2, "no"); + action.AddParameter("Object"); + if (isBehavior) { + action.AddParameter("Behavior"); + action.AddParameter("no"); + } else { + action.AddParameter("no"); + } event.GetActions().Insert(action, 0); } } else { @@ -201,18 +231,24 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter( gd::Instruction action; action.SetType(setterType); - action.SetParametersCount(4); - action.SetParameter(0, "Object"); - action.SetParameter(1, "Behavior"); - action.SetParameter(2, "="); - action.SetParameter(3, "GetArgumentAs" + numberOrString + "(\"Value\")"); + action.AddParameter("Object"); + gd::String parameterGetterCall = + "GetArgumentAs" + numberOrString + "(\"Value\")"; + if (isBehavior) { + action.AddParameter("Behavior"); + action.AddParameter("="); + action.AddParameter(parameterGetterCall); + } else { + action.AddParameter("="); + action.AddParameter(parameterGetterCall); + } event.GetActions().Insert(action, 0); } } } bool PropertyFunctionGenerator::CanGenerateGetterAndSetter( - const gd::EventsBasedBehavior &eventsBasedBehavior, + const gd::AbstractEventsBasedEntity &eventsBasedEntity, const gd::NamedPropertyDescriptor &property) { auto &type = property.GetType(); if (type != "Boolean" && type != "Number" && type != "String" && @@ -220,7 +256,7 @@ bool PropertyFunctionGenerator::CanGenerateGetterAndSetter( return false; } - auto &functionsContainer = eventsBasedBehavior.GetEventsFunctions(); + auto &functionsContainer = eventsBasedEntity.GetEventsFunctions(); auto getterName = CapitalizeFirstLetter(property.GetName()); auto setterName = "Set" + getterName; return !functionsContainer.HasEventsFunctionNamed(setterName) && diff --git a/Core/GDCore/IDE/PropertyFunctionGenerator.h b/Core/GDCore/IDE/PropertyFunctionGenerator.h index 7cc3204987..7ca1fb90f3 100644 --- a/Core/GDCore/IDE/PropertyFunctionGenerator.h +++ b/Core/GDCore/IDE/PropertyFunctionGenerator.h @@ -11,6 +11,8 @@ class String; class Project; class EventsFunctionsExtension; class EventsBasedBehavior; +class EventsBasedObject; +class AbstractEventsBasedEntity; class PropertyDescriptor; class NamedPropertyDescriptor; } // namespace gd @@ -23,19 +25,33 @@ namespace gd { class GD_CORE_API PropertyFunctionGenerator { public: /** - * \brief Generate a getter and a setter for the given property. + * \brief Generate a getter and a setter for the given behavior property. */ - static void GenerateGetterAndSetter( + static void GenerateBehaviorGetterAndSetter( gd::Project &project, gd::EventsFunctionsExtension &extension, gd::EventsBasedBehavior &eventsBasedBehavior, const gd::NamedPropertyDescriptor &property, bool isSharedProperties); - static bool - CanGenerateGetterAndSetter(const gd::EventsBasedBehavior &eventsBasedBehavior, - const gd::NamedPropertyDescriptor &property); + /** + * \brief Generate a getter and a setter for the given object property. + */ + static void + GenerateObjectGetterAndSetter(gd::Project &project, + gd::EventsFunctionsExtension &extension, + gd::EventsBasedObject &eventsBasedObject, + const gd::NamedPropertyDescriptor &property); + static bool CanGenerateGetterAndSetter( + const gd::AbstractEventsBasedEntity &eventsBasedEntity, + const gd::NamedPropertyDescriptor &property); ~PropertyFunctionGenerator(); private: + static void GenerateGetterAndSetter( + gd::Project &project, gd::EventsFunctionsExtension &extension, + gd::AbstractEventsBasedEntity &eventsBasedEntity, + const gd::NamedPropertyDescriptor &property, const gd::String &objectType, + bool isBehavior, bool isSharedProperties); + static gd::String CapitalizeFirstLetter(const gd::String &string); static gd::String UnCapitalizeFirstLetter(const gd::String &string); static gd::String diff --git a/Core/tests/PropertyFunctionGenerator.cpp b/Core/tests/PropertyFunctionGenerator.cpp index cf6e81f301..1316079b16 100644 --- a/Core/tests/PropertyFunctionGenerator.cpp +++ b/Core/tests/PropertyFunctionGenerator.cpp @@ -29,10 +29,20 @@ CreateBehavior(gd::EventsFunctionsExtension &eventsExtension) { eventsBasedBehavior.SetObjectType(""); return eventsBasedBehavior; }; + +gd::EventsBasedObject & +CreateObject(gd::EventsFunctionsExtension &eventsExtension) { + auto &eventsBasedObject = eventsExtension.GetEventsBasedObjects().InsertNew( + "MyEventsBasedObject", 0); + eventsBasedObject.SetFullName("My events based object"); + eventsBasedObject.SetDescription("An events based object for test"); + return eventsBasedObject; +}; + } // namespace TEST_CASE("PropertyFunctionGenerator", "[common]") { - SECTION("Can generate functions for a number property") { + SECTION("Can generate functions for a number property in a behavior") { gd::Platform platform; gd::Project project; SetupProjectWithDummyPlatform(project, platform); @@ -47,7 +57,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { .SetDescription("The angle of the trajectory direction.") .SetGroup("Movement"); - gd::PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( project, extension, behavior, property, false); REQUIRE( @@ -119,7 +129,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { } } - SECTION("Can generate functions for a choice property") { + SECTION("Can generate functions for a choice property in a behavior") { gd::Platform platform; gd::Project project; SetupProjectWithDummyPlatform(project, platform); @@ -137,7 +147,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { property.GetExtraInfo().push_back("Dot shape"); property.GetExtraInfo().push_back("Bounding disk"); - gd::PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( project, extension, behavior, property, false); REQUIRE( @@ -155,7 +165,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { "[\"Dot shape\",\"Bounding disk\"]"); } - SECTION("Can generate functions for a boolean property") { + SECTION("Can generate functions for a boolean property in a behavior") { gd::Platform platform; gd::Project project; SetupProjectWithDummyPlatform(project, platform); @@ -170,7 +180,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { "The rotation follows movements done by this behavior only.") .SetGroup("Movement"); - gd::PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( project, extension, behavior, property, false); REQUIRE(behavior.GetEventsFunctions().HasEventsFunctionNamed("Rotate")); @@ -284,6 +294,249 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { REQUIRE(setterYesAction.GetParameter(2).GetPlainString() == "yes"); } } + SECTION("Can generate functions for a number property in an object") { + gd::Platform platform; + gd::Project project; + SetupProjectWithDummyPlatform(project, platform); + auto &extension = + project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0); + auto &object = CreateObject(extension); + + auto &property = + object.GetPropertyDescriptors().InsertNew("MovementAngle", 0); + property.SetType("Number") + .SetLabel("Movement angle") + .SetDescription("The angle of the trajectory direction.") + .SetGroup("Movement"); + + gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter( + project, extension, object, property); + + REQUIRE( + object.GetEventsFunctions().HasEventsFunctionNamed("MovementAngle")); + REQUIRE( + object.GetEventsFunctions().HasEventsFunctionNamed("SetMovementAngle")); + { + auto &getter = + object.GetEventsFunctions().GetEventsFunction("MovementAngle"); + + REQUIRE(getter.GetFunctionType() == + gd::EventsFunction::ExpressionAndCondition); + REQUIRE(getter.GetExpressionType().GetName() == "expression"); + REQUIRE(getter.GetFullName() == "Movement angle"); + REQUIRE(getter.GetGroup() == + "My events based object movement configuration"); + REQUIRE(getter.GetDescription() == + "the movement angle of the object. The " + "angle of the trajectory direction."); + REQUIRE(getter.GetSentence() == "the movement angle"); + // Object parameter is added automatically. + REQUIRE(getter.GetParameters().size() == 0); + + REQUIRE(getter.GetEvents().GetEventsCount() == 1); + REQUIRE(getter.GetEvents().GetEvent(0).GetType() == + "BuiltinCommonInstructions::Standard"); + auto &getterEvent = + dynamic_cast(getter.GetEvents().GetEvent(0)); + REQUIRE(getterEvent.GetConditions().size() == 0); + REQUIRE(getterEvent.GetActions().size() == 1); + auto &getterAction = getterEvent.GetActions().at(0); + REQUIRE(getterAction.GetType() == "SetReturnNumber"); + REQUIRE(getterAction.GetParametersCount() == 1); + REQUIRE(getterAction.GetParameter(0).GetPlainString() == + "Object.PropertyMovementAngle()"); + } + { + auto &setter = + object.GetEventsFunctions().GetEventsFunction("SetMovementAngle"); + + REQUIRE(setter.GetFunctionType() == + gd::EventsFunction::ActionWithOperator); + REQUIRE(setter.GetGetterName() == "MovementAngle"); + // These fields are deducted from the getter. + REQUIRE(setter.GetFullName() == ""); + REQUIRE(setter.GetGroup() == ""); + REQUIRE(setter.GetDescription() == ""); + REQUIRE(setter.GetSentence() == ""); + // Object parameter is added automatically. + REQUIRE(setter.GetParameters().size() == 0); + + REQUIRE(setter.GetEvents().GetEventsCount() == 1); + REQUIRE(setter.GetEvents().GetEvent(0).GetType() == + "BuiltinCommonInstructions::Standard"); + auto &setterEvent = + dynamic_cast(setter.GetEvents().GetEvent(0)); + REQUIRE(setterEvent.GetConditions().size() == 0); + REQUIRE(setterEvent.GetActions().size() == 1); + auto &setterAction = setterEvent.GetActions().at(0); + REQUIRE( + setterAction.GetType() == + "MyEventsExtension::MyEventsBasedObject::SetPropertyMovementAngle"); + REQUIRE(setterAction.GetParametersCount() == 3); + REQUIRE(setterAction.GetParameter(0).GetPlainString() == "Object"); + REQUIRE(setterAction.GetParameter(1).GetPlainString() == "="); + REQUIRE(setterAction.GetParameter(2).GetPlainString() == + "GetArgumentAsNumber(\"Value\")"); + } + } + + SECTION("Can generate functions for a choice property in an object") { + gd::Platform platform; + gd::Project project; + SetupProjectWithDummyPlatform(project, platform); + auto &extension = + project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0); + auto &object = CreateObject(extension); + + auto &property = + object.GetPropertyDescriptors().InsertNew("CollisionShape", 0); + property.SetType("Choice") + .SetLabel("Collision shape") + .SetLabel("Dot shape") + .SetDescription("The shape is used for collision.") + .SetGroup("Movement"); + property.GetExtraInfo().push_back("Dot shape"); + property.GetExtraInfo().push_back("Bounding disk"); + + gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter( + project, extension, object, property); + + REQUIRE( + object.GetEventsFunctions().HasEventsFunctionNamed("CollisionShape")); + REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed( + "SetCollisionShape")); + + auto &getter = + object.GetEventsFunctions().GetEventsFunction("CollisionShape"); + + REQUIRE(getter.GetFunctionType() == + gd::EventsFunction::ExpressionAndCondition); + REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector"); + REQUIRE(getter.GetExpressionType().GetExtraInfo() == + "[\"Dot shape\",\"Bounding disk\"]"); + } + + SECTION("Can generate functions for a boolean property in an object") { + gd::Platform platform; + gd::Project project; + SetupProjectWithDummyPlatform(project, platform); + auto &extension = + project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0); + auto &object = CreateObject(extension); + + auto &property = object.GetPropertyDescriptors().InsertNew("Rotate", 0); + property.SetType("Boolean") + .SetLabel("Rotate object") + .SetDescription("The rotation follows movements done by this object.") + .SetGroup("Movement"); + + gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter( + project, extension, object, property); + + REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("Rotate")); + REQUIRE(object.GetEventsFunctions().HasEventsFunctionNamed("SetRotate")); + { + auto &getter = object.GetEventsFunctions().GetEventsFunction("Rotate"); + REQUIRE(getter.GetFunctionType() == gd::EventsFunction::Condition); + REQUIRE(getter.GetExpressionType().GetName() == "boolean"); + REQUIRE(getter.GetFullName() == "Rotate object"); + REQUIRE(getter.GetGroup() == + "My events based object movement configuration"); + REQUIRE(getter.GetDescription() == + "Check if rotate object. The rotation follows movements done by " + "this object."); + REQUIRE(getter.GetSentence() == "_PARAM0_ rotate object"); + // The Object parameter is added automatically. + REQUIRE(getter.GetParameters().size() == 0); + + REQUIRE(getter.GetEvents().GetEventsCount() == 1); + REQUIRE(getter.GetEvents().GetEvent(0).GetType() == + "BuiltinCommonInstructions::Standard"); + auto &getterEvent = + dynamic_cast(getter.GetEvents().GetEvent(0)); + REQUIRE(getterEvent.GetConditions().size() == 1); + REQUIRE(getterEvent.GetActions().size() == 1); + + auto &getterCondition = getterEvent.GetConditions().at(0); + REQUIRE(getterCondition.GetType() == + "MyEventsExtension::MyEventsBasedObject::PropertyRotate"); + REQUIRE(!getterCondition.IsInverted()); + REQUIRE(getterCondition.GetParametersCount() == 1); + REQUIRE(getterCondition.GetParameter(0).GetPlainString() == "Object"); + + auto &getterAction = getterEvent.GetActions().at(0); + REQUIRE(getterAction.GetType() == "SetReturnBoolean"); + REQUIRE(getterAction.GetParametersCount() == 1); + REQUIRE(getterAction.GetParameter(0).GetPlainString() == "True"); + } + { + auto &setter = object.GetEventsFunctions().GetEventsFunction("SetRotate"); + + REQUIRE(setter.GetFunctionType() == gd::EventsFunction::Action); + REQUIRE(setter.GetFullName() == "Rotate object"); + REQUIRE(setter.GetGroup() == + "My events based object movement configuration"); + REQUIRE(setter.GetDescription() == + "Change if rotate object. The rotation follows movements done by " + "this object."); + REQUIRE(setter.GetSentence() == "_PARAM0_ rotate object: _PARAM1_"); + // To generate the value parameter, the object parameter has to + // be declared too. + REQUIRE(setter.GetParameters().size() == 2); + auto &objectParameter = setter.GetParameters().at(0); + REQUIRE(objectParameter.GetName() == "Object"); + REQUIRE(objectParameter.GetType() == "object"); + REQUIRE(objectParameter.GetExtraInfo() == + "MyEventsExtension::MyEventsBasedObject"); + auto &valueParameter = setter.GetParameters().at(1); + REQUIRE(valueParameter.GetName() == "Value"); + REQUIRE(valueParameter.GetType() == "yesorno"); + + REQUIRE(setter.GetEvents().GetEventsCount() == 2); + REQUIRE(setter.GetEvents().GetEvent(0).GetType() == + "BuiltinCommonInstructions::Standard"); + REQUIRE(setter.GetEvents().GetEvent(1).GetType() == + "BuiltinCommonInstructions::Standard"); + + auto &setterNoEvent = + dynamic_cast(setter.GetEvents().GetEvent(0)); + REQUIRE(setterNoEvent.GetConditions().size() == 1); + REQUIRE(setterNoEvent.GetActions().size() == 1); + + auto &setterNoCondition = setterNoEvent.GetConditions().at(0); + REQUIRE(setterNoCondition.GetType() == "GetArgumentAsBoolean"); + REQUIRE(setterNoCondition.IsInverted()); + REQUIRE(setterNoCondition.GetParametersCount() == 1); + REQUIRE(setterNoCondition.GetParameter(0).GetPlainString() == + "\"Value\""); + + auto &setterNoAction = setterNoEvent.GetActions().at(0); + REQUIRE(setterNoAction.GetType() == + "MyEventsExtension::MyEventsBasedObject::SetPropertyRotate"); + REQUIRE(setterNoAction.GetParametersCount() == 2); + REQUIRE(setterNoAction.GetParameter(0).GetPlainString() == "Object"); + REQUIRE(setterNoAction.GetParameter(1).GetPlainString() == "no"); + + auto &setterYesEvent = + dynamic_cast(setter.GetEvents().GetEvent(1)); + REQUIRE(setterYesEvent.GetConditions().size() == 1); + REQUIRE(setterYesEvent.GetActions().size() == 1); + + auto &setterYesCondition = setterYesEvent.GetConditions().at(0); + REQUIRE(setterYesCondition.GetType() == "GetArgumentAsBoolean"); + REQUIRE(!setterYesCondition.IsInverted()); + REQUIRE(setterYesCondition.GetParametersCount() == 1); + REQUIRE(setterYesCondition.GetParameter(0).GetPlainString() == + "\"Value\""); + + auto &setterYesAction = setterYesEvent.GetActions().at(0); + REQUIRE(setterYesAction.GetType() == + "MyEventsExtension::MyEventsBasedObject::SetPropertyRotate"); + REQUIRE(setterYesAction.GetParametersCount() == 2); + REQUIRE(setterYesAction.GetParameter(0).GetPlainString() == "Object"); + REQUIRE(setterYesAction.GetParameter(1).GetPlainString() == "yes"); + } + } SECTION("Can generate functions for a shared property") { gd::Platform platform; @@ -300,7 +553,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { .SetDescription("The angle of the trajectory direction.") .SetGroup("Movement"); - gd::PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( project, extension, behavior, property, true); REQUIRE( @@ -464,7 +717,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") { behavior.GetPropertyDescriptors().InsertNew("MovementAngle", 0); property.SetType("Number"); - gd::PropertyFunctionGenerator::GenerateGetterAndSetter( + gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter( project, extension, behavior, property, false); REQUIRE( diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 4797c47178..c4f91f8554 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -2195,8 +2195,9 @@ interface WholeProjectRefactorer { }; interface PropertyFunctionGenerator { - void STATIC_GenerateGetterAndSetter([Ref] Project project, [Ref] EventsFunctionsExtension extension, [Ref] EventsBasedBehavior eventsBasedBehavior, [Const, Ref] NamedPropertyDescriptor property, boolean isSharedProperties); - boolean STATIC_CanGenerateGetterAndSetter([Const, Ref] EventsBasedBehavior eventsBasedBehavior, [Const, Ref] NamedPropertyDescriptor property); + void STATIC_GenerateBehaviorGetterAndSetter([Ref] Project project, [Ref] EventsFunctionsExtension extension, [Ref] EventsBasedBehavior eventsBasedBehavior, [Const, Ref] NamedPropertyDescriptor property, boolean isSharedProperties); + void STATIC_GenerateObjectGetterAndSetter([Ref] Project project, [Ref] EventsFunctionsExtension extension, [Ref] EventsBasedObject eventsBasedObject, [Const, Ref] NamedPropertyDescriptor property); + boolean STATIC_CanGenerateGetterAndSetter([Const, Ref] AbstractEventsBasedEntity eventsBasedBehavior, [Const, Ref] NamedPropertyDescriptor property); }; interface UsedExtensionsFinder { @@ -2391,6 +2392,14 @@ interface EventsFunctionsContainer { unsigned long GetEventsFunctionPosition([Const, Ref] EventsFunction eventsFunction); }; +interface AbstractEventsBasedEntity { + [Ref] EventsFunctionsContainer GetEventsFunctions(); + [Ref] NamedPropertyDescriptorsList GetPropertyDescriptors(); + + void SerializeTo([Ref] SerializerElement element); + void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element); +}; + interface EventsBasedBehavior { void EventsBasedBehavior(); @@ -2405,13 +2414,8 @@ interface EventsBasedBehavior { [Ref] EventsBasedBehavior SetPrivate(boolean isPrivate); boolean IsPrivate(); - [Ref] EventsFunctionsContainer GetEventsFunctions(); - [Ref] NamedPropertyDescriptorsList GetPropertyDescriptors(); [Ref] NamedPropertyDescriptorsList GetSharedPropertyDescriptors(); - void SerializeTo([Ref] SerializerElement element); - void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element); - [Const, Value] DOMString STATIC_GetPropertyActionName([Const] DOMString propertyName); [Const, Value] DOMString STATIC_GetPropertyConditionName([Const] DOMString propertyName); [Const, Value] DOMString STATIC_GetPropertyExpressionName([Const] DOMString propertyName); @@ -2419,6 +2423,7 @@ interface EventsBasedBehavior { [Const, Value] DOMString STATIC_GetSharedPropertyConditionName([Const] DOMString propertyName); [Const, Value] DOMString STATIC_GetSharedPropertyExpressionName([Const] DOMString propertyName); }; +EventsBasedBehavior implements AbstractEventsBasedEntity; interface EventsBasedBehaviorsList { [Ref] EventsBasedBehavior InsertNew([Const] DOMString name, unsigned long pos); @@ -2447,17 +2452,26 @@ interface EventsBasedObject { [Ref] EventsBasedObject SetDefaultName([Const] DOMString defaultName); [Const, Ref] DOMString GetDefaultName(); - [Ref] EventsFunctionsContainer GetEventsFunctions(); - [Ref] NamedPropertyDescriptorsList GetPropertyDescriptors(); - - void SerializeTo([Ref] SerializerElement element); - void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element); - [Const, Value] DOMString STATIC_GetPropertyActionName([Const] DOMString propertyName); [Const, Value] DOMString STATIC_GetPropertyConditionName([Const] DOMString propertyName); [Const, Value] DOMString STATIC_GetPropertyExpressionName([Const] DOMString propertyName); + + // Inherited from gd::ObjectsContainer + [Ref] gdObject InsertNewObject([Ref] Project project, [Const] DOMString type, [Const] DOMString name, unsigned long pos); + [Ref] gdObject InsertObject([Const, Ref] gdObject obj, unsigned long pos); + boolean HasObjectNamed([Const] DOMString name); + [Ref] gdObject GetObject([Const] DOMString name); + [Ref] gdObject GetObjectAt(unsigned long pos); + unsigned long GetObjectPosition([Const] DOMString name); + void RemoveObject([Const] DOMString name); + void SwapObjects(unsigned long first, unsigned long second); + void MoveObject(unsigned long oldIndex, unsigned long newIndex); + void MoveObjectToAnotherContainer([Const] DOMString name, [Ref] ObjectsContainer newObjectsContainer, unsigned long newPosition); + unsigned long GetObjectsCount(); + + [Ref] ObjectGroupsContainer GetObjectGroups(); }; -EventsBasedObject implements ObjectsContainer; +EventsBasedObject implements AbstractEventsBasedEntity; interface EventsBasedObjectsList { [Ref] EventsBasedObject InsertNew([Const] DOMString name, unsigned long pos); diff --git a/GDevelop.js/Bindings/Wrapper.cpp b/GDevelop.js/Bindings/Wrapper.cpp index eb1fbffd45..2fcc699944 100644 --- a/GDevelop.js/Bindings/Wrapper.cpp +++ b/GDevelop.js/Bindings/Wrapper.cpp @@ -600,7 +600,8 @@ typedef ExtensionAndMetadata ExtensionAndExpressionMetadata; GetBehaviorsWithType #define STATIC_FixInvalidRequiredBehaviorProperties \ FixInvalidRequiredBehaviorProperties -#define STATIC_GenerateGetterAndSetter GenerateGetterAndSetter +#define STATIC_GenerateBehaviorGetterAndSetter GenerateBehaviorGetterAndSetter +#define STATIC_GenerateObjectGetterAndSetter GenerateObjectGetterAndSetter #define STATIC_CanGenerateGetterAndSetter CanGenerateGetterAndSetter #define STATIC_CreateRectangle CreateRectangle #define STATIC_SanityCheckBehaviorProperty SanityCheckBehaviorProperty diff --git a/GDevelop.js/types/gdabstracteventsbasedentity.js b/GDevelop.js/types/gdabstracteventsbasedentity.js new file mode 100644 index 0000000000..e7b37bb45c --- /dev/null +++ b/GDevelop.js/types/gdabstracteventsbasedentity.js @@ -0,0 +1,9 @@ +// Automatically generated by GDevelop.js/scripts/generate-types.js +declare class gdAbstractEventsBasedEntity { + getEventsFunctions(): gdEventsFunctionsContainer; + getPropertyDescriptors(): gdNamedPropertyDescriptorsList; + serializeTo(element: gdSerializerElement): void; + unserializeFrom(project: gdProject, element: gdSerializerElement): void; + delete(): void; + ptr: number; +}; \ No newline at end of file diff --git a/GDevelop.js/types/gdeventsbasedbehavior.js b/GDevelop.js/types/gdeventsbasedbehavior.js index 5126402451..f59eacb87c 100644 --- a/GDevelop.js/types/gdeventsbasedbehavior.js +++ b/GDevelop.js/types/gdeventsbasedbehavior.js @@ -1,5 +1,5 @@ // Automatically generated by GDevelop.js/scripts/generate-types.js -declare class gdEventsBasedBehavior { +declare class gdEventsBasedBehavior extends gdAbstractEventsBasedEntity { constructor(): void; setDescription(description: string): gdEventsBasedBehavior; getDescription(): string; @@ -11,11 +11,7 @@ declare class gdEventsBasedBehavior { getObjectType(): string; setPrivate(isPrivate: boolean): gdEventsBasedBehavior; isPrivate(): boolean; - getEventsFunctions(): gdEventsFunctionsContainer; - getPropertyDescriptors(): gdNamedPropertyDescriptorsList; getSharedPropertyDescriptors(): gdNamedPropertyDescriptorsList; - serializeTo(element: gdSerializerElement): void; - unserializeFrom(project: gdProject, element: gdSerializerElement): void; static getPropertyActionName(propertyName: string): string; static getPropertyConditionName(propertyName: string): string; static getPropertyExpressionName(propertyName: string): string; diff --git a/GDevelop.js/types/gdeventsbasedobject.js b/GDevelop.js/types/gdeventsbasedobject.js index 81c512abf6..621724dde1 100644 --- a/GDevelop.js/types/gdeventsbasedobject.js +++ b/GDevelop.js/types/gdeventsbasedobject.js @@ -1,5 +1,5 @@ // Automatically generated by GDevelop.js/scripts/generate-types.js -declare class gdEventsBasedObject extends gdObjectsContainer { +declare class gdEventsBasedObject extends gdAbstractEventsBasedEntity { constructor(): void; setDescription(description: string): gdEventsBasedObject; getDescription(): string; @@ -9,13 +9,21 @@ declare class gdEventsBasedObject extends gdObjectsContainer { getFullName(): string; setDefaultName(defaultName: string): gdEventsBasedObject; getDefaultName(): string; - getEventsFunctions(): gdEventsFunctionsContainer; - getPropertyDescriptors(): gdNamedPropertyDescriptorsList; - serializeTo(element: gdSerializerElement): void; - unserializeFrom(project: gdProject, element: gdSerializerElement): void; static getPropertyActionName(propertyName: string): string; static getPropertyConditionName(propertyName: string): string; static getPropertyExpressionName(propertyName: string): string; + insertNewObject(project: gdProject, type: string, name: string, pos: number): gdObject; + insertObject(obj: gdObject, pos: number): gdObject; + hasObjectNamed(name: string): boolean; + getObject(name: string): gdObject; + getObjectAt(pos: number): gdObject; + getObjectPosition(name: string): number; + removeObject(name: string): void; + swapObjects(first: number, second: number): void; + moveObject(oldIndex: number, newIndex: number): void; + moveObjectToAnotherContainer(name: string, newObjectsContainer: gdObjectsContainer, newPosition: number): void; + getObjectsCount(): number; + getObjectGroups(): gdObjectGroupsContainer; delete(): void; ptr: number; }; \ No newline at end of file diff --git a/GDevelop.js/types/gdpropertyfunctiongenerator.js b/GDevelop.js/types/gdpropertyfunctiongenerator.js index 4808353d73..6c3ecfd9f0 100644 --- a/GDevelop.js/types/gdpropertyfunctiongenerator.js +++ b/GDevelop.js/types/gdpropertyfunctiongenerator.js @@ -1,7 +1,8 @@ // Automatically generated by GDevelop.js/scripts/generate-types.js declare class gdPropertyFunctionGenerator { - static generateGetterAndSetter(project: gdProject, extension: gdEventsFunctionsExtension, eventsBasedBehavior: gdEventsBasedBehavior, property: gdNamedPropertyDescriptor, isSharedProperties: boolean): void; - static canGenerateGetterAndSetter(eventsBasedBehavior: gdEventsBasedBehavior, property: gdNamedPropertyDescriptor): boolean; + static generateBehaviorGetterAndSetter(project: gdProject, extension: gdEventsFunctionsExtension, eventsBasedBehavior: gdEventsBasedBehavior, property: gdNamedPropertyDescriptor, isSharedProperties: boolean): void; + static generateObjectGetterAndSetter(project: gdProject, extension: gdEventsFunctionsExtension, eventsBasedObject: gdEventsBasedObject, property: gdNamedPropertyDescriptor): void; + static canGenerateGetterAndSetter(eventsBasedBehavior: gdAbstractEventsBasedEntity, property: gdNamedPropertyDescriptor): boolean; delete(): void; ptr: number; }; \ No newline at end of file diff --git a/GDevelop.js/types/libgdevelop.js b/GDevelop.js/types/libgdevelop.js index d180aa3aeb..bc418f5aa3 100644 --- a/GDevelop.js/types/libgdevelop.js +++ b/GDevelop.js/types/libgdevelop.js @@ -172,6 +172,7 @@ declare class libGDevelop { EventsFunction_FunctionType: Class; EventsFunction: Class; EventsFunctionsContainer: Class; + AbstractEventsBasedEntity: Class; EventsBasedBehavior: Class; EventsBasedBehaviorsList: Class; EventsBasedObject: Class; diff --git a/newIDE/app/src/EventsBasedBehaviorEditor/EventsBasedBehaviorPropertiesEditor.js b/newIDE/app/src/EventsBasedBehaviorEditor/EventsBasedBehaviorPropertiesEditor.js index 06c03a5998..6764f48e35 100644 --- a/newIDE/app/src/EventsBasedBehaviorEditor/EventsBasedBehaviorPropertiesEditor.js +++ b/newIDE/app/src/EventsBasedBehaviorEditor/EventsBasedBehaviorPropertiesEditor.js @@ -230,7 +230,7 @@ export default class EventsBasedBehaviorPropertiesEditor extends React.Component { label: i18n._(t`Generate expression and action`), click: () => - gd.PropertyFunctionGenerator.generateGetterAndSetter( + gd.PropertyFunctionGenerator.generateBehaviorGetterAndSetter( this.props.project, this.props.extension, this.props.eventsBasedBehavior, diff --git a/newIDE/app/src/EventsBasedObjectEditor/EventBasedObjectChildrenEditor.js b/newIDE/app/src/EventsBasedObjectEditor/EventBasedObjectChildrenEditor.js index c2e8b39e95..2d7e900155 100644 --- a/newIDE/app/src/EventsBasedObjectEditor/EventBasedObjectChildrenEditor.js +++ b/newIDE/app/src/EventsBasedObjectEditor/EventBasedObjectChildrenEditor.js @@ -61,6 +61,7 @@ export default class EventBasedObjectChildrenEditor extends React.Component< project, eventsBasedObject, globalObjectsContainer, + // $FlowFixMe gdObjectsContainer should be a member of gdEventsBasedObject instead of a base class. eventsBasedObject, object.getName(), /* isObjectGroup=*/ false, @@ -208,6 +209,7 @@ export default class EventBasedObjectChildrenEditor extends React.Component< ObjectsRenderingService )} project={project} + // $FlowFixMe gdObjectsContainer should be a member of gdEventsBasedObject instead of a base class. objectsContainer={eventsBasedObject} layout={null} // TODO EBO Allow to use project resources as place holders diff --git a/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectEditorDialog.js b/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectEditorDialog.js index 680d0127d4..9f9591eee5 100644 --- a/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectEditorDialog.js +++ b/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectEditorDialog.js @@ -75,6 +75,7 @@ export default function EventsBasedObjectEditorDialog({ {currentTab === 'properties' && ( diff --git a/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectPropertiesEditor.js b/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectPropertiesEditor.js index af7db12e25..805d188f0a 100644 --- a/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectPropertiesEditor.js +++ b/newIDE/app/src/EventsBasedObjectEditor/EventsBasedObjectPropertiesEditor.js @@ -30,6 +30,7 @@ const gd: libGDevelop = global.gd; type Props = {| project: gdProject, + extension: gdEventsFunctionsExtension, eventsBasedObject: gdEventsBasedObject, onPropertiesUpdated?: () => void, onRenameProperty: (oldName: string, newName: string) => void, @@ -231,6 +232,20 @@ export default class EventsBasedObjectPropertiesEditor extends React.Component< click: () => this._moveProperty(i, i + 1), enabled: i + 1 < properties.getCount(), }, + { + label: i18n._(t`Generate expression and action`), + click: () => + gd.PropertyFunctionGenerator.generateObjectGetterAndSetter( + this.props.project, + this.props.extension, + this.props.eventsBasedObject, + property + ), + enabled: gd.PropertyFunctionGenerator.canGenerateGetterAndSetter( + this.props.eventsBasedObject, + property + ), + }, ]} />