Compare commits

...

28 Commits

Author SHA1 Message Date
Davy Hélard
55fc686a5b Fix expression default type. 2022-10-29 22:02:37 +02:00
Davy Hélard
5580189f88 Remove unused imports. 2022-10-29 20:49:29 +02:00
Davy Hélard
9a6d1d6d32 Remove useless lines added by mistake. 2022-10-29 19:42:54 +02:00
Davy Hélard
6c9739c01d Fix object parameter extraInfo declaration. 2022-10-29 19:41:07 +02:00
Davy Hélard
cd3c997b28 Review changes: more renaming 2022-10-29 17:53:15 +02:00
Davy Hélard
8064c4de57 Review changes: renaming 2022-10-29 14:54:09 +02:00
Davy Hélard
8666851f54 Documentation. 2022-10-29 13:40:53 +02:00
Davy Hélard
6d568b2f2c Fix type conversion to expressionType. 2022-10-29 13:40:52 +02:00
Davy Hélard
180d4318aa Add ValueTypeEditor to edit type definitions. 2022-10-29 13:40:52 +02:00
Davy Hélard
65a57f86da Allow to define type extraInfo for operands. 2022-10-29 13:40:52 +02:00
Davy Hélard
6a3e7f9c58 Define expression types with ValueTypeMetadata. 2022-10-29 13:40:52 +02:00
Davy Hélard
74f1d571ba Extract ValueTypeMetadata from ParameterMetadata. 2022-10-29 13:40:51 +02:00
Davy Hélard
a7cb3fc5a2 Add some comment about parameter types. 2022-10-29 13:40:51 +02:00
Davy Hélard
08d3c3323a Move out changes to mapFor. 2022-10-29 13:40:51 +02:00
Davy Hélard
fec603b811 Format 2022-10-29 13:40:51 +02:00
Davy Hélard
d68affc117 Add tests for shiftSentenceParamIndexes 2022-10-29 13:40:50 +02:00
Davy Hélard
77cd6c44d6 Review change: memory leak in test. 2022-10-29 13:40:50 +02:00
Davy Hélard
2b4c8813e4 Review changes 2022-10-29 13:40:50 +02:00
Davy Hélard
2ef9266ec4 Disable the "Add parameter" button for ActionWithOperator. 2022-10-29 13:40:49 +02:00
Davy Hélard
ceba6cf739 Split function types and returned types in 2 drop-down lists. 2022-10-29 13:40:49 +02:00
Davy Hélard
18f2085de7 Better disable parameters in UI. 2022-10-29 13:40:49 +02:00
Davy Hélard
b586fb87ed Show the right parameters in the editor. 2022-10-29 13:40:49 +02:00
Davy Hélard
8aed02ab17 Allow event extensions to define an action with an operator. 2022-10-29 13:40:48 +02:00
Davy Hélard
8c383fc448 Cleanup stories imports. 2022-10-29 13:40:48 +02:00
Davy Hélard
af3a2016f2 Fix flow. 2022-10-29 13:40:48 +02:00
Davy Hélard
7a20161794 Use the icon of expressions. 2022-10-29 13:40:47 +02:00
Davy Hélard
0feb4ef321 Put operator and operand first. 2022-10-29 13:40:47 +02:00
Davy Hélard
5cbcd16523 Allow event extensions to define a condition with an operator from an expression. 2022-10-29 13:40:47 +02:00
60 changed files with 2656 additions and 760 deletions

View File

@@ -44,8 +44,10 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
info.SetExtraInfo(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +

View File

@@ -222,6 +222,18 @@ class GD_CORE_API ExpressionMetadata {
return *this;
};
/**
* \brief Set the additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter), for the last added parameter.
*
* \see AddParameter
*/
ExpressionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
return *this;
}
/**
* \brief Mark this (object) expression as requiring the specified capability,
* offered by the base object.
@@ -256,7 +268,30 @@ class GD_CORE_API ExpressionMetadata {
*/
ExpressionCodeGenerationInformation& GetCodeExtraInformation() {
return codeExtraInformation;
};
}
/**
* \brief Erase any existing include file and add the specified include.
*/
ExpressionMetadata &SetIncludeFile(const gd::String &includeFile) {
codeExtraInformation.SetIncludeFile(includeFile);
return *this;
}
/**
* \brief Add a file to the already existing include files.
*/
ExpressionMetadata &AddIncludeFile(const gd::String &includeFile) {
codeExtraInformation.AddIncludeFile(includeFile);
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
return codeExtraInformation.GetIncludeFiles();
}
ExpressionCodeGenerationInformation codeExtraInformation;

View File

@@ -62,8 +62,10 @@ InstructionMetadata& InstructionMetadata::AddParameter(
info.SetExtraInfo(
// For objects/behavior, the supplementary information
// parameter is an object/behavior type...
(gd::ParameterMetadata::IsObject(type) ||
((gd::ParameterMetadata::IsObject(type) ||
gd::ParameterMetadata::IsBehavior(type))
// Prefix with the namespace if it's not already there.
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
? (supplementaryInformation.empty()
? ""
: extensionNamespace +
@@ -92,8 +94,10 @@ InstructionMetadata& InstructionMetadata::AddCodeOnlyParameter(
}
InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
const gd::String& type, const gd::String& typeExtraInfo) {
const gd::String& expressionValueType =
gd::ValueTypeMetadata::GetPrimitiveValueType(type);
SetManipulatedType(expressionValueType);
if (type == "boolean") {
AddParameter("yesorno", _("New value"));
@@ -117,8 +121,8 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
"_PARAM" + gd::String::From(valueParamIndex) + "_");
}
} else {
AddParameter("operator", _("Modification's sign"), type);
AddParameter(type == "number" ? "expression" : type, _("Value"));
AddParameter("operator", _("Modification's sign"), expressionValueType);
AddParameter(type, _("Value"), typeExtraInfo);
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;
@@ -151,8 +155,10 @@ InstructionMetadata& InstructionMetadata::UseStandardOperatorParameters(
InstructionMetadata&
InstructionMetadata::UseStandardRelationalOperatorParameters(
const gd::String& type) {
SetManipulatedType(type);
const gd::String& type, const gd::String& typeExtraInfo) {
const gd::String& expressionValueType =
gd::ValueTypeMetadata::GetPrimitiveValueType(type);
SetManipulatedType(expressionValueType);
if (type == "boolean") {
if (isObjectInstruction || isBehaviorInstruction) {
@@ -168,8 +174,8 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
templateSentence.FindAndReplace("<subject>", sentence);
}
} else {
AddParameter("relationalOperator", _("Sign of the test"), type);
AddParameter(type == "number" ? "expression" : type, _("Value to compare"));
AddParameter("relationalOperator", _("Sign of the test"), expressionValueType);
AddParameter(type, _("Value to compare"), typeExtraInfo);
size_t operatorParamIndex = parameters.size() - 2;
size_t valueParamIndex = parameters.size() - 1;

View File

@@ -206,7 +206,7 @@ class GD_CORE_API InstructionMetadata {
if (!parameters.empty())
parameters.back().SetLongDescription(longDescription);
return *this;
};
}
/**
* \brief Set the additional information, used for some parameters
@@ -218,20 +218,26 @@ class GD_CORE_API InstructionMetadata {
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) {
if (!parameters.empty()) parameters.back().SetExtraInfo(extraInfo);
return *this;
};
}
/**
* \brief Add the default parameters for an instruction manipulating the
* specified type ("string", "number") with the default operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardOperatorParameters(const gd::String &type);
InstructionMetadata &UseStandardOperatorParameters(const gd::String &type, const gd::String& typeExtraInfo = "");
/**
* \brief Add the default parameters for an instruction comparing the
* specified type ("string", "number") with the default relational operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardRelationalOperatorParameters(
const gd::String &type);
const gd::String &type, const gd::String& typeExtraInfo = "");
/**
* \brief Mark the instruction as an object instruction. Automatically called
@@ -276,7 +282,7 @@ class GD_CORE_API InstructionMetadata {
*/
const gd::String &GetRequiredBaseObjectCapability() const {
return requiredBaseObjectCapability;
};
}
/**
* \brief Consider that the instruction is easy for a user to understand.
@@ -487,6 +493,29 @@ class GD_CORE_API InstructionMetadata {
return codeExtraInformation.SetAsyncFunctionName(functionName);
}
/**
* \brief Erase any existing include file and add the specified include.
*/
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) {
codeExtraInformation.SetIncludeFile(includeFile);
return *this;
}
/**
* \brief Add a file to the already existing include files.
*/
InstructionMetadata &AddIncludeFile(const gd::String &includeFile) {
codeExtraInformation.AddIncludeFile(includeFile);
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
return codeExtraInformation.GetIncludeFiles();
};
std::vector<ParameterMetadata> parameters;
private:

View File

@@ -80,6 +80,16 @@ class GD_CORE_API MultipleInstructionMetadata {
return *this;
};
/**
* \see gd::InstructionMetadata::SetParameterExtraInfo
*/
MultipleInstructionMetadata &SetParameterExtraInfo(const gd::String &defaultValue) {
if (expression) expression->SetParameterExtraInfo(defaultValue);
if (condition) condition->SetParameterExtraInfo(defaultValue);
if (action) action->SetParameterExtraInfo(defaultValue);
return *this;
};
/**
* \see gd::InstructionMetadata::SetParameterLongDescription
*/
@@ -116,9 +126,9 @@ class GD_CORE_API MultipleInstructionMetadata {
* \see gd::InstructionMetadata::UseStandardOperatorParameters
* \see gd::InstructionMetadata::UseStandardRelationalOperatorParameters
*/
MultipleInstructionMetadata &UseStandardParameters(const gd::String &type) {
if (condition) condition->UseStandardRelationalOperatorParameters(type);
if (action) action->UseStandardOperatorParameters(type);
MultipleInstructionMetadata &UseStandardParameters(const gd::String &type, const gd::String& typeExtraInfo = "") {
if (condition) condition->UseStandardRelationalOperatorParameters(type, typeExtraInfo);
if (action) action->UseStandardOperatorParameters(type, typeExtraInfo);
return *this;
}
@@ -154,6 +164,34 @@ class GD_CORE_API MultipleInstructionMetadata {
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String>& GetIncludeFiles() const {
if (expression)
return expression->GetCodeExtraInformation().GetIncludeFiles();
if (condition)
return condition->GetCodeExtraInformation().GetIncludeFiles();
if (action)
return action->GetCodeExtraInformation().GetIncludeFiles();
// It can't actually happen.
throw std::logic_error("no instruction metadata");
}
/**
* Set that the instruction is private - it can't be used outside of the
* object/ behavior that it is attached too.
*/
MultipleInstructionMetadata &SetPrivate() {
if (expression)
expression->SetPrivate();
if (condition)
condition->SetPrivate();
if (action)
action->SetPrivate();
return *this;
}
/**
* \see gd::InstructionMetadata::MarkAsSimple
*/

View File

@@ -10,12 +10,10 @@
namespace gd {
ParameterMetadata::ParameterMetadata() : optional(false), codeOnly(false) {}
ParameterMetadata::ParameterMetadata() : codeOnly(false) {}
void ParameterMetadata::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", type);
element.SetAttribute("supplementaryInformation", supplementaryInformation);
element.SetAttribute("optional", optional);
valueTypeMetadata.SerializeTo(element);
element.SetAttribute("description", description);
element.SetAttribute("longDescription", longDescription);
element.SetAttribute("codeOnly", codeOnly);
@@ -24,10 +22,7 @@ void ParameterMetadata::SerializeTo(SerializerElement& element) const {
}
void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
type = element.GetStringAttribute("type");
supplementaryInformation =
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
valueTypeMetadata.UnserializeFrom(element);
description = element.GetStringAttribute("description");
longDescription = element.GetStringAttribute("longDescription");
codeOnly = element.GetBoolAttribute("codeOnly");
@@ -35,18 +30,4 @@ void ParameterMetadata::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("name");
}
// TODO factorize in a file with an enum and helpers?
const gd::String ParameterMetadata::numberType = "number";
const gd::String ParameterMetadata::stringType = "string";
const gd::String &ParameterMetadata::GetExpressionValueType(const gd::String &parameterType) {
if (parameterType == "number" || gd::ParameterMetadata::IsExpression("number", parameterType)) {
return ParameterMetadata::numberType;
}
if (parameterType == "string" || gd::ParameterMetadata::IsExpression("string", parameterType)) {
return ParameterMetadata::stringType;
}
return parameterType;
}
} // namespace gd

View File

@@ -6,16 +6,13 @@
#ifndef PARAMETER_METADATA_H
#define PARAMETER_METADATA_H
#if defined(GD_IDE_ONLY)
#include <map>
#include <memory>
#include "GDCore/String.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
namespace gd {
class Project;
class Layout;
class EventsCodeGenerator;
class EventsCodeGenerationContext;
class SerializerElement;
} // namespace gd
@@ -32,17 +29,32 @@ class GD_CORE_API ParameterMetadata {
ParameterMetadata();
virtual ~ParameterMetadata(){};
/**
* \brief Return the metadata of the parameter type.
*/
gd::ValueTypeMetadata &GetValueTypeMetadata() { return valueTypeMetadata; }
/**
* \brief Set the metadata of the parameter type.
*/
ParameterMetadata &SetValueTypeMetadata(const gd::ValueTypeMetadata &valueTypeMetadata_) {
valueTypeMetadata = valueTypeMetadata_;
return *this;
}
/**
* \brief Return the type of the parameter.
* \see gd::ParameterMetadata::IsObject
* \deprecated Use gd::ValueTypeMetadata instead.
*/
const gd::String &GetType() const { return type; }
const gd::String &GetType() const { return valueTypeMetadata.GetName(); }
/**
* \brief Set the type of the parameter.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetType(const gd::String &type_) {
type = type_;
valueTypeMetadata.SetName(type_);
return *this;
}
@@ -71,29 +83,33 @@ class GD_CORE_API ParameterMetadata {
* \brief Return an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
* \deprecated Use gd::ValueTypeMetadata instead.
*/
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
const gd::String &GetExtraInfo() const { return valueTypeMetadata.GetExtraInfo(); }
/**
* \brief Set an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
supplementaryInformation = supplementaryInformation_;
valueTypeMetadata.SetExtraInfo(supplementaryInformation_);
return *this;
}
/**
* \brief Return true if the parameter is optional.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
bool IsOptional() const { return optional; }
bool IsOptional() const { return valueTypeMetadata.IsOptional(); }
/**
* \brief Set if the parameter is optional.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
ParameterMetadata &SetOptional(bool optional_ = true) {
optional = optional_;
valueTypeMetadata.SetOptional(optional_);
return *this;
}
@@ -151,26 +167,27 @@ class GD_CORE_API ParameterMetadata {
return *this;
}
// TODO Remove these deprecated functions.
/**
* \brief Return true if the type of the parameter is representing one object
* (or more, i.e: an object group).
*
* \see gd::ParameterMetadata::GetType
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
parameterType == "objectList" ||
parameterType == "objectListOrEmptyIfJustDeclared" ||
parameterType == "objectListOrEmptyWithoutPicking";
return gd::ValueTypeMetadata::IsTypeObject(parameterType);
}
/**
* \brief Return true if the type of the parameter is "behavior".
*
* \see gd::ParameterMetadata::GetType
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsBehavior(const gd::String &parameterType) {
return parameterType == "behavior";
return gd::ValueTypeMetadata::IsTypeBehavior(parameterType);
}
/**
@@ -179,43 +196,22 @@ class GD_CORE_API ParameterMetadata {
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static bool IsExpression(const gd::String &type,
const gd::String &parameterType) {
if (type == "number") {
return parameterType == "expression" || parameterType == "camera" ||
parameterType == "forceMultiplier";
} else if (type == "string") {
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName" ||
parameterType == "layerEffectName" ||
parameterType == "layerEffectParameterName" ||
parameterType == "objectEffectName" ||
parameterType == "objectEffectParameterName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "identifier";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
}
return false;
return gd::ValueTypeMetadata::IsTypeExpression(type, parameterType);
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
* \deprecated Use gd::ValueTypeMetadata instead.
*/
static const gd::String &GetExpressionValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
static const gd::String &GetExpressionValueType(const gd::String &parameterType) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(parameterType);
}
/** \name Serialization
*/
@@ -238,9 +234,7 @@ class GD_CORE_API ParameterMetadata {
bool codeOnly; ///< True if parameter is relative to code generation only,
///< i.e. must not be shown in editor
private:
gd::String type; ///< Parameter type
gd::String supplementaryInformation; ///< Used if needed
bool optional; ///< True if the parameter is optional
gd::ValueTypeMetadata valueTypeMetadata; ///< Parameter type
gd::String longDescription; ///< Long description shown in the editor.
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
@@ -250,5 +244,4 @@ class GD_CORE_API ParameterMetadata {
} // namespace gd
#endif
#endif // PARAMETER_METADATA_H

View File

@@ -0,0 +1,49 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "ValueTypeMetadata.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
ValueTypeMetadata::ValueTypeMetadata() : optional(false) {}
void ValueTypeMetadata::SerializeTo(SerializerElement& element) const {
element.SetAttribute("type", name);
if (!supplementaryInformation.empty()) {
element.SetAttribute("supplementaryInformation", supplementaryInformation);
}
if (optional) {
element.SetAttribute("optional", optional);
}
if (!defaultValue.empty()) {
element.SetAttribute("defaultValue", defaultValue);
}
}
void ValueTypeMetadata::UnserializeFrom(const SerializerElement& element) {
name = element.GetStringAttribute("type");
supplementaryInformation =
element.GetStringAttribute("supplementaryInformation");
optional = element.GetBoolAttribute("optional");
defaultValue = element.GetStringAttribute("defaultValue");
}
const gd::String ValueTypeMetadata::numberType = "number";
const gd::String ValueTypeMetadata::stringType = "string";
const gd::String &ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "number" || gd::ValueTypeMetadata::IsTypeExpression("number", parameterType)) {
return ValueTypeMetadata::numberType;
}
if (parameterType == "string" || gd::ValueTypeMetadata::IsTypeExpression("string", parameterType)) {
return ValueTypeMetadata::stringType;
}
return parameterType;
}
} // namespace gd

View File

@@ -0,0 +1,219 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef VALUE_TYPE_METADATA_H
#define VALUE_TYPE_METADATA_H
#include <map>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
} // namespace gd
namespace gd {
/**
* \brief Define a type for parameters of a function (action, condition or
* expression) or the returned value of an expression.
*
* \see gd::EventsFunction
* \ingroup Events
*/
class GD_CORE_API ValueTypeMetadata {
public:
ValueTypeMetadata();
virtual ~ValueTypeMetadata(){};
/**
* \brief Return the string representation of the type.
*/
const gd::String &GetName() const { return name; }
/**
* \brief Set the string representation of the type.
*/
ValueTypeMetadata &SetName(const gd::String &name_) {
name = name_;
return *this;
}
/**
* \brief Return an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
const gd::String &GetExtraInfo() const { return supplementaryInformation; }
/**
* \brief Set an optional additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter).
*/
ValueTypeMetadata &SetExtraInfo(const gd::String &supplementaryInformation_) {
supplementaryInformation = supplementaryInformation_;
return *this;
}
/**
* \brief Return true if the parameter is optional.
*/
bool IsOptional() const { return optional; }
/**
* \brief Set if the parameter is optional.
*/
ValueTypeMetadata &SetOptional(bool optional_ = true) {
optional = optional_;
return *this;
}
/**
* \brief Get the default value for the parameter.
*/
const gd::String &GetDefaultValue() const { return defaultValue; }
/**
* \brief Set the default value, if the parameter is optional.
*/
ValueTypeMetadata &SetDefaultValue(const gd::String &defaultValue_) {
defaultValue = defaultValue_;
return *this;
}
/**
* \brief Return true if the type is defined.
*/
bool IsDefined() const {
return !name.empty();
}
/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
*/
bool IsObject() const {
return gd::ValueTypeMetadata::IsTypeObject(name);
}
/**
* \brief Return true if the type is "behavior".
*/
bool IsBehavior() const {
return gd::ValueTypeMetadata::IsTypeBehavior(name);
}
/**
* \brief Return true if the type is an expression of the
* given type.
*/
bool IsNumber() const {
return gd::ValueTypeMetadata::IsTypeExpression("number", name);
}
/**
* \brief Return true if the type is a string.
*/
bool IsString() const {
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
}
/**
* \brief Return true if the type of the parameter is a number.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
bool IsVariable() const {
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
}
/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
*/
static bool IsTypeObject(const gd::String &parameterType) {
return parameterType == "object" || parameterType == "objectPtr" ||
parameterType == "objectList" ||
parameterType == "objectListOrEmptyIfJustDeclared" ||
parameterType == "objectListOrEmptyWithoutPicking";
}
/**
* \brief Return true if the type is "behavior".
*/
static bool IsTypeBehavior(const gd::String &parameterType) {
return parameterType == "behavior";
}
/**
* \brief Return true if the type is an expression of the given type.
* \note If you are adding a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
*/
static bool IsTypeExpression(const gd::String &type,
const gd::String &parameterType) {
if (type == "number") {
return parameterType == "number" || parameterType == "expression" ||
parameterType == "camera" || parameterType == "forceMultiplier";
} else if (type == "string") {
return parameterType == "string" || parameterType == "layer" ||
parameterType == "color" || parameterType == "file" ||
parameterType == "joyaxis" ||
parameterType == "stringWithSelector" ||
parameterType == "sceneName" ||
parameterType == "layerEffectName" ||
parameterType == "layerEffectParameterName" ||
parameterType == "objectEffectName" ||
parameterType == "objectEffectParameterName" ||
parameterType == "objectPointName" ||
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "identifier";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
}
return false;
}
/**
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*/
static const gd::String &GetPrimitiveValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
/** \name Serialization
*/
///@{
/**
* \brief Serialize the ParameterMetadata to the specified element
*/
void SerializeTo(gd::SerializerElement &element) const;
/**
* \brief Load the ParameterMetadata from the specified element
*/
void UnserializeFrom(const gd::SerializerElement &element);
///@}
private:
gd::String name; ///< Parameter type
gd::String supplementaryInformation; ///< Used if needed
bool optional; ///< True if the parameter is optional
gd::String defaultValue; ///< Used as a default value in editor or if an
///< optional parameter is empty.
};
} // namespace gd
#endif // VALUE_TYPE_METADATA_H

View File

@@ -20,6 +20,7 @@ namespace gd {
void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer) {
@@ -31,8 +32,12 @@ void EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
// to parameters
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.GetObjectGroups().Clear();
auto &parameters = eventsFunction.GetParametersForEvents(functionContainer);
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), outputObjectsContainer);
project,
parameters,
outputObjectsContainer);
outputObjectsContainer.GetObjectGroups() = eventsFunction.GetObjectGroups();
}
@@ -44,6 +49,7 @@ void EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project,
eventsBasedBehavior.GetEventsFunctions(),
eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer);
@@ -81,6 +87,7 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
gd::ObjectsContainer& outputObjectsContainer) {
// The context is build the same way as free function...
FreeEventsFunctionToObjectsContainer(project,
eventsBasedObject.GetEventsFunctions(),
eventsFunction,
outputGlobalObjectsContainer,
outputObjectsContainer);

View File

@@ -10,6 +10,7 @@
#include "GDCore/String.h"
namespace gd {
class Project;
class EventsFunctionsContainer;
class ObjectsContainer;
class ParameterMetadata;
class EventsFunction;
@@ -34,6 +35,7 @@ class GD_CORE_API EventsFunctionTools {
*/
static void FreeEventsFunctionToObjectsContainer(
const gd::Project& project,
const gd::EventsFunctionsContainer functionContainer,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputGlobalObjectsContainer,
gd::ObjectsContainer& outputObjectsContainer);

View File

@@ -154,7 +154,11 @@ void WholeProjectRefactorer::ExposeProjectEvents(
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, *eventsFunction, globalObjectsAndGroups, objectsAndGroups);
project,
eventsFunctionsExtension,
*eventsFunction,
globalObjectsAndGroups,
objectsAndGroups);
worker.Launch(eventsFunction->GetEvents(),
globalObjectsAndGroups,
@@ -334,8 +338,10 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
[&project, &oldName, &newName](
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the extension name
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(oldName,
@@ -345,11 +351,6 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsBasedBehavior.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the extension name
}
};
@@ -394,8 +395,10 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
[&project, &oldName, &newName](
const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the extension name
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(oldName,
@@ -405,11 +408,6 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsBasedObject.GetName(),
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the extension name
}
};
@@ -456,9 +454,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// Free expressions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameEventsFunction(*eventsFunction);
}
}
@@ -467,9 +463,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
@@ -477,8 +471,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// Free instructions
for (auto&& eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameEventsFunction(*eventsFunction);
}
}
@@ -488,8 +481,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedBehaviors().GetInternalVector()) {
auto& behaviorEventsFunctions = eventsBasedBehavior->GetEventsFunctions();
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameBehaviorEventsFunction(*eventsBasedBehavior, *eventsFunction);
}
}
@@ -510,8 +502,7 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
auto& objectEventsFunctions = eventsBasedObject->GetEventsFunctions();
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameObjectEventsFunction(*eventsBasedObject, *eventsFunction);
}
}
@@ -563,6 +554,16 @@ void WholeProjectRefactorer::RenameEventsFunction(
oldFunctionName),
GetEventsFunctionFullType(eventsFunctionsExtension.GetName(),
newFunctionName));
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsFunctionsExtension.GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
void WholeProjectRefactorer::RenameBehaviorEventsFunction(
@@ -577,8 +578,20 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(oldFunctionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(eventsFunctionsExtension.GetName(),
@@ -588,18 +601,15 @@ void WholeProjectRefactorer::RenameBehaviorEventsFunction(
eventsBasedBehavior.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedBehaviorExpression(
GetBehaviorFullType(eventsFunctionsExtension.GetName(),
eventsBasedBehavior.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsBasedBehavior.GetEventsFunctions().GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
@@ -615,8 +625,17 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
const gd::EventsFunction& eventsFunction =
eventsFunctions.GetEventsFunction(oldFunctionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedObjectExpression(
GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(eventsFunctionsExtension.GetName(),
@@ -626,18 +645,15 @@ void WholeProjectRefactorer::RenameObjectEventsFunction(
eventsBasedObject.GetName(),
newFunctionName));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedObjectExpression(
GetObjectFullType(eventsFunctionsExtension.GetName(),
eventsBasedObject.GetName()),
oldFunctionName,
newFunctionName);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.GetFunctionType() == gd::EventsFunction::ExpressionAndCondition) {
for (auto&& otherFunction : eventsBasedObject.GetEventsFunctions().GetInternalVector())
{
if (otherFunction->GetFunctionType() == gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
otherFunction->SetGetterName(newFunctionName);
}
}
}
}
@@ -655,21 +671,22 @@ void WholeProjectRefactorer::MoveEventsFunctionParameter(
const gd::String& eventsFunctionType = GetEventsFunctionFullType(
eventsFunctionsExtension.GetName(), functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetFreeExpressionMovedParameter(
eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
@@ -690,15 +707,7 @@ void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
eventsBasedBehavior.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetBehaviorExpressionMovedParameter(
@@ -709,6 +718,15 @@ void WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
@@ -729,15 +747,7 @@ void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
eventsBasedObject.GetName(),
functionName);
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project, eventsFunctionType, oldIndex, newIndex);
ExposeProjectEvents(project, mover);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction.IsExpression()) {
gd::ExpressionsParameterMover mover =
gd::ExpressionsParameterMover(project.GetCurrentPlatform());
mover.SetObjectExpressionMovedParameter(
@@ -748,6 +758,15 @@ void WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
newIndex);
ExposeProjectEvents(project, mover);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
const int operatorIndexOffset = eventsFunction.IsExpression() ? 2 : 0;
gd::InstructionsParameterMover mover = gd::InstructionsParameterMover(
project,
eventsFunctionType,
oldIndex + operatorIndexOffset,
newIndex + operatorIndexOffset);
ExposeProjectEvents(project, mover);
}
}
void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
@@ -1088,8 +1107,11 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
&eventsFunctionsExtension,
&oldBehaviorName,
&newBehaviorName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// behavior
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetBehaviorEventsFunctionFullType(
@@ -1101,12 +1123,6 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
newBehaviorName,
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the name of the
// behavior
}
};
@@ -1151,17 +1167,14 @@ void WholeProjectRefactorer::RenameEventsBasedBehavior(
// Behavior expressions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
// Behavior instructions
for (auto&& eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameBehaviorEventsFunction(*eventsFunction);
}
}
@@ -1197,8 +1210,11 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
&eventsFunctionsExtension,
&oldObjectName,
&newObjectName](const gd::EventsFunction& eventsFunction) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction.IsExpression()) {
// Nothing to do, expressions are not including the name of the
// object
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer = gd::InstructionsTypeRenamer(
project,
GetObjectEventsFunctionFullType(
@@ -1210,12 +1226,6 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
newObjectName,
eventsFunction.GetName()));
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Nothing to do, expressions are not including the name of the
// object
}
};
@@ -1260,17 +1270,14 @@ void WholeProjectRefactorer::RenameEventsBasedObject(
// Object expressions
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Expression ||
eventsFunction->GetFunctionType() ==
gd::EventsFunction::StringExpression) {
if (eventsFunction->IsExpression()) {
renameObjectEventsFunction(*eventsFunction);
}
}
// Object instructions
for (auto&& eventsFunction : objectEventsFunctions.GetInternalVector()) {
if (eventsFunction->GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction->GetFunctionType() == gd::EventsFunction::Condition) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameObjectEventsFunction(*eventsFunction);
}
}
@@ -1292,20 +1299,20 @@ void WholeProjectRefactorer::DoRenameEventsFunction(
const gd::EventsFunction& eventsFunction,
const gd::String& oldFullType,
const gd::String& newFullType) {
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Action ||
eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
gd::InstructionsTypeRenamer renamer =
gd::InstructionsTypeRenamer(project, oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression ||
eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
// Order is important: we first rename the expressions then the instructions,
// to avoid being unable to fetch the metadata (the types of parameters) of
// instructions after they are renamed.
if (eventsFunction.IsExpression()) {
gd::ExpressionsRenamer renamer =
gd::ExpressionsRenamer(project.GetCurrentPlatform());
renamer.SetReplacedFreeExpression(oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
}
if (eventsFunction.IsAction() || eventsFunction.IsCondition()) {
gd::InstructionsTypeRenamer renamer =
gd::InstructionsTypeRenamer(project, oldFullType, newFullType);
ExposeProjectEvents(project, renamer);
}
}
void WholeProjectRefactorer::DoRenameBehavior(

View File

@@ -10,8 +10,10 @@
namespace gd {
AbstractEventsBasedEntity::AbstractEventsBasedEntity(const gd::String& _name)
: name(_name), fullName("") {}
AbstractEventsBasedEntity::AbstractEventsBasedEntity(
const gd::String& _name,
gd::EventsFunctionsContainer::FunctionOwner functionContainerSource)
: name(_name), fullName(""), eventsFunctionsContainer(functionContainerSource) {}
void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);

View File

@@ -29,7 +29,9 @@ namespace gd {
*/
class GD_CORE_API AbstractEventsBasedEntity {
public:
AbstractEventsBasedEntity(const gd::String& _name);
AbstractEventsBasedEntity(
const gd::String& _name,
gd::EventsFunctionsContainer::FunctionOwner functionContainerSource);
virtual ~AbstractEventsBasedEntity(){};
/**

View File

@@ -11,7 +11,9 @@
namespace gd {
EventsBasedBehavior::EventsBasedBehavior()
: AbstractEventsBasedEntity("MyBehavior") {}
: AbstractEventsBasedEntity(
"MyBehavior",
gd::EventsFunctionsContainer::FunctionOwner::Behavior) {}
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
AbstractEventsBasedEntity::SerializeTo(element);

View File

@@ -10,7 +10,10 @@
namespace gd {
EventsBasedObject::EventsBasedObject()
: AbstractEventsBasedEntity("MyObject"), ObjectsContainer() {
: AbstractEventsBasedEntity(
"MyObject",
gd::EventsFunctionsContainer::FunctionOwner::Object),
ObjectsContainer() {
}
EventsBasedObject::~EventsBasedObject() {}

View File

@@ -7,10 +7,54 @@
#include "EventsFunction.h"
#include <vector>
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
namespace gd {
EventsFunction::EventsFunction() : functionType(Action) {}
EventsFunction::EventsFunction() : functionType(Action) {
expressionType.SetName("expression");
}
const std::vector<gd::ParameterMetadata>& EventsFunction::GetParametersForEvents(
const gd::EventsFunctionsContainer& functionsContainer) const {
if (functionType != FunctionType::ActionWithOperator) {
// For most function types, the parameters are specified in the function.
return parameters;
}
// For ActionWithOperator, the parameters are auto generated.
actionWithOperationParameters.clear();
if (!functionsContainer.HasEventsFunctionNamed(getterName)) {
return actionWithOperationParameters;
}
const auto& expression = functionsContainer.GetEventsFunction(getterName);
const auto& expressionParameters = expression.parameters;
const auto functionsSource = functionsContainer.GetOwner();
const int expressionValueParameterIndex =
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Behavior ?
2 :
functionsSource == gd::EventsFunctionsContainer::FunctionOwner::Object ?
1 :
0;
for (size_t i = 0;
i < expressionValueParameterIndex && i < expressionParameters.size();
i++)
{
actionWithOperationParameters.push_back(expressionParameters[i]);
}
gd::ParameterMetadata parameterMetadata;
parameterMetadata.SetName("Value").SetValueTypeMetadata(expression.expressionType);
actionWithOperationParameters.push_back(parameterMetadata);
for (size_t i = expressionValueParameterIndex;
i < expressionParameters.size();
i++)
{
actionWithOperationParameters.push_back(expressionParameters[i]);
}
return actionWithOperationParameters;
}
void EventsFunction::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", name);
@@ -18,18 +62,33 @@ void EventsFunction::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);
element.SetAttribute("sentence", sentence);
element.SetAttribute("group", group);
element.SetAttribute("getterName", getterName);
element.SetBoolAttribute("private", isPrivate);
events.SerializeTo(element.AddChild("events"));
gd::String functionTypeStr = "Action";
if (functionType == Condition)
functionTypeStr = "Condition";
else if (functionType == Expression)
else if (functionType == Expression) {
functionTypeStr = "Expression";
else if (functionType == StringExpression)
functionTypeStr = "StringExpression";
// Compatibility code for version 5.1.147 and older.
// There is no longer distinction between number and string in the function
// type directly. The expression type is now used for this.
if (expressionType.IsString()) {
functionTypeStr = "StringExpression";
}
}
else if (functionType == ExpressionAndCondition) {
functionTypeStr = "ExpressionAndCondition";
}
else if (functionType == ActionWithOperator)
functionTypeStr = "ActionWithOperator";
element.SetAttribute("functionType", functionTypeStr);
if (this->IsExpression()) {
expressionType.SerializeTo(element.AddChild("expressionType"));
}
gd::SerializerElement& parametersElement = element.AddChild("parameters");
parametersElement.ConsiderAsArrayOf("parameter");
for (const auto& parameter : parameters) {
@@ -46,16 +105,32 @@ void EventsFunction::UnserializeFrom(gd::Project& project,
description = element.GetStringAttribute("description");
sentence = element.GetStringAttribute("sentence");
group = element.GetStringAttribute("group");
getterName = element.GetStringAttribute("getterName");
isPrivate = element.GetBoolAttribute("private");
events.UnserializeFrom(project, element.GetChild("events"));
gd::String functionTypeStr = element.GetStringAttribute("functionType");
if (functionTypeStr == "Condition")
functionType = Condition;
else if (functionTypeStr == "Expression")
else if (functionTypeStr == "Expression" || functionTypeStr == "StringExpression") {
functionType = Expression;
else if (functionTypeStr == "StringExpression")
functionType = StringExpression;
if (element.HasChild("expressionType")) {
expressionType.UnserializeFrom(element.GetChild("expressionType"));
}
else {
// Compatibility code for version 5.1.147 and older.
// There is no longer distinction between number and string in the function
// type directly. The expression type is now used for this.
expressionType.SetName(functionTypeStr == "StringExpression" ? "string" : "expression");
}
}
else if (functionTypeStr == "ExpressionAndCondition") {
functionType = ExpressionAndCondition;
expressionType.UnserializeFrom(element.GetChild("expressionType"));
}
else if (functionTypeStr == "ActionWithOperator")
functionType = ActionWithOperator;
else
functionType = Action;

View File

@@ -12,6 +12,7 @@
#include "GDCore/Events/EventsList.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/String.h"
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
// TODO: In theory (for separation of concerns between Project and
// extensions/events), this include should be removed and gd::ParameterMetadata
// replaced by a new gd::EventsFunctionParameter class.
@@ -19,6 +20,7 @@
namespace gd {
class SerializerElement;
class Project;
class EventsFunctionsContainer;
} // namespace gd
namespace gd {
@@ -115,7 +117,40 @@ class GD_CORE_API EventsFunction {
return *this;
}
enum FunctionType { Action, Condition, Expression, StringExpression };
/**
* \brief Get the name of the ExpressionAndCondition to use as an operand
* that is defined in the editor.
*/
const gd::String& GetGetterName() const { return getterName; };
/**
* \brief Set the name of the ExpressionAndCondition to use as an operand
* that is defined in the editor.
*/
EventsFunction& SetGetterName(const gd::String& getterName_) {
getterName = getterName_;
return *this;
}
/**
* \brief Set the type of the expression
*/
EventsFunction& SetExpressionType(const gd::ValueTypeMetadata& type) {
expressionType = type;
return *this;
}
/**
* \brief Get the type of the expression
*/
const gd::ValueTypeMetadata& GetExpressionType() const { return expressionType; }
enum FunctionType {
Action,
Condition,
Expression,
ExpressionAndCondition,
ActionWithOperator };
/**
* \brief Set the type of the function
@@ -123,12 +158,40 @@ class GD_CORE_API EventsFunction {
EventsFunction& SetFunctionType(FunctionType type) {
functionType = type;
return *this;
};
}
/**
* \brief Get the type of the function
*/
FunctionType GetFunctionType() const { return functionType; };
FunctionType GetFunctionType() const { return functionType; }
/**
* \brief Return true if the function is an action.
*/
bool IsAction() const {
return functionType == gd::EventsFunction::Action ||
functionType == gd::EventsFunction::ActionWithOperator;
}
/**
* \brief Return true if the function is an expression.
*
* Note that a function can be both an expression and a condition.
*/
bool IsExpression() const {
return functionType == gd::EventsFunction::Expression ||
functionType == gd::EventsFunction::ExpressionAndCondition;
}
/**
* \brief Return true if the function is a condition.
*
* Note that a function can be both an expression and a condition.
*/
bool IsCondition() const {
return functionType == gd::EventsFunction::Condition ||
functionType == gd::EventsFunction::ExpressionAndCondition;
}
/**
* \brief Returns true if the function is private.
@@ -154,7 +217,20 @@ class GD_CORE_API EventsFunction {
gd::EventsList& GetEvents() { return events; };
/**
* \brief Return the parameters of the function.
* \brief Return the parameters of the function that are used in the events.
*
* \note During code/extension generation, new parameters are added
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
* This should be transparent to the user.
*/
const std::vector<gd::ParameterMetadata>& GetParametersForEvents(
const gd::EventsFunctionsContainer& functionsContainer) const;
/**
* \brief Return the parameters of the function that are filled in the editor.
*
* \note They won't be used for ActionWithOperator, but they need to be kept
* to avoid to loose them when the function type is changed.
*
* \note During code/extension generation, new parameters are added
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
@@ -202,9 +278,12 @@ class GD_CORE_API EventsFunction {
gd::String description;
gd::String sentence;
gd::String group;
gd::String getterName;
gd::ValueTypeMetadata expressionType;
gd::EventsList events;
FunctionType functionType;
std::vector<gd::ParameterMetadata> parameters;
mutable std::vector<gd::ParameterMetadata> actionWithOperationParameters;
gd::ObjectGroupsContainer objectGroups;
bool isPrivate = false;
};

View File

@@ -25,7 +25,24 @@ namespace gd {
*/
class GD_CORE_API EventsFunctionsContainer
: private SerializableWithNameList<gd::EventsFunction> {
public:
public:
enum FunctionOwner {
Extension,
Object,
Behavior};
EventsFunctionsContainer(FunctionOwner source_) : owner(source_) {}
/**
* \brief Get the source of the function container.
*
* \note For instance, it can be useful to handle specific parameters for
* behaviors.
*/
FunctionOwner GetOwner() const {
return owner;
}
/** \name Events Functions management
*/
///@{
@@ -139,6 +156,9 @@ class GD_CORE_API EventsFunctionsContainer
void Init(const gd::EventsFunctionsContainer& other) {
return SerializableWithNameList<gd::EventsFunction>::Init(other);
};
private:
FunctionOwner owner;
};
} // namespace gd

View File

@@ -13,10 +13,14 @@
namespace gd {
EventsFunctionsExtension::EventsFunctionsExtension() {}
EventsFunctionsExtension::EventsFunctionsExtension() :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {}
EventsFunctionsExtension::EventsFunctionsExtension(
const EventsFunctionsExtension& other) {
const EventsFunctionsExtension& other) :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {
Init(other);
}

View File

@@ -20,7 +20,9 @@ class Project;
} // namespace gd
namespace gd {
// TODO Remove the EventsFunctionsContainer inheritance and make it an attribute.
// This will allow to get EventsFunctionsContainer the same way for extensions,
// objects and behaviors.
/**
* \brief Hold a list of Events Functions (gd::EventsFunction) and Events Based
* Behaviors.

View File

@@ -10,7 +10,8 @@
TEST_CASE("EventsFunctionsContainer", "[common]") {
SECTION("Sanity checks") {
gd::EventsFunctionsContainer eventsFunctionContainer;
gd::EventsFunctionsContainer eventsFunctionContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer.InsertNewEventsFunction("Function1", 0);
eventsFunctionContainer.InsertNewEventsFunction("Function2", 1);
eventsFunctionContainer.InsertNewEventsFunction("Function3", 2);
@@ -62,7 +63,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
}
SECTION("Serialization") {
gd::Project project;
gd::EventsFunctionsContainer eventsFunctionContainer;
gd::EventsFunctionsContainer eventsFunctionContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer.InsertNewEventsFunction("Function1", 0);
eventsFunctionContainer.InsertNewEventsFunction("Function2", 1);
eventsFunctionContainer.InsertNewEventsFunction("Function3", 2);
@@ -72,7 +74,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
eventsFunctionContainer.RemoveEventsFunction("Function2");
gd::EventsFunctionsContainer eventsFunctionContainer2;
gd::EventsFunctionsContainer eventsFunctionContainer2(
gd::EventsFunctionsContainer::FunctionOwner::Extension);
eventsFunctionContainer2.UnserializeEventsFunctionsFrom(project, element);
REQUIRE(eventsFunctionContainer.GetEventsFunctionsCount() == 2);
REQUIRE(eventsFunctionContainer.GetEventsFunction(0).GetName() ==

View File

@@ -70,6 +70,9 @@ const gd::String &GetEventFirstActionType(const gd::BaseEvent &event) {
enum TestEvent {
FreeFunctionAction,
FreeFunctionWithExpression,
FreeConditionFromExpressionAndCondition,
FreeExpressionFromExpressionAndCondition,
FreeActionWithOperator,
FreeFunctionWithObjects,
FreeFunctionWithObjectExpression,
@@ -81,6 +84,9 @@ enum TestEvent {
IllNamedBehaviorExpression,
NoParameterBehaviorExpression,
NoParameterIllNamedBehaviorExpression,
BehaviorConditionFromExpressionAndCondition,
BehaviorExpressionFromExpressionAndCondition,
BehaviorActionWithOperator,
ObjectAction,
ObjectPropertyAction,
@@ -90,6 +96,9 @@ enum TestEvent {
IllNamedObjectExpression,
NoParameterObjectExpression,
NoParameterIllNamedObjectExpression,
ObjectConditionFromExpressionAndCondition,
ObjectExpressionFromExpressionAndCondition,
ObjectActionWithOperator,
};
const std::vector<const gd::EventsList *> GetEventsLists(gd::Project &project) {
@@ -115,7 +124,7 @@ const void SetupEvents(gd::EventsList &eventList) {
// Add some free functions usages in events
{
if (eventList.GetEventsCount() != FreeFunctionAction) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsFunction
@@ -133,7 +142,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != FreeFunctionWithExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsFunctionExpression
@@ -150,8 +159,86 @@ const void SetupEvents(gd::EventsList &eventList) {
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeConditionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsFunctionExpressionAndCondition
// as a condition.
{
gd::StandardEvent event;
gd::Instruction condition;
condition.SetType("MyEventsExtension::MyEventsFunctionExpressionAndCondition");
condition.SetParametersCount(5);
condition.SetParameter(
0,
gd::Expression("scene"));
condition.SetParameter(
1,
gd::Expression(">"));
condition.SetParameter(
2,
gd::Expression("2"));
condition.SetParameter(
3,
gd::Expression("111"));
condition.SetParameter(
4,
gd::Expression("222"));
event.GetConditions().Insert(condition);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeExpressionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsFunctionExpressionAndCondition
// as an expression.
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
"2 + MyEventsExtension::MyEventsFunctionExpressionAndCondition(111, 222)"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeActionWithOperator) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsFunctionActionWithOperator
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyEventsExtension::MyEventsFunctionActionWithOperator");
action.SetParametersCount(5);
action.SetParameter(
0,
gd::Expression("scene"));
action.SetParameter(
1,
gd::Expression("+"));
action.SetParameter(
2,
gd::Expression("2"));
action.SetParameter(
3,
gd::Expression("111"));
action.SetParameter(
4,
gd::Expression("222"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjects) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to objects
{
@@ -166,7 +253,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != FreeFunctionWithObjectExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to objects in an expression
{
@@ -186,7 +273,7 @@ const void SetupEvents(gd::EventsList &eventList) {
// Add some events based behavior usages in events
{
if (eventList.GetEventsCount() != BehaviorAction) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunction
@@ -206,7 +293,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != BehaviorPropertyAction) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" action
{
@@ -220,7 +307,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != BehaviorPropertyCondition) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" condition
{
@@ -234,7 +321,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != BehaviorPropertyExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" expression
{
@@ -252,7 +339,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != BehaviorExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
@@ -271,7 +358,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != IllNamedBehaviorExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event **wrongly** referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
@@ -291,7 +378,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != NoParameterBehaviorExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
@@ -310,7 +397,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != NoParameterIllNamedBehaviorExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event **wrongly** referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpression
@@ -327,12 +414,99 @@ const void SetupEvents(gd::EventsList &eventList) {
event.GetActions().Insert(instruction);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorConditionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpressionAndCondition
// as a condition.
{
gd::StandardEvent event;
gd::Instruction condition;
condition.SetType("MyEventsExtension::MyEventsBasedBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition");
condition.SetParametersCount(6);
condition.SetParameter(
0,
gd::Expression("ObjectWithMyBehavior"));
condition.SetParameter(
1,
gd::Expression("MyBehavior"));
condition.SetParameter(
2,
gd::Expression(">"));
condition.SetParameter(
3,
gd::Expression("5"));
condition.SetParameter(
4,
gd::Expression("111"));
condition.SetParameter(
5,
gd::Expression("222"));
event.GetConditions().Insert(condition);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorExpressionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionExpressionAndCondition
// as an expression.
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression("5 + "
"ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition(111, 222)"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != BehaviorActionWithOperator) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedBehavior::MyBehaviorEventsFunctionActionWithOperator
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyEventsExtension::MyEventsBasedBehavior::"
"MyBehaviorEventsFunctionActionWithOperator");
action.SetParametersCount(6);
action.SetParameter(
0,
gd::Expression("ObjectWithMyBehavior"));
action.SetParameter(
1,
gd::Expression("MyBehavior"));
action.SetParameter(
2,
gd::Expression("+"));
action.SetParameter(
3,
gd::Expression("5"));
action.SetParameter(
4,
gd::Expression("111"));
action.SetParameter(
5,
gd::Expression("222"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
}
// Add some events based object usages in events
{
if (eventList.GetEventsCount() != ObjectAction) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunction
@@ -351,7 +525,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != ObjectPropertyAction) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" action
{
@@ -365,7 +539,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != ObjectPropertyCondition) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" condition
{
@@ -379,7 +553,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != ObjectPropertyExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event in the layout using "MyProperty" expression
{
@@ -397,7 +571,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != ObjectExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpression
@@ -415,7 +589,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != IllNamedObjectExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event **wrongly** referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpression
@@ -434,7 +608,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != NoParameterObjectExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpression
@@ -453,7 +627,7 @@ const void SetupEvents(gd::EventsList &eventList) {
}
if (eventList.GetEventsCount() != NoParameterIllNamedObjectExpression) {
throw std::logic_error("Invalid events setup");
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event **wrongly** referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpression
@@ -470,6 +644,87 @@ const void SetupEvents(gd::EventsList &eventList) {
event.GetActions().Insert(instruction);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != ObjectConditionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpressionAndCondition
// as a condition.
{
gd::StandardEvent event;
gd::Instruction condition;
condition.SetType("MyEventsExtension::MyEventsBasedObject::"
"MyObjectEventsFunctionExpressionAndCondition");
condition.SetParametersCount(5);
condition.SetParameter(
0,
gd::Expression("MyCustomObject"));
condition.SetParameter(
1,
gd::Expression(">"));
condition.SetParameter(
2,
gd::Expression("5"));
condition.SetParameter(
3,
gd::Expression("111"));
condition.SetParameter(
4,
gd::Expression("222"));
event.GetConditions().Insert(condition);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != ObjectExpressionFromExpressionAndCondition) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionExpressionAndCondition
// as an expression.
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression("5 + "
"MyCustomObject."
"MyObjectEventsFunctionExpressionAndCondition(111, 222)"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != ObjectActionWithOperator) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to
// MyEventsExtension::MyEventsBasedObject::MyObjectEventsFunctionActionWithOperator
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyEventsExtension::MyEventsBasedObject::"
"MyObjectEventsFunctionActionWithOperator");
action.SetParametersCount(5);
action.SetParameter(
0,
gd::Expression("MyCustomObject"));
action.SetParameter(
1,
gd::Expression("+"));
action.SetParameter(
2,
gd::Expression("5"));
action.SetParameter(
3,
gd::Expression("111"));
action.SetParameter(
4,
gd::Expression("222"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
}
}
@@ -527,6 +782,31 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetType("behavior")
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
auto &behaviorExpressionAndCondition =
behaviorEventsFunctions
.InsertNewEventsFunction("MyBehaviorEventsFunctionExpressionAndCondition", 2)
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
behaviorExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata().SetName("Object").SetType("object"));
behaviorExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Behavior")
.SetType("behavior")
.SetExtraInfo("MyExtension::MyEventsBasedBehavior"));
behaviorExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value1")
.SetType("expression"));
behaviorExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value2")
.SetType("expression"));
behaviorEventsFunctions
.InsertNewEventsFunction("MyBehaviorEventsFunctionActionWithOperator", 2)
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
.SetGetterName("MyBehaviorEventsFunctionExpressionAndCondition");
// Add property
eventsBasedBehavior.GetPropertyDescriptors()
.InsertNew("MyProperty", 0)
@@ -570,6 +850,26 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetType("object")
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
auto &objectExpressionAndCondition =
objectEventsFunctions
.InsertNewEventsFunction("MyObjectEventsFunctionExpressionAndCondition", 2)
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
objectExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata().SetName("Object").SetType("object"));
objectExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value1")
.SetType("expression"));
objectExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value2")
.SetType("expression"));
objectEventsFunctions
.InsertNewEventsFunction("MyObjectEventsFunctionActionWithOperator", 2)
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
.SetGetterName("MyObjectEventsFunctionExpressionAndCondition");
// Add a property
eventsBasedObject.GetPropertyDescriptors()
.InsertNew("MyProperty", 0)
@@ -622,6 +922,7 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetName("Behavior")
.SetType("behavior")
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
auto &expression =
eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpression", 1)
.SetFunctionType(gd::EventsFunction::Expression);
@@ -629,6 +930,21 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetName("currentScene")
.SetType("")
.SetCodeOnly(true));
auto &freeExpressionAndCondition = eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpressionAndCondition", 2)
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
freeExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value1")
.SetType("expression"));
freeExpressionAndCondition.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("Value2")
.SetType("expression"));
eventsExtension.InsertNewEventsFunction("MyEventsFunctionActionWithOperator", 2)
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
.SetGetterName("MyEventsFunctionExpressionAndCondition");
}
// Add some usages in events
@@ -1048,6 +1364,21 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyRenamedExtension::MyEventsFunctionExpression(123, 456)");
// Check that events function calls from an ExpressionAndCondition have
// been renamed.
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(FreeConditionFromExpressionAndCondition)) ==
"MyRenamedExtension::MyEventsFunctionExpressionAndCondition");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeExpressionFromExpressionAndCondition)) ==
"2 + MyRenamedExtension::MyEventsFunctionExpressionAndCondition(111, 222)");
// Check that events function calls from an ActionWithOperator has
// been renamed.
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(FreeActionWithOperator)) ==
"MyRenamedExtension::MyEventsFunctionActionWithOperator");
// Check that the type of the behavior was changed in the behaviors of
// objects. Name is *not* changed.
REQUIRE(project.GetLayout("Scene")
@@ -1084,7 +1415,17 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(GetEventFirstActionType(eventsList->GetEvent(BehaviorAction)) ==
"MyRenamedExtension::MyEventsBasedBehavior::"
"MyBehaviorEventsFunction");
REQUIRE(
GetEventFirstConditionType(
eventsList->GetEvent(BehaviorConditionFromExpressionAndCondition)) ==
"MyRenamedExtension::MyEventsBasedBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition");
REQUIRE(
GetEventFirstActionType(
eventsList->GetEvent(BehaviorActionWithOperator)) ==
"MyRenamedExtension::MyEventsBasedBehavior::"
"MyBehaviorEventsFunctionActionWithOperator");
// Check if events-based behaviors properties have been renamed in
// instructions
REQUIRE(GetEventFirstActionType(
@@ -1096,16 +1437,19 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpression)) ==
"1 + "
"ObjectWithMyBehavior.MyBehavior::"
"1 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(NoParameterBehaviorExpression)) ==
"3 + "
"ObjectWithMyBehavior.MyBehavior::"
"3 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpression");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpressionFromExpressionAndCondition)) ==
"5 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition(111, 222)");
// Check that the type of the object was changed in the custom
// objects. Name is *not* changed.
REQUIRE(
@@ -1119,6 +1463,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(
GetEventFirstActionType(eventsList->GetEvent(ObjectAction)) ==
"MyRenamedExtension::MyEventsBasedObject::MyObjectEventsFunction");
REQUIRE(
GetEventFirstConditionType(
eventsList->GetEvent(ObjectConditionFromExpressionAndCondition)) ==
"MyRenamedExtension::MyEventsBasedObject::"
"MyObjectEventsFunctionExpressionAndCondition");
REQUIRE(
GetEventFirstActionType(
eventsList->GetEvent(ObjectActionWithOperator)) ==
"MyRenamedExtension::MyEventsBasedObject::"
"MyObjectEventsFunctionActionWithOperator");
// Check if events-based object properties have been renamed in
// instructions
@@ -1130,14 +1484,17 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpression)) ==
"1 + "
"MyCustomObject."
"1 + MyCustomObject."
"MyObjectEventsFunctionExpression(123, 456, 789)");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(NoParameterObjectExpression)) ==
"3 + "
"MyCustomObject.MyObjectEventsFunctionExpression");
"3 + MyCustomObject.MyObjectEventsFunctionExpression");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpressionFromExpressionAndCondition)) ==
"5 + MyCustomObject."
"MyObjectEventsFunctionExpressionAndCondition(111, 222)");
}
}
@@ -1239,6 +1596,35 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression and condition renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
gd::WholeProjectRefactorer::RenameEventsFunction(
project,
eventsExtension,
"MyEventsFunctionExpressionAndCondition",
"MyRenamedFunctionExpressionAndCondition");
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been renamed
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeExpressionFromExpressionAndCondition)) ==
"2 + MyEventsExtension::MyRenamedFunctionExpressionAndCondition(111, 222)");
// Check that events function calls in instructions have been renamed
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(FreeConditionFromExpressionAndCondition)) ==
"MyEventsExtension::MyRenamedFunctionExpressionAndCondition");
// Check that the action still refer to the right ExpressionAndCondition.
REQUIRE(eventsExtension.GetEventsFunction("MyEventsFunctionActionWithOperator")
.GetGetterName() == "MyRenamedFunctionExpressionAndCondition");
}
}
SECTION("(Free) events action parameter moved") {
gd::Project project;
gd::Platform platform;
@@ -1279,6 +1665,35 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Free) events expression and condition parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
// The index 0 is reserved for the RuntimeScene.
gd::WholeProjectRefactorer::MoveEventsFunctionParameter(
project, eventsExtension, "MyEventsFunctionExpressionAndCondition", 1, 2);
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been updated
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeExpressionFromExpressionAndCondition)) ==
"2 + MyEventsExtension::MyEventsFunctionExpressionAndCondition(222, 111)");
// Check that events function calls in instructions have been updated
auto &condition = static_cast<const gd::StandardEvent &>(
eventsList->GetEvent(FreeConditionFromExpressionAndCondition))
.GetConditions()
.Get(0);
REQUIRE(condition.GetParameter(0).GetPlainString() == "scene");
REQUIRE(condition.GetParameter(1).GetPlainString() == ">");
REQUIRE(condition.GetParameter(2).GetPlainString() == "2");
REQUIRE(condition.GetParameter(3).GetPlainString() == "222");
REQUIRE(condition.GetParameter(4).GetPlainString() == "111");
}
}
SECTION("Events based behavior renamed (instructions update)") {
gd::Project project;
gd::Platform platform;
@@ -1316,6 +1731,14 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(GetEventFirstActionType(eventsList->GetEvent(BehaviorAction)) ==
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
"MyBehaviorEventsFunction");
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(BehaviorConditionFromExpressionAndCondition)) ==
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition");
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(BehaviorActionWithOperator)) ==
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
"MyBehaviorEventsFunctionActionWithOperator");
// Check if events-based behaviors properties have been renamed in
// instructions
@@ -1328,9 +1751,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpression)) ==
"1 + "
"ObjectWithMyBehavior.MyBehavior::"
"1 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpression(123, 456, 789)");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpressionFromExpressionAndCondition)) ==
"5 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition(111, 222)");
}
}
@@ -1407,6 +1833,14 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(GetEventFirstActionType(eventsList->GetEvent(ObjectAction)) ==
"MyEventsExtension::MyRenamedEventsBasedObject::"
"MyObjectEventsFunction");
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(ObjectConditionFromExpressionAndCondition)) ==
"MyEventsExtension::MyRenamedEventsBasedObject::"
"MyObjectEventsFunctionExpressionAndCondition");
REQUIRE(GetEventFirstActionType(
eventsList->GetEvent(ObjectActionWithOperator)) ==
"MyEventsExtension::MyRenamedEventsBasedObject::"
"MyObjectEventsFunctionActionWithOperator");
// Check if events-based object properties have been renamed in
// instructions
@@ -1419,9 +1853,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpression)) ==
"1 + "
"MyCustomObject."
"1 + MyCustomObject."
"MyObjectEventsFunctionExpression(123, 456, 789)");
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpressionFromExpressionAndCondition)) ==
"5 + MyCustomObject."
"MyObjectEventsFunctionExpressionAndCondition(111, 222)");
}
}
@@ -1623,6 +2060,80 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Events based behavior) events expression and condition renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
gd::WholeProjectRefactorer::RenameBehaviorEventsFunction(
project,
eventsExtension,
eventsBasedBehavior,
"MyBehaviorEventsFunctionExpressionAndCondition",
"MyRenamedBehaviorEventsFunctionExpressionAndCondition");
for (auto *eventsList : GetEventsLists(project)) {
// Check events-based behavior methods have been renamed in
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpressionFromExpressionAndCondition)) ==
"5 + ObjectWithMyBehavior.MyBehavior::"
"MyRenamedBehaviorEventsFunctionExpressionAndCondition(111, 222)");
// Check if events-based behavior methods have been renamed in
// instructions
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(BehaviorConditionFromExpressionAndCondition)) ==
"MyEventsExtension::MyEventsBasedBehavior::"
"MyRenamedBehaviorEventsFunctionExpressionAndCondition");
// Check that the action still refer to the right ExpressionAndCondition.
REQUIRE(eventsBasedBehavior.GetEventsFunctions()
.GetEventsFunction("MyBehaviorEventsFunctionActionWithOperator")
.GetGetterName() == "MyRenamedBehaviorEventsFunctionExpressionAndCondition");
}
}
SECTION("(Events based object) events expression and condition renamed") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
gd::WholeProjectRefactorer::RenameObjectEventsFunction(
project,
eventsExtension,
eventsBasedObject,
"MyObjectEventsFunctionExpressionAndCondition",
"MyRenamedObjectEventsFunctionExpressionAndCondition");
for (auto *eventsList : GetEventsLists(project)) {
// Check events-based behavior methods have been renamed in
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpressionFromExpressionAndCondition)) ==
"5 + MyCustomObject."
"MyRenamedObjectEventsFunctionExpressionAndCondition(111, 222)");
// Check if events-based behavior methods have been renamed in
// instructions
REQUIRE(GetEventFirstConditionType(
eventsList->GetEvent(ObjectConditionFromExpressionAndCondition)) ==
"MyEventsExtension::MyEventsBasedObject::"
"MyRenamedObjectEventsFunctionExpressionAndCondition");
// Check that the action still refer to the right ExpressionAndCondition.
REQUIRE(eventsBasedObject.GetEventsFunctions()
.GetEventsFunction("MyObjectEventsFunctionActionWithOperator")
.GetGetterName() == "MyRenamedObjectEventsFunctionExpressionAndCondition");
}
}
SECTION("(Events based behavior) events action parameter moved") {
gd::Project project;
gd::Platform platform;
@@ -1739,6 +2250,83 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
}
}
SECTION("(Events based behavior) events expression and condition parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
// The first 2 parameters are reserved for the object and behavior.
gd::WholeProjectRefactorer::MoveBehaviorEventsFunctionParameter(
project,
eventsExtension,
eventsBasedBehavior,
"MyBehaviorEventsFunctionExpressionAndCondition",
2,
3);
for (auto *eventsList : GetEventsLists(project)) {
// Check parameters of events-based behavior methods have been moved in
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(BehaviorExpressionFromExpressionAndCondition)) ==
"5 + ObjectWithMyBehavior.MyBehavior::"
"MyBehaviorEventsFunctionExpressionAndCondition(222, 111)");
// Check if parameters of events-based behavior methods have been moved in
// instructions
auto &action = static_cast<const gd::StandardEvent &>(
eventsList->GetEvent(BehaviorConditionFromExpressionAndCondition))
.GetConditions()
.Get(0);
REQUIRE(action.GetParameter(0).GetPlainString() == "ObjectWithMyBehavior");
REQUIRE(action.GetParameter(1).GetPlainString() == "MyBehavior");
REQUIRE(action.GetParameter(2).GetPlainString() == ">");
REQUIRE(action.GetParameter(3).GetPlainString() == "5");
REQUIRE(action.GetParameter(4).GetPlainString() == "222");
REQUIRE(action.GetParameter(5).GetPlainString() == "111");
}
}
SECTION("(Events based object) events expression and condition parameter moved") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
// The first 2 parameters are reserved for the object and behavior.
gd::WholeProjectRefactorer::MoveObjectEventsFunctionParameter(
project,
eventsExtension,
eventsBasedObject,
"MyObjectEventsFunctionExpressionAndCondition",
1,
2);
for (auto *eventsList : GetEventsLists(project)) {
// Check parameters of events-based behavior methods have been moved in
// expressions
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(ObjectExpressionFromExpressionAndCondition)) ==
"5 + MyCustomObject."
"MyObjectEventsFunctionExpressionAndCondition(222, 111)");
// Check if parameters of events-based behavior methods have been moved in
// instructions
auto &action = static_cast<const gd::StandardEvent &>(
eventsList->GetEvent(ObjectConditionFromExpressionAndCondition))
.GetConditions()
.Get(0);
REQUIRE(action.GetParameter(0).GetPlainString() == "MyCustomObject");
REQUIRE(action.GetParameter(1).GetPlainString() == ">");
REQUIRE(action.GetParameter(2).GetPlainString() == "5");
REQUIRE(action.GetParameter(3).GetPlainString() == "222");
REQUIRE(action.GetParameter(4).GetPlainString() == "111");
}
}
SECTION(
"(Events based behavior) property renamed (not a required behavior)") {
gd::Project project;

View File

@@ -116,6 +116,7 @@ gd::String EventsCodeGenerator::GenerateLayoutCode(
gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
gd::Project& project,
const gd::EventsFunctionsContainer& functionsContainer,
const gd::EventsFunction& eventsFunction,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,
@@ -123,7 +124,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
gd::ObjectsContainer globalObjectsAndGroups;
gd::ObjectsContainer objectsAndGroups;
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunction, globalObjectsAndGroups, objectsAndGroups);
project, functionsContainer, eventsFunction, globalObjectsAndGroups, objectsAndGroups);
EventsCodeGenerator codeGenerator(globalObjectsAndGroups, objectsAndGroups);
codeGenerator.SetCodeNamespace(codeNamespace);
@@ -133,9 +134,9 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
codeGenerator,
codeGenerator.GetCodeNamespaceAccessor() + "func",
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
eventsFunction.GetParameters(), 0, true),
eventsFunction.GetParametersForEvents(functionsContainer), 0, true),
codeGenerator.GenerateFreeEventsFunctionContext(
eventsFunction.GetParameters(), "runtimeScene.getOnceTriggers()"),
eventsFunction.GetParametersForEvents(functionsContainer), "runtimeScene.getOnceTriggers()"),
eventsFunction.GetEvents(),
"",
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
@@ -185,7 +186,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
"var Behavior = this.name;\n" +
codeGenerator.GenerateBehaviorEventsFunctionContext(
eventsBasedBehavior,
eventsFunction.GetParameters(),
eventsFunction.GetParametersForEvents(
eventsBasedBehavior.GetEventsFunctions()),
onceTriggersVariable,
// Pass the names of the parameters considered as the current
// object and behavior parameters:
@@ -196,7 +198,8 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
codeGenerator,
fullyQualifiedFunctionName,
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
eventsFunction.GetParameters(), 2, false),
eventsFunction.GetParametersForEvents(
eventsBasedBehavior.GetEventsFunctions()), 2, false),
fullPreludeCode,
eventsFunction.GetEvents(),
"",
@@ -258,7 +261,8 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
fullPreludeCode += codeGenerator.GenerateObjectEventsFunctionContext(
eventsBasedObject,
eventsFunction.GetParameters(),
eventsFunction.GetParametersForEvents(
eventsBasedObject.GetEventsFunctions()),
onceTriggersVariable,
// Pass the names of the parameters considered as the current
// object and behavior parameters:
@@ -269,7 +273,8 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
fullyQualifiedFunctionName,
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
// TODO EBO use constants for firstParameterIndex
eventsFunction.GetParameters(), 1, false),
eventsFunction.GetParametersForEvents(
eventsBasedObject.GetEventsFunctions()), 1, false),
fullPreludeCode,
eventsFunction.GetEvents(),
endingCode,
@@ -557,16 +562,20 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
gd::String EventsCodeGenerator::GenerateEventsFunctionReturn(
const gd::EventsFunction& eventsFunction) {
// We don't use IsCondition because ExpressionAndCondition event functions
// don't need a boolean function. They use the expression function with a
// relational operator.
if (eventsFunction.GetFunctionType() == gd::EventsFunction::Condition) {
return "return !!eventsFunctionContext.returnValue;";
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::Expression) {
return "return Number(eventsFunctionContext.returnValue) || 0;";
} else if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::StringExpression) {
return "return \"\" + eventsFunctionContext.returnValue;";
} else if (eventsFunction.IsExpression()) {
if (eventsFunction.GetExpressionType().IsNumber()) {
return "return Number(eventsFunctionContext.returnValue) || 0;";
} else {
// Default on string because it's more likely that future expression
// types are strings.
return "return \"\" + eventsFunctionContext.returnValue;";
}
}
return "return;";
}

View File

@@ -14,6 +14,7 @@
#include "GDCore/Events/InstructionsList.h"
namespace gd {
class ObjectsContainer;
class EventsFunctionsContainer;
class EventsFunction;
class EventsBasedBehavior;
class EventsBasedObject;
@@ -55,6 +56,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
* Generate JavaScript for executing events of an events based function.
*
* \param project Project used.
* \param functionsContainer The container of the compiled event function.
* \param eventsFunction The events function to be compiled.
* \param codeNamespace Where to store the context used by the function.
* \param includeFiles Will be filled with the necessary include files.
@@ -65,6 +67,7 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
*/
static gd::String GenerateEventsFunctionCode(
gd::Project& project,
const gd::EventsFunctionsContainer& functionsContainer,
const gd::EventsFunction& eventsFunction,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,

View File

@@ -11,6 +11,7 @@
namespace gdjs {
gd::String
EventsFunctionsExtensionCodeGenerator::GenerateFreeEventsFunctionCompleteCode(
const gd::EventsFunctionsExtension& extension,
const gd::EventsFunction& eventsFunction,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,
@@ -27,6 +28,7 @@ if (typeof CODE_NAMESPACE !== "undefined") {
gd::String eventsFunctionCode =
EventsCodeGenerator::GenerateEventsFunctionCode(project,
extension,
eventsFunction,
codeNamespace,
includeFiles,

View File

@@ -29,6 +29,7 @@ class EventsFunctionsExtensionCodeGenerator {
* \brief Generate the complete code for the specified events function.
*/
gd::String GenerateFreeEventsFunctionCompleteCode(
const gd::EventsFunctionsExtension& extension,
const gd::EventsFunction& eventsFunction,
const gd::String& codeNamespace,
std::set<gd::String>& includeFiles,

View File

@@ -1200,8 +1200,8 @@ interface InstructionMetadata {
[Ref] InstructionMetadata SetParameterLongDescription([Const] DOMString longDescription);
[Ref] InstructionMetadata SetParameterExtraInfo([Const] DOMString extraInfo);
[Ref] InstructionMetadata UseStandardOperatorParameters([Const] DOMString type);
[Ref] InstructionMetadata UseStandardRelationalOperatorParameters([Const] DOMString type);
[Ref] InstructionMetadata UseStandardOperatorParameters([Const] DOMString type, [Const] optional DOMString typeExtraInfo = "");
[Ref] InstructionMetadata UseStandardRelationalOperatorParameters([Const] DOMString type, [Const] optional DOMString typeExtraInfo = "");
[Ref] InstructionMetadata SetRequiresBaseObjectCapability([Const] DOMString capability);
[Const, Ref] DOMString GetRequiredBaseObjectCapability();
@@ -1211,6 +1211,12 @@ interface InstructionMetadata {
[Ref] InstructionMetadata MarkAsComplex();
[Ref] ExtraInformation GetCodeExtraInformation();
[Ref] ExtraInformation SetFunctionName([Const] DOMString functionName);
[Ref] InstructionMetadata SetIncludeFile([Const] DOMString includeFile);
[Ref] InstructionMetadata AddIncludeFile([Const] DOMString includeFile);
[Const, Ref] VectorString GetIncludeFiles();
};
interface ExpressionMetadata {
@@ -1246,11 +1252,18 @@ interface ExpressionMetadata {
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] ExpressionMetadata SetDefaultValue([Const] DOMString defaultValue);
[Ref] ExpressionMetadata SetParameterLongDescription([Const] DOMString longDescription);
[Ref] ExpressionMetadata SetParameterExtraInfo([Const] DOMString extraInfo);
[Ref] ExpressionMetadata SetRequiresBaseObjectCapability([Const] DOMString capability);
[Const, Ref] DOMString GetRequiredBaseObjectCapability();
[Ref] ExpressionCodeGenerationInformation GetCodeExtraInformation();
[Ref] ExpressionCodeGenerationInformation SetFunctionName([Const] DOMString functionName);
[Ref] ExpressionMetadata SetIncludeFile([Const] DOMString includeFile);
[Ref] ExpressionMetadata AddIncludeFile([Const] DOMString includeFile);
[Const, Ref] VectorString GetIncludeFiles();
};
interface MultipleInstructionMetadata {
@@ -1262,8 +1275,9 @@ interface MultipleInstructionMetadata {
[Const] DOMString type, [Const] DOMString supplementaryInformation);
[Ref] MultipleInstructionMetadata SetDefaultValue([Const] DOMString defaultValue);
[Ref] MultipleInstructionMetadata SetParameterLongDescription([Const] DOMString longDescription);
[Ref] MultipleInstructionMetadata SetParameterExtraInfo([Const] DOMString extraInfo);
[Ref] MultipleInstructionMetadata UseStandardParameters([Const] DOMString type);
[Ref] MultipleInstructionMetadata UseStandardParameters([Const] DOMString type, [Const] optional DOMString typeExtraInfo = "");
[Ref] MultipleInstructionMetadata SetHidden();
@@ -1272,10 +1286,12 @@ interface MultipleInstructionMetadata {
[Ref] MultipleInstructionMetadata SetIncludeFile([Const] DOMString includeFile);
[Ref] MultipleInstructionMetadata AddIncludeFile([Const] DOMString includeFile);
[Const, Ref] VectorString GetIncludeFiles();
[Ref] MultipleInstructionMetadata MarkAsSimple();
[Ref] MultipleInstructionMetadata MarkAsAdvanced();
[Ref] MultipleInstructionMetadata MarkAsComplex();
[Ref] MultipleInstructionMetadata SetPrivate();
};
interface DependencyMetadata {
@@ -1317,6 +1333,10 @@ interface ParameterMetadata {
[Ref] ParameterMetadata SetCodeOnly(boolean codeOnly_);
[Const, Ref] DOMString GetDefaultValue();
[Ref] ParameterMetadata SetDefaultValue([Const] DOMString defaultValue_);
[Ref] ParameterMetadata SetValueTypeMetadata([Const, Ref] ValueTypeMetadata type);
[Const, Ref] ValueTypeMetadata GetValueTypeMetadata();
boolean STATIC_IsObject([Const] DOMString param);
boolean STATIC_IsBehavior([Const] DOMString param);
boolean STATIC_IsExpression([Const] DOMString type_, [Const] DOMString parameterType);
@@ -1325,6 +1345,33 @@ interface ParameterMetadata {
void UnserializeFrom([Const, Ref] SerializerElement element);
};
interface ValueTypeMetadata {
void ValueTypeMetadata();
[Const, Ref] DOMString GetName();
[Ref] ValueTypeMetadata SetName([Const] DOMString name_);
[Const, Ref] DOMString GetExtraInfo();
[Ref] ValueTypeMetadata SetExtraInfo([Const] DOMString extraInfo_);
boolean IsOptional();
[Ref] ValueTypeMetadata SetOptional(boolean optional_);
[Const, Ref] DOMString GetDefaultValue();
[Ref] ValueTypeMetadata SetDefaultValue([Const] DOMString defaultValue_);
boolean IsObject();
boolean IsBehavior();
boolean IsNumber();
boolean IsString();
boolean IsVariable();
boolean STATIC_IsTypeObject([Const] DOMString parameterType);
boolean STATIC_IsTypeBehavior([Const] DOMString parameterType);
boolean STATIC_IsTypeExpression([Const] DOMString type, [Const] DOMString parameterType);
[Const, Ref] DOMString STATIC_GetPrimitiveValueType([Const] DOMString parameterType);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Const, Ref] SerializerElement element);
};
interface VectorParameterMetadata {
void VectorParameterMetadata();
@@ -1343,7 +1390,7 @@ interface ParameterMetadataTools {
};
interface EventsFunctionTools {
void STATIC_FreeEventsFunctionToObjectsContainer([Ref] Project project, [Const, Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer outputGlobalObjectsContainer, [Ref] ObjectsContainer outputObjectsContainer);
void STATIC_FreeEventsFunctionToObjectsContainer([Ref] Project project, [Const, Ref] EventsFunctionsContainer functionsContainer, [Const, Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer outputGlobalObjectsContainer, [Ref] ObjectsContainer outputObjectsContainer);
void STATIC_BehaviorEventsFunctionToObjectsContainer([Ref] Project project, [Const, Ref] EventsBasedBehavior eventsBasedBehavior, [Const, Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer outputGlobalObjectsContainer, [Ref] ObjectsContainer outputObjectsContainer);
void STATIC_ObjectEventsFunctionToObjectsContainer([Ref] Project project, [Const, Ref] EventsBasedObject eventsBasedObject, [Const, Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer outputGlobalObjectsContainer, [Ref] ObjectsContainer outputObjectsContainer);
};
@@ -2243,7 +2290,8 @@ enum EventsFunction_FunctionType {
"EventsFunction::Action",
"EventsFunction::Condition",
"EventsFunction::Expression",
"EventsFunction::StringExpression"
"EventsFunction::ExpressionAndCondition",
"EventsFunction::ActionWithOperator"
};
interface EventsFunction {
@@ -2260,13 +2308,21 @@ interface EventsFunction {
[Const, Ref] DOMString GetSentence();
[Ref] EventsFunction SetGroup([Const] DOMString group);
[Const, Ref] DOMString GetGroup();
[Ref] EventsFunction SetGetterName([Const] DOMString group);
[Const, Ref] DOMString GetGetterName();
[Ref] EventsFunction SetExpressionType([Const, Ref] ValueTypeMetadata type);
[Const, Ref] ValueTypeMetadata GetExpressionType();
[Ref] EventsFunction SetPrivate(boolean isPrivate);
boolean IsPrivate();
boolean IsAction();
boolean IsExpression();
boolean IsCondition();
[Ref] EventsFunction SetFunctionType(EventsFunction_FunctionType type);
EventsFunction_FunctionType GetFunctionType();
[Ref] EventsList GetEvents();
[Ref] VectorParameterMetadata GetParameters();
[Const, Ref] VectorParameterMetadata GetParametersForEvents([Const, Ref] EventsFunctionsContainer functionsContainer);
[Ref] ObjectGroupsContainer GetObjectGroups();
void SerializeTo([Ref] SerializerElement element);
@@ -2947,7 +3003,7 @@ interface ObjectCodeGenerator {
[Prefix="gdjs::"]
interface EventsFunctionsExtensionCodeGenerator {
void EventsFunctionsExtensionCodeGenerator([Ref] Project project);
[Const, Value] DOMString GenerateFreeEventsFunctionCompleteCode([Const, Ref] EventsFunction eventsFunction, [Const] DOMString codeNamespac, [Ref] SetString includes, boolean compilationForRuntime);
[Const, Value] DOMString GenerateFreeEventsFunctionCompleteCode([Const, Ref] EventsFunctionsExtension extension, [Const, Ref] EventsFunction eventsFunction, [Const] DOMString codeNamespac, [Ref] SetString includes, boolean compilationForRuntime);
};
[Prefix="gdjs::"]

View File

@@ -511,6 +511,11 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
#define STATIC_IsObject IsObject
#define STATIC_IsBehavior IsBehavior
#define STATIC_IsExpression IsExpression
#define STATIC_IsTypeObject IsTypeObject
#define STATIC_IsTypeBehavior IsTypeBehavior
#define STATIC_IsTypeExpression IsTypeExpression
#define STATIC_GetExpressionValueType GetExpressionValueType
#define STATIC_GetPrimitiveValueType GetPrimitiveValueType
#define STATIC_Get Get
#define STATIC_GetAllUseless GetAllUseless
#define STATIC_RemoveAllUseless RemoveAllUseless

View File

@@ -17,8 +17,10 @@ function generateCompiledEventsForEventsFunction(
new gd.EventsFunctionsExtensionCodeGenerator(project);
const includeFiles = new gd.SetString();
const extension = new gd.EventsFunctionsExtension();
const code =
eventsFunctionsExtensionCodeGenerator.generateFreeEventsFunctionCompleteCode(
extension,
eventsFunction,
namespace,
includeFiles,
@@ -26,6 +28,7 @@ function generateCompiledEventsForEventsFunction(
);
eventsFunctionsExtensionCodeGenerator.delete();
extension.delete();
includeFiles.delete();
if (logCode) console.log(code);

View File

@@ -2468,8 +2468,11 @@ describe('libGD.js', function () {
expect(parametersLister.getParametersAndTypes().get('MyObject')).toBe(
'object'
);
// There are a lot of parameter definitions with 'expression' instead
// of 'number'. They both means the same thing but 'expression' is
// deprecated.
expect(parametersLister.getParametersAndTypes().get('300')).toBe(
'expression'
'number'
);
project.delete();

View File

@@ -475,10 +475,12 @@ describe('libGD.js - GDJS related tests', function () {
gd.asRepeatEvent(evt).getActions().insert(action2, 1);
const namespace = 'gdjs.eventsFunction.myTest';
const extension = new gd.EventsFunctionsExtension();
const eventsFunctionsExtensionCodeGenerator =
new gd.EventsFunctionsExtensionCodeGenerator(project);
const code =
eventsFunctionsExtensionCodeGenerator.generateFreeEventsFunctionCompleteCode(
extension,
eventsFunction,
namespace,
includeFiles,
@@ -531,6 +533,7 @@ describe('libGD.js - GDJS related tests', function () {
condition.delete();
action.delete();
extension.delete();
});
it('can generate code for an events function, with groups', function () {
@@ -583,6 +586,7 @@ describe('libGD.js - GDJS related tests', function () {
new gd.EventsFunctionsExtensionCodeGenerator(project);
const code =
eventsFunctionsExtensionCodeGenerator.generateFreeEventsFunctionCompleteCode(
new gd.EventsFunctionsExtension(),
eventsFunction,
namespace,
includeFiles,

View File

@@ -82,7 +82,7 @@ declare type gdEmscriptenObject = {
fs.writeFileSync(
'types/eventsfunction_functiontype.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type EventsFunction_FunctionType = 0 | 1 | 2 | 3`
type EventsFunction_FunctionType = 0 | 1 | 2 | 3 | 4`
);
shell.sed(
'-i',
@@ -92,7 +92,8 @@ type EventsFunction_FunctionType = 0 | 1 | 2 | 3`
' static Action: 0;',
' static Condition: 1;',
' static Expression: 2;',
' static StringExpression: 3;',
' static ExpressionAndCondition: 3;',
' static ActionWithOperator: 4;',
].join('\n'),
'types/gdeventsfunction.js'
);

View File

@@ -1,2 +1,2 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
type EventsFunction_FunctionType = 0 | 1 | 2 | 3
type EventsFunction_FunctionType = 0 | 1 | 2 | 3 | 4

View File

@@ -3,7 +3,8 @@ declare class gdEventsFunction {
static Action: 0;
static Condition: 1;
static Expression: 2;
static StringExpression: 3;
static ExpressionAndCondition: 3;
static ActionWithOperator: 4;
constructor(): void;
clone(): gdEventsFunction;
setDescription(description: string): gdEventsFunction;
@@ -16,12 +17,20 @@ declare class gdEventsFunction {
getSentence(): string;
setGroup(group: string): gdEventsFunction;
getGroup(): string;
setGetterName(group: string): gdEventsFunction;
getGetterName(): string;
setExpressionType(type: gdValueTypeMetadata): gdEventsFunction;
getExpressionType(): gdValueTypeMetadata;
setPrivate(isPrivate: boolean): gdEventsFunction;
isPrivate(): boolean;
isAction(): boolean;
isExpression(): boolean;
isCondition(): boolean;
setFunctionType(type: EventsFunction_FunctionType): gdEventsFunction;
getFunctionType(): EventsFunction_FunctionType;
getEvents(): gdEventsList;
getParameters(): gdVectorParameterMetadata;
getParametersForEvents(functionsContainer: gdEventsFunctionsContainer): gdVectorParameterMetadata;
getObjectGroups(): gdObjectGroupsContainer;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;

View File

@@ -1,7 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsFunctionsExtensionCodeGenerator {
constructor(project: gdProject): void;
generateFreeEventsFunctionCompleteCode(eventsFunction: gdEventsFunction, codeNamespac: string, includes: gdSetString, compilationForRuntime: boolean): string;
generateFreeEventsFunctionCompleteCode(extension: gdEventsFunctionsExtension, eventsFunction: gdEventsFunction, codeNamespac: string, includes: gdSetString, compilationForRuntime: boolean): string;
delete(): void;
ptr: number;
};

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsFunctionTools {
static freeEventsFunctionToObjectsContainer(project: gdProject, eventsFunction: gdEventsFunction, outputGlobalObjectsContainer: gdObjectsContainer, outputObjectsContainer: gdObjectsContainer): void;
static freeEventsFunctionToObjectsContainer(project: gdProject, functionsContainer: gdEventsFunctionsContainer, eventsFunction: gdEventsFunction, outputGlobalObjectsContainer: gdObjectsContainer, outputObjectsContainer: gdObjectsContainer): void;
static behaviorEventsFunctionToObjectsContainer(project: gdProject, eventsBasedBehavior: gdEventsBasedBehavior, eventsFunction: gdEventsFunction, outputGlobalObjectsContainer: gdObjectsContainer, outputObjectsContainer: gdObjectsContainer): void;
static objectEventsFunctionToObjectsContainer(project: gdProject, eventsBasedObject: gdEventsBasedObject, eventsFunction: gdEventsFunction, outputGlobalObjectsContainer: gdObjectsContainer, outputObjectsContainer: gdObjectsContainer): void;
delete(): void;

View File

@@ -18,9 +18,14 @@ declare class gdExpressionMetadata {
addCodeOnlyParameter(type: string, supplementaryInformation: string): gdExpressionMetadata;
setDefaultValue(defaultValue: string): gdExpressionMetadata;
setParameterLongDescription(longDescription: string): gdExpressionMetadata;
setParameterExtraInfo(extraInfo: string): gdExpressionMetadata;
setRequiresBaseObjectCapability(capability: string): gdExpressionMetadata;
getRequiredBaseObjectCapability(): string;
getCodeExtraInformation(): gdExpressionCodeGenerationInformation;
setFunctionName(functionName: string): gdExpressionCodeGenerationInformation;
setIncludeFile(includeFile: string): gdExpressionMetadata;
addIncludeFile(includeFile: string): gdExpressionMetadata;
getIncludeFiles(): gdVectorString;
delete(): void;
ptr: number;
};

View File

@@ -26,14 +26,18 @@ declare class gdInstructionMetadata {
setDefaultValue(defaultValue: string): gdInstructionMetadata;
setParameterLongDescription(longDescription: string): gdInstructionMetadata;
setParameterExtraInfo(extraInfo: string): gdInstructionMetadata;
useStandardOperatorParameters(type: string): gdInstructionMetadata;
useStandardRelationalOperatorParameters(type: string): gdInstructionMetadata;
useStandardOperatorParameters(type: string, typeExtraInfo?: string): gdInstructionMetadata;
useStandardRelationalOperatorParameters(type: string, typeExtraInfo?: string): gdInstructionMetadata;
setRequiresBaseObjectCapability(capability: string): gdInstructionMetadata;
getRequiredBaseObjectCapability(): string;
markAsSimple(): gdInstructionMetadata;
markAsAdvanced(): gdInstructionMetadata;
markAsComplex(): gdInstructionMetadata;
getCodeExtraInformation(): gdExtraInformation;
setFunctionName(functionName: string): gdExtraInformation;
setIncludeFile(includeFile: string): gdInstructionMetadata;
addIncludeFile(includeFile: string): gdInstructionMetadata;
getIncludeFiles(): gdVectorString;
delete(): void;
ptr: number;
};

View File

@@ -4,15 +4,18 @@ declare class gdMultipleInstructionMetadata {
addCodeOnlyParameter(type: string, supplementaryInformation: string): gdMultipleInstructionMetadata;
setDefaultValue(defaultValue: string): gdMultipleInstructionMetadata;
setParameterLongDescription(longDescription: string): gdMultipleInstructionMetadata;
useStandardParameters(type: string): gdMultipleInstructionMetadata;
setParameterExtraInfo(extraInfo: string): gdMultipleInstructionMetadata;
useStandardParameters(type: string, typeExtraInfo?: string): gdMultipleInstructionMetadata;
setHidden(): gdMultipleInstructionMetadata;
setFunctionName(functionName: string): gdMultipleInstructionMetadata;
setGetter(getter: string): gdMultipleInstructionMetadata;
setIncludeFile(includeFile: string): gdMultipleInstructionMetadata;
addIncludeFile(includeFile: string): gdMultipleInstructionMetadata;
getIncludeFiles(): gdVectorString;
markAsSimple(): gdMultipleInstructionMetadata;
markAsAdvanced(): gdMultipleInstructionMetadata;
markAsComplex(): gdMultipleInstructionMetadata;
setPrivate(): gdMultipleInstructionMetadata;
delete(): void;
ptr: number;
};

View File

@@ -17,6 +17,8 @@ declare class gdParameterMetadata {
setCodeOnly(codeOnly_: boolean): gdParameterMetadata;
getDefaultValue(): string;
setDefaultValue(defaultValue_: string): gdParameterMetadata;
setValueTypeMetadata(type: gdValueTypeMetadata): gdParameterMetadata;
getValueTypeMetadata(): gdValueTypeMetadata;
static isObject(param: string): boolean;
static isBehavior(param: string): boolean;
static isExpression(type_: string, parameterType: string): boolean;

View File

@@ -0,0 +1,25 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdValueTypeMetadata {
constructor(): void;
getName(): string;
setName(name_: string): gdValueTypeMetadata;
getExtraInfo(): string;
setExtraInfo(extraInfo_: string): gdValueTypeMetadata;
isOptional(): boolean;
setOptional(optional_: boolean): gdValueTypeMetadata;
getDefaultValue(): string;
setDefaultValue(defaultValue_: string): gdValueTypeMetadata;
isObject(): boolean;
isBehavior(): boolean;
isNumber(): boolean;
isString(): boolean;
isVariable(): boolean;
static isTypeObject(parameterType: string): boolean;
static isTypeBehavior(parameterType: string): boolean;
static isTypeExpression(type: string, parameterType: string): boolean;
static getPrimitiveValueType(parameterType: string): string;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(element: gdSerializerElement): void;
delete(): void;
ptr: number;
};

View File

@@ -119,6 +119,7 @@ declare class libGDevelop {
MultipleInstructionMetadata: Class<gdMultipleInstructionMetadata>;
DependencyMetadata: Class<gdDependencyMetadata>;
ParameterMetadata: Class<gdParameterMetadata>;
ValueTypeMetadata: Class<gdValueTypeMetadata>;
VectorParameterMetadata: Class<gdVectorParameterMetadata>;
ParameterMetadataTools: Class<gdParameterMetadataTools>;
EventsFunctionTools: Class<gdEventsFunctionTools>;

View File

@@ -5,8 +5,6 @@ import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
import * as React from 'react';
import { Column, Line, Spacer } from '../../UI/Grid';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import { mapVector } from '../../Utils/MapFor';
import RaisedButton from '../../UI/RaisedButton';
import IconButton from '../../UI/IconButton';
@@ -17,8 +15,6 @@ import HelpButton from '../../UI/HelpButton';
import SemiControlledTextField from '../../UI/SemiControlledTextField';
import MiniToolbar, { MiniToolbarText } from '../../UI/MiniToolbar';
import { showWarningBox } from '../../UI/Messages/MessageBox';
import ObjectTypeSelector from '../../ObjectTypeSelector';
import BehaviorTypeSelector from '../../BehaviorTypeSelector';
import {
isBehaviorLifecycleEventsFunction,
isExtensionLifecycleEventsFunction,
@@ -26,10 +22,10 @@ import {
import { ParametersIndexOffsets } from '../../EventsFunctionsExtensionsLoader';
import Add from '@material-ui/icons/Add';
import DismissableAlertMessage from '../../UI/DismissableAlertMessage';
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../../UI/Layout';
import { ColumnStackLayout } from '../../UI/Layout';
import { getLastObjectParameterObjectType } from '../../EventsSheet/ParameterFields/ParameterMetadataTools';
import StringArrayEditor from '../../StringArrayEditor';
import newNameGenerator from '../../Utils/NewNameGenerator';
import ValueTypeEditor from './ValueTypeEditor';
const gd: libGDevelop = global.gd;
@@ -38,6 +34,7 @@ type Props = {|
eventsFunction: gdEventsFunction,
eventsBasedBehavior: ?gdEventsBasedBehavior,
eventsBasedObject: ?gdEventsBasedObject,
eventsFunctionsContainer: ?gdEventsFunctionsContainer,
onParametersUpdated: () => void,
helpPagePath?: string,
freezeParameters?: boolean,
@@ -97,27 +94,6 @@ const validateParameterName = (i18n: I18nType, newName: string) => {
return true;
};
const getExtraInfoArray = (parameter: gdParameterMetadata) => {
const extraInfoJson = parameter.getExtraInfo();
let array = [];
try {
if (extraInfoJson !== '') array = JSON.parse(extraInfoJson);
if (!Array.isArray(array)) array = [];
} catch (e) {
console.error('Cannot parse parameter extraInfo: ', e);
}
return array;
};
const getIdentifierScope = (scopedIdentifier: string) =>
scopedIdentifier.startsWith('object') ? 'object' : 'scene';
const getIdentifierName = (scopedIdentifier: string) =>
scopedIdentifier.startsWith('object')
? scopedIdentifier.substring('object'.length)
: scopedIdentifier.substring('scene'.length);
export default class EventsFunctionParametersEditor extends React.Component<
Props,
State
@@ -244,11 +220,11 @@ export default class EventsFunctionParametersEditor extends React.Component<
eventsFunction,
eventsBasedBehavior,
eventsBasedObject,
eventsFunctionsContainer,
freezeParameters,
helpPagePath,
} = this.props;
const parameters = eventsFunction.getParameters();
const isABehaviorLifecycleEventsFunction =
!!eventsBasedBehavior &&
isBehaviorLifecycleEventsFunction(eventsFunction.getName());
@@ -292,29 +268,48 @@ export default class EventsFunctionParametersEditor extends React.Component<
);
}
const parameters =
eventsFunctionsContainer &&
eventsFunction.getFunctionType() === gd.EventsFunction.ActionWithOperator
? eventsFunction.getParametersForEvents(eventsFunctionsContainer)
: eventsFunction.getParameters();
const firstParameterIndex = eventsBasedBehavior
? 2
: eventsBasedObject
? 1
: 0;
const isParameterDisabled = index => {
return (
!!freezeParameters ||
(!!eventsBasedBehavior && index < 2) ||
(!!eventsBasedObject && index < 1)
eventsFunction.getFunctionType() ===
gd.EventsFunction.ActionWithOperator ||
freezeParameters ||
index < firstParameterIndex
);
};
const isParameterDescriptionAndTypeShown = index => {
// The first two parameters of a behavior method should not be changed at all,
// so we even hide their description and type to avoid cluttering the interface.
// Same thing for an object which has mandatory Object parameter.
return (
(!eventsBasedBehavior && !eventsBasedObject) ||
(!!eventsBasedBehavior && index >= 2) ||
(!!eventsBasedObject && index >= 1)
);
// The first two parameters of a behavior method should not be changed at all,
// so we even hide their description and type to avoid cluttering the interface.
// Same thing for an object which has mandatory Object parameter.
const typeShownFirstIndex = firstParameterIndex;
const isParameterTypeShown = index => {
return index >= typeShownFirstIndex;
};
// The first two parameters of a behavior method should not be changed at all,
// so we even hide their description and type to avoid cluttering the interface.
// Same thing for an object which has mandatory Object parameter.
const labelShownFirstIndex =
firstParameterIndex +
(eventsFunction.getFunctionType() === gd.EventsFunction.ActionWithOperator
? 1
: 0);
const isParameterDescriptionShown = index => {
return index >= labelShownFirstIndex;
};
const isParameterLongDescriptionShown = (parameter, index): boolean => {
if (!isParameterDescriptionAndTypeShown(index)) return false;
return (
!!parameter.getLongDescription() ||
!!this.state.longDescriptionShownIndexes[index]
isParameterDescriptionShown(index) &&
(!!parameter.getLongDescription() ||
!!this.state.longDescriptionShownIndexes[index])
);
};
const parametersIndexOffset = eventsBasedBehavior
@@ -406,221 +401,19 @@ export default class EventsFunctionParametersEditor extends React.Component<
</MiniToolbar>
<Line>
<ColumnStackLayout expand>
<ResponsiveLineStackLayout noMargin>
{isParameterDescriptionAndTypeShown(i) && (
<SelectField
floatingLabelText={<Trans>Type</Trans>}
value={parameter.getType()}
onChange={(e, i, value: string) => {
parameter.setType(value);
parameter.setOptional(false);
parameter.setDefaultValue('');
this.forceUpdate();
this.props.onParametersUpdated();
}}
disabled={isParameterDisabled(i)}
fullWidth
>
<SelectOption
value="objectList"
primaryText={t`Objects`}
/>
<SelectOption
value="behavior"
primaryText={t`Behavior (for the previous object)`}
/>
<SelectOption
value="expression"
primaryText={t`Number`}
/>
<SelectOption
value="string"
primaryText={t`String (text)`}
/>
<SelectOption
value="stringWithSelector"
primaryText={t`String from a list of options (text)`}
/>
<SelectOption
value="key"
primaryText={t`Keyboard Key (text)`}
/>
<SelectOption
value="mouse"
primaryText={t`Mouse button (text)`}
/>
<SelectOption
value="color"
primaryText={t`Color (text)`}
/>
<SelectOption
value="layer"
primaryText={t`Layer (text)`}
/>
<SelectOption
value="sceneName"
primaryText={t`Scene name (text)`}
/>
<SelectOption
value="yesorno"
primaryText={t`Yes or No (boolean)`}
/>
<SelectOption
value="trueorfalse"
primaryText={t`True or False (boolean)`}
/>
<SelectOption
value="objectPointName"
primaryText={t`Object point (text)`}
/>
<SelectOption
value="objectAnimationName"
primaryText={t`Object animation (text)`}
/>
<SelectOption
value="identifier"
primaryText={t`Identifier (text)`}
/>
</SelectField>
)}
{gd.ParameterMetadata.isObject(
parameter.getType()
) && (
<ObjectTypeSelector
project={project}
value={parameter.getExtraInfo()}
onChange={(value: string) => {
parameter.setExtraInfo(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
disabled={isParameterDisabled(i)}
/>
)}
{parameter.getType() === 'behavior' && (
<BehaviorTypeSelector
project={project}
objectType={getLastObjectParameterObjectType(
parameters,
i
)}
value={parameter.getExtraInfo()}
onChange={(value: string) => {
parameter.setExtraInfo(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
disabled={isParameterDisabled(i)}
/>
)}
{parameter.getType() === 'yesorno' && (
<SelectField
floatingLabelText={<Trans>Default value</Trans>}
value={
parameter.getDefaultValue() === 'yes'
? 'yes'
: 'no'
}
onChange={(e, i, value) => {
parameter.setOptional(true);
parameter.setDefaultValue(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<SelectOption
value="yes"
primaryText={t`Yes`}
/>
<SelectOption value="no" primaryText={t`No`} />
</SelectField>
)}
{parameter.getType() === 'trueorfalse' && (
<SelectField
floatingLabelText={<Trans>Default value</Trans>}
value={
parameter.getDefaultValue() === 'True'
? 'True'
: 'False'
}
onChange={(e, i, value) => {
parameter.setOptional(true);
parameter.setDefaultValue(value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<SelectOption
value="True"
primaryText={t`True`}
/>
<SelectOption
value="False"
primaryText={t`False`}
/>
</SelectField>
)}
{parameter.getType() === 'identifier' && (
<SelectField
floatingLabelText={<Trans>Scope</Trans>}
value={getIdentifierScope(
parameter.getExtraInfo()
)}
onChange={(e, i, value) => {
const identifierName = getIdentifierName(
parameter.getExtraInfo()
);
parameter.setExtraInfo(
value + identifierName
);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
>
<SelectOption
value="scene"
primaryText={t`Scene`}
/>
<SelectOption
value="object"
primaryText={t`Object`}
/>
</SelectField>
)}
{parameter.getType() === 'identifier' && (
<SemiControlledTextField
commitOnBlur
floatingLabelText={
<Trans>Identifier name</Trans>
}
floatingLabelFixed
value={getIdentifierName(
parameter.getExtraInfo()
)}
onChange={value => {
const scope = getIdentifierScope(
parameter.getExtraInfo()
);
parameter.setExtraInfo(scope + value);
this.forceUpdate();
this.props.onParametersUpdated();
}}
fullWidth
/>
)}
</ResponsiveLineStackLayout>
{parameter.getType() === 'stringWithSelector' && (
<StringArrayEditor
extraInfo={getExtraInfoArray(parameter)}
setExtraInfo={this._setStringSelectorExtraInfo(
parameter
)}
/>
)}
{isParameterDescriptionAndTypeShown(i) && (
<ValueTypeEditor
project={project}
valueTypeMetadata={parameter.getValueTypeMetadata()}
disabled={isParameterDisabled(i)}
isTypeSelectorShown={isParameterTypeShown(i)}
onTypeUpdated={() =>
this.props.onParametersUpdated()
}
getLastObjectParameterObjectType={() =>
getLastObjectParameterObjectType(parameters, i)
}
/>
{isParameterDescriptionShown(i) && (
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Label</Trans>}
@@ -632,7 +425,8 @@ export default class EventsFunctionParametersEditor extends React.Component<
}}
fullWidth
disabled={
false /* Label, if shown, can always be changed */
/* When parameter are freezed, long description (if shown) can always be changed */
isParameterDisabled(i) && !freezeParameters
}
/>
)}
@@ -651,7 +445,8 @@ export default class EventsFunctionParametersEditor extends React.Component<
multiline
fullWidth
disabled={
false /* Long description, if shown, can always be changed */
/* When parameter are freezed, long description (if shown) can always be changed */
isParameterDisabled(i) && !freezeParameters
}
/>
)}
@@ -673,6 +468,10 @@ export default class EventsFunctionParametersEditor extends React.Component<
label={<Trans>Add a parameter</Trans>}
onClick={this._addParameter}
icon={<Add />}
disabled={
eventsFunction.getFunctionType() ===
gd.EventsFunction.ActionWithOperator
}
/>
)}
</Line>

View File

@@ -8,7 +8,7 @@ import * as React from 'react';
import { Column, Line, Spacer } from '../../UI/Grid';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import { mapVector } from '../../Utils/MapFor';
import { mapVector, mapFor } from '../../Utils/MapFor';
import HelpButton from '../../UI/HelpButton';
import SemiControlledTextField from '../../UI/SemiControlledTextField';
import {
@@ -22,13 +22,16 @@ import { type MessageDescriptor } from '../../Utils/i18n/MessageDescriptor.flow'
import { ResponsiveLineStackLayout, ColumnStackLayout } from '../../UI/Layout';
import DismissableAlertMessage from '../../UI/DismissableAlertMessage';
import SemiControlledAutoComplete from '../../UI/SemiControlledAutoComplete';
import ValueTypeEditor from './ValueTypeEditor';
const gd: libGDevelop = global.gd;
type Props = {|
project: gdProject,
eventsFunction: gdEventsFunction,
eventsBasedBehavior: ?gdEventsBasedBehavior,
eventsBasedObject: ?gdEventsBasedObject,
eventsFunctionsContainer: ?gdEventsFunctionsContainer,
helpPagePath?: string,
onConfigurationUpdated?: (whatChanged?: 'type') => void,
renderConfigurationHeader?: () => React.Node,
@@ -56,11 +59,19 @@ export const getSentenceErrorText = (
? ParametersIndexOffsets.ObjectFunction
: ParametersIndexOffsets.FreeFunction;
const type = eventsFunction.getFunctionType();
const param0isImplicit =
(eventsBasedBehavior || eventsBasedObject) &&
type === gd.EventsFunction.ExpressionAndCondition;
const missingParameters = mapVector(
eventsFunction.getParameters(),
(parameter, index) => {
if (gd.ParameterMetadata.isBehavior(parameter.getType())) {
return null; // Behaviors are usually not shown in sentences.
if (parameter.getValueTypeMetadata().isBehavior()) {
// Behaviors are usually not shown in sentences.
return null;
}
if (index === 0 && param0isImplicit) {
return null;
}
const expectedString = `_PARAM${index + parametersIndexOffset}_`;
@@ -103,27 +114,32 @@ export const getSentenceErrorText = (
return undefined;
};
const getFullNameHintText = (type: any): MessageDescriptor => {
const getFullNameHintText = (
type: EventsFunction_FunctionType,
expressionType: gdValueTypeMetadata
): MessageDescriptor => {
if (type === gd.EventsFunction.Condition) {
return t`Example: Is flashing`;
} else if (type === gd.EventsFunction.Expression) {
return t`Example: Remaining life`;
} else if (type === gd.EventsFunction.StringExpression) {
return t`Example: Equipped shield name`;
return expressionType.isNumber()
? t`Example: Remaining life`
: t`Example: Equipped shield name`;
}
return t`Example: Flash the object`;
};
const getDescriptionHintText = (type: any): MessageDescriptor => {
const getDescriptionHintText = (
type: EventsFunction_FunctionType,
expressionType: gdValueTypeMetadata
): MessageDescriptor => {
if (type === gd.EventsFunction.Condition) {
return t`Example: Check if the object is flashing.`;
} else if (type === gd.EventsFunction.Expression) {
return t`Example: Return the number of remaining lives for the player.`;
} else if (type === gd.EventsFunction.StringExpression) {
return t`Example: Return the name of the shield equipped by the player.`;
return expressionType.isNumber()
? t`Example: Return the number of remaining lives for the player.`
: t`Example: Return the name of the shield equipped by the player.`;
}
return t`Example: Make the object flash for 5 seconds.`;
};
@@ -133,6 +149,7 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
> {
render() {
const {
project,
eventsFunction,
freezeEventsFunctionType,
onConfigurationUpdated,
@@ -141,6 +158,7 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
eventsBasedBehavior,
eventsBasedObject,
getFunctionGroupNames,
eventsFunctionsContainer,
} = this.props;
const type = eventsFunction.getFunctionType();
@@ -205,6 +223,17 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
);
}
const getterFunction =
eventsFunctionsContainer &&
type === gd.EventsFunction.ActionWithOperator &&
eventsFunctionsContainer.hasEventsFunctionNamed(
eventsFunction.getGetterName()
)
? eventsFunctionsContainer.getEventsFunction(
eventsFunction.getGetterName()
)
: null;
return (
<I18n>
{({ i18n }) => (
@@ -217,8 +246,9 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
floatingLabelText={<Trans>Function type</Trans>}
fullWidth
disabled={!!freezeEventsFunctionType}
onChange={(e, i, value: string) => {
onChange={(e, i, valueString: string) => {
// $FlowFixMe
const value: EventsFunction_FunctionType = valueString;
eventsFunction.setFunctionType(value);
if (onConfigurationUpdated) onConfigurationUpdated('type');
this.forceUpdate();
@@ -237,86 +267,218 @@ export default class EventsFunctionPropertiesEditor extends React.Component<
primaryText={t`Expression`}
/>
<SelectOption
value={gd.EventsFunction.StringExpression}
primaryText={t`String Expression`}
value={gd.EventsFunction.ExpressionAndCondition}
primaryText={t`Expression and condition`}
/>
<SelectOption
value={gd.EventsFunction.ActionWithOperator}
primaryText={t`Action with operator`}
/>
</SelectField>
</Line>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Full name displayed in editor</Trans>}
translatableHintText={getFullNameHintText(type)}
value={eventsFunction.getFullName()}
onChange={text => {
eventsFunction.setFullName(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
fullWidth
/>
<Column expand noMargin>
{type === gd.EventsFunction.ActionWithOperator ? (
<SelectField
value={(getterFunction && getterFunction.getName()) || ''}
floatingLabelText={
<Trans>Related action and expression</Trans>
}
fullWidth
onChange={(e, i, value: string) => {
eventsFunction.setGetterName(value);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
>
{eventsFunctionsContainer
? mapFor(
0,
eventsFunctionsContainer.getEventsFunctionsCount(),
i => {
const eventsFunction = eventsFunctionsContainer.getEventsFunctionAt(
i
);
return (
eventsFunction.getFunctionType() ===
gd.EventsFunction.ExpressionAndCondition && (
<SelectOption
key={eventsFunction.getName()}
value={eventsFunction.getName()}
primaryText={
eventsFunction.getFullName() ||
eventsFunction.getName()
}
/>
)
);
}
)
: []}
</SelectField>
) : (
<SemiControlledTextField
commitOnBlur
floatingLabelText={
<Trans>Full name displayed in editor</Trans>
}
translatableHintText={getFullNameHintText(
type,
eventsFunction.getExpressionType()
)}
value={eventsFunction.getFullName()}
onChange={text => {
eventsFunction.setFullName(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
fullWidth
/>
)}
</Column>
<Column expand noMargin>
{type === gd.EventsFunction.ActionWithOperator ? (
<SemiControlledTextField
disabled
floatingLabelText={<Trans>Group name</Trans>}
fullWidth
value={getterFunction ? getterFunction.getGroup() : ''}
onChange={text => {}}
/>
) : (
<SemiControlledAutoComplete
floatingLabelText={<Trans>Group name</Trans>}
hintText={t`Leave it empty to use the default group for this extension.`}
fullWidth
value={eventsFunction.getGroup()}
onChange={text => {
eventsFunction.setGroup(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
dataSource={
getFunctionGroupNames
? getFunctionGroupNames().map(name => ({
text: name,
value: name,
}))
: []
}
openOnFocus={true}
/>
)}
</Column>
</ResponsiveLineStackLayout>
<Line noMargin>
<SemiControlledAutoComplete
floatingLabelText={<Trans>Group name</Trans>}
hintText={t`Leave it empty to use the default group for this extension.`}
fullWidth
value={eventsFunction.getGroup()}
onChange={text => {
eventsFunction.setGroup(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
dataSource={
getFunctionGroupNames
? getFunctionGroupNames().map(name => ({
text: name,
value: name,
}))
: []
}
openOnFocus={true}
/>
</Line>
<Line noMargin>
<SemiControlledTextField
commitOnBlur
floatingLabelText={
<Trans>Description, displayed in editor</Trans>
}
translatableHintText={getDescriptionHintText(type)}
fullWidth
multiline
value={eventsFunction.getDescription()}
onChange={text => {
eventsFunction.setDescription(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
/>
</Line>
<Line noMargin>
{type === gd.EventsFunction.Action ||
type === gd.EventsFunction.Condition ? (
{type === gd.EventsFunction.ActionWithOperator ? (
<SemiControlledTextField
disabled
commitOnBlur
floatingLabelText={
<Trans>Description, displayed in editor</Trans>
}
fullWidth
multiline
value={
getterFunction
? 'Change ' + getterFunction.getDescription()
: ''
}
onChange={text => {}}
/>
) : (
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Sentence in Events Sheet</Trans>}
translatableHintText={t`Note: write _PARAMx_ for parameters, e.g: Flash _PARAM1_ for 5 seconds`}
floatingLabelText={
type === gd.EventsFunction.ExpressionAndCondition ? (
<Trans>
Description, displayed in editor (automatically prefixed
by "Compare" or "Return")
</Trans>
) : (
<Trans>Description, displayed in editor</Trans>
)
}
translatableHintText={getDescriptionHintText(
type,
eventsFunction.getExpressionType()
)}
fullWidth
value={eventsFunction.getSentence()}
multiline
value={eventsFunction.getDescription()}
onChange={text => {
eventsFunction.setSentence(text);
eventsFunction.setDescription(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
errorText={getSentenceErrorText(
i18n,
eventsBasedBehavior,
eventsBasedObject,
eventsFunction
)}
/>
) : null}
)}
</Line>
{type === gd.EventsFunction.ActionWithOperator ? (
<Line noMargin>
<SemiControlledTextField
disabled
commitOnBlur
floatingLabelText={<Trans>Sentence in Events Sheet</Trans>}
fullWidth
value={
getterFunction
? 'Change ' +
getterFunction.getSentence() +
' of _PARAM0_'
: ''
}
onChange={text => {}}
/>
</Line>
) : (
(type === gd.EventsFunction.Action ||
type === gd.EventsFunction.Condition ||
type === gd.EventsFunction.ExpressionAndCondition) && (
<Line noMargin>
<SemiControlledTextField
commitOnBlur
floatingLabelText={
eventsBasedBehavior &&
type === gd.EventsFunction.ExpressionAndCondition ? (
<Trans>
Sentence in Events Sheet (automatically suffixed by
"of _PARAM0_")
</Trans>
) : (
<Trans>Sentence in Events Sheet</Trans>
)
}
translatableHintText={t`Note: write _PARAMx_ for parameters, e.g: Flash _PARAM1_ for 5 seconds`}
fullWidth
value={eventsFunction.getSentence()}
onChange={text => {
eventsFunction.setSentence(text);
if (onConfigurationUpdated) onConfigurationUpdated();
this.forceUpdate();
}}
errorText={getSentenceErrorText(
i18n,
eventsBasedBehavior,
eventsBasedObject,
eventsFunction
)}
/>
</Line>
)
)}
{eventsFunction.isExpression() && (
<ValueTypeEditor
isExpressionType
project={project}
valueTypeMetadata={eventsFunction.getExpressionType()}
isTypeSelectorShown={true}
onTypeUpdated={() => {
if (onConfigurationUpdated) onConfigurationUpdated();
}}
getLastObjectParameterObjectType={() => ''}
/>
)}
{helpPagePath ? (
<Line noMargin>
<HelpButton helpPagePath={helpPagePath} />

View File

@@ -0,0 +1,244 @@
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import { I18n } from '@lingui/react';
import * as React from 'react';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import SemiControlledTextField from '../../UI/SemiControlledTextField';
import ObjectTypeSelector from '../../ObjectTypeSelector';
import BehaviorTypeSelector from '../../BehaviorTypeSelector';
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../../UI/Layout';
import StringArrayEditor from '../../StringArrayEditor';
import useForceUpdate from '../../Utils/UseForceUpdate';
type Props = {|
project: gdProject,
valueTypeMetadata: gdValueTypeMetadata,
onTypeUpdated: () => void,
disabled?: boolean,
isTypeSelectorShown: boolean,
isExpressionType?: boolean,
getLastObjectParameterObjectType: () => string,
|};
const getExtraInfoArray = (type: gdValueTypeMetadata) => {
const extraInfoJson = type.getExtraInfo();
let array = [];
try {
if (extraInfoJson !== '') array = JSON.parse(extraInfoJson);
if (!Array.isArray(array)) array = [];
} catch (e) {
console.error('Cannot parse parameter extraInfo: ', e);
}
return array;
};
const getIdentifierScope = (scopedIdentifier: string) =>
scopedIdentifier.startsWith('object') ? 'object' : 'scene';
const getIdentifierName = (scopedIdentifier: string) =>
scopedIdentifier.startsWith('object')
? scopedIdentifier.substring('object'.length)
: scopedIdentifier.substring('scene'.length);
export default function ValueTypeEditor({
project,
valueTypeMetadata,
disabled,
isTypeSelectorShown,
onTypeUpdated,
getLastObjectParameterObjectType,
isExpressionType,
}: Props) {
const forceUpdate = useForceUpdate();
return (
<I18n>
{({ i18n }) => (
<ColumnStackLayout noMargin expand>
<ResponsiveLineStackLayout noMargin>
{isTypeSelectorShown && (
<SelectField
floatingLabelText={<Trans>Type</Trans>}
value={valueTypeMetadata.getName()}
onChange={(e, i, value: string) => {
valueTypeMetadata.setName(value);
valueTypeMetadata.setOptional(false);
valueTypeMetadata.setDefaultValue('');
forceUpdate();
onTypeUpdated();
}}
disabled={disabled}
fullWidth
>
{!isExpressionType && (
<SelectOption value="objectList" primaryText={t`Objects`} />
)}
{!isExpressionType && (
<SelectOption
value="behavior"
primaryText={t`Behavior (for the previous object)`}
/>
)}
<SelectOption value="expression" primaryText={t`Number`} />
<SelectOption value="string" primaryText={t`String (text)`} />
<SelectOption
value="stringWithSelector"
primaryText={t`String from a list of options (text)`}
/>
<SelectOption
value="key"
primaryText={t`Keyboard Key (text)`}
/>
<SelectOption
value="mouse"
primaryText={t`Mouse button (text)`}
/>
<SelectOption value="color" primaryText={t`Color (text)`} />
<SelectOption value="layer" primaryText={t`Layer (text)`} />
<SelectOption
value="sceneName"
primaryText={t`Scene name (text)`}
/>
{!isExpressionType && (
<SelectOption
value="yesorno"
primaryText={t`Yes or No (boolean)`}
/>
)}
{!isExpressionType && (
<SelectOption
value="trueorfalse"
primaryText={t`True or False (boolean)`}
/>
)}
<SelectOption
value="objectPointName"
primaryText={t`Object point (text)`}
/>
<SelectOption
value="objectAnimationName"
primaryText={t`Object animation (text)`}
/>
<SelectOption
value="identifier"
primaryText={t`Identifier (text)`}
/>
</SelectField>
)}
{valueTypeMetadata.isObject() && (
<ObjectTypeSelector
project={project}
value={valueTypeMetadata.getExtraInfo()}
onChange={(value: string) => {
valueTypeMetadata.setExtraInfo(value);
forceUpdate();
onTypeUpdated();
}}
disabled={disabled}
/>
)}
{valueTypeMetadata.isBehavior() && (
<BehaviorTypeSelector
project={project}
objectType={getLastObjectParameterObjectType()}
value={valueTypeMetadata.getExtraInfo()}
onChange={(value: string) => {
valueTypeMetadata.setExtraInfo(value);
forceUpdate();
onTypeUpdated();
}}
disabled={disabled}
/>
)}
{valueTypeMetadata.getName() === 'yesorno' && (
<SelectField
floatingLabelText={<Trans>Default value</Trans>}
value={
valueTypeMetadata.getDefaultValue() === 'yes' ? 'yes' : 'no'
}
onChange={(e, i, value) => {
valueTypeMetadata.setOptional(true);
valueTypeMetadata.setDefaultValue(value);
forceUpdate();
onTypeUpdated();
}}
fullWidth
>
<SelectOption value="yes" primaryText={t`Yes`} />
<SelectOption value="no" primaryText={t`No`} />
</SelectField>
)}
{valueTypeMetadata.getName() === 'trueorfalse' && (
<SelectField
floatingLabelText={<Trans>Default value</Trans>}
value={
valueTypeMetadata.getDefaultValue() === 'True'
? 'True'
: 'False'
}
onChange={(e, i, value) => {
valueTypeMetadata.setOptional(true);
valueTypeMetadata.setDefaultValue(value);
forceUpdate();
onTypeUpdated();
}}
fullWidth
>
<SelectOption value="True" primaryText={t`True`} />
<SelectOption value="False" primaryText={t`False`} />
</SelectField>
)}
{valueTypeMetadata.getName() === 'identifier' && (
<SelectField
floatingLabelText={<Trans>Scope</Trans>}
value={getIdentifierScope(valueTypeMetadata.getExtraInfo())}
onChange={(e, i, value) => {
const identifierName = getIdentifierName(
valueTypeMetadata.getExtraInfo()
);
valueTypeMetadata.setExtraInfo(value + identifierName);
forceUpdate();
onTypeUpdated();
}}
fullWidth
>
<SelectOption value="scene" primaryText={t`Scene`} />
<SelectOption value="object" primaryText={t`Object`} />
</SelectField>
)}
{valueTypeMetadata.getName() === 'identifier' && (
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Identifier name</Trans>}
floatingLabelFixed
value={getIdentifierName(valueTypeMetadata.getExtraInfo())}
onChange={value => {
const scope = getIdentifierScope(
valueTypeMetadata.getExtraInfo()
);
valueTypeMetadata.setExtraInfo(scope + value);
forceUpdate();
onTypeUpdated();
}}
fullWidth
/>
)}
</ResponsiveLineStackLayout>
{valueTypeMetadata.getName() === 'stringWithSelector' && (
<StringArrayEditor
disabled={disabled}
extraInfo={getExtraInfoArray(valueTypeMetadata)}
setExtraInfo={(newExtraInfo: Array<string>) => {
valueTypeMetadata.setExtraInfo(JSON.stringify(newExtraInfo));
forceUpdate();
onTypeUpdated();
}}
/>
)}
</ColumnStackLayout>
)}
</I18n>
);
}

View File

@@ -22,6 +22,7 @@ type Props = {|
eventsFunction: gdEventsFunction,
eventsBasedBehavior: ?gdEventsBasedBehavior,
eventsBasedObject: ?gdEventsBasedObject,
eventsFunctionsContainer: gdEventsFunctionsContainer,
onParametersOrGroupsUpdated: () => void,
helpPagePath?: string,
onConfigurationUpdated?: (whatChanged?: 'type') => void,
@@ -173,6 +174,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component<
onMoveBehaviorEventsParameter,
onMoveObjectEventsParameter,
getFunctionGroupNames,
eventsFunctionsContainer,
} = this.props;
return (
@@ -195,9 +197,11 @@ export default class EventsFunctionConfigurationEditor extends React.Component<
<ScrollView>
<Line>
<EventsFunctionPropertiesEditor
project={project}
eventsFunction={eventsFunction}
eventsBasedBehavior={eventsBasedBehavior}
eventsBasedObject={eventsBasedObject}
eventsFunctionsContainer={eventsFunctionsContainer}
helpPagePath={helpPagePath}
onConfigurationUpdated={onConfigurationUpdated}
renderConfigurationHeader={renderConfigurationHeader}
@@ -215,6 +219,7 @@ export default class EventsFunctionConfigurationEditor extends React.Component<
eventsFunction={eventsFunction}
eventsBasedBehavior={eventsBasedBehavior}
eventsBasedObject={eventsBasedObject}
eventsFunctionsContainer={eventsFunctionsContainer}
onParametersUpdated={onParametersOrGroupsUpdated}
helpPagePath={helpPagePath}
freezeParameters={freezeParameters}

View File

@@ -169,7 +169,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
project: gdProject,
eventsFunction: gdEventsFunction,
eventsBasedBehavior: ?gdEventsBasedBehavior,
eventsBasedObject: ?gdEventsBasedObject
eventsBasedObject: ?gdEventsBasedObject,
eventsFunctionsExtension: ?gdEventsFunctionsExtension
) => {
// Initialize this "context" of objects with the function
// (as done during code generation).
@@ -189,13 +190,18 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this._globalObjectsContainer,
this._objectsContainer
);
} else {
} else if (eventsFunctionsExtension) {
gd.EventsFunctionTools.freeEventsFunctionToObjectsContainer(
project,
eventsFunctionsExtension,
eventsFunction,
this._globalObjectsContainer,
this._objectsContainer
);
} else {
throw new Error(
'No extension, behavior or object was specified when loading a function'
);
}
};
@@ -258,7 +264,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this.props.project,
selectedEventsFunction,
selectedEventsBasedBehavior,
selectedEventsBasedObject
selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
this.setState(
{
@@ -592,7 +599,9 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this._loadEventsFunctionFrom(
this.props.project,
this.state.selectedEventsFunction,
this.state.selectedEventsBasedBehavior
this.state.selectedEventsBasedBehavior,
this.state.selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
}
};
@@ -611,7 +620,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this.props.project,
this.state.selectedEventsFunction,
this.state.selectedEventsBasedBehavior,
this.state.selectedEventsBasedObject
this.state.selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
}
};
@@ -825,7 +835,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this.props.project,
this.state.selectedEventsFunction,
this.state.selectedEventsBasedBehavior,
this.state.selectedEventsBasedObject
this.state.selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
}
}
@@ -867,7 +878,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
this.props.project,
this.state.selectedEventsFunction,
this.state.selectedEventsBasedBehavior,
this.state.selectedEventsBasedObject
this.state.selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
}
}
@@ -1000,6 +1012,9 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
editedEventsBasedObject,
} = this.state;
const selectedEventsBasedEntity =
selectedEventsBasedBehavior || selectedEventsBasedObject;
const editors = {
'choose-editor': {
type: 'primary',
@@ -1029,6 +1044,11 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
eventsFunction={selectedEventsFunction}
eventsBasedBehavior={selectedEventsBasedBehavior}
eventsBasedObject={selectedEventsBasedObject}
eventsFunctionsContainer={
(selectedEventsBasedEntity &&
selectedEventsBasedEntity.getEventsFunctions()) ||
eventsFunctionsExtension
}
globalObjectsContainer={this._globalObjectsContainer}
objectsContainer={this._objectsContainer}
onConfigurationUpdated={this._onConfigurationUpdated}
@@ -1045,7 +1065,8 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
project,
selectedEventsFunction,
selectedEventsBasedBehavior,
selectedEventsBasedObject
selectedEventsBasedObject,
this.props.eventsFunctionsExtension
);
this.forceUpdate();
}}

View File

@@ -2,6 +2,7 @@
import { type I18n as I18nType } from '@lingui/core';
import { t } from '@lingui/macro';
import { mapVector } from '../Utils/MapFor';
import { getFreeFunctionCodeName } from '.';
const gd: libGDevelop = global.gd;
// This file contains the logic to declare extension metadata from
@@ -301,6 +302,12 @@ export const isExtensionLifecycleEventsFunction = (functionName: string) => {
);
};
const removeTrailingDot = (description: string): string => {
return description.endsWith('.')
? description.slice(0, description.length - 1)
: description;
};
/**
* Declare the instruction (action/condition) or expression for the given
* (free) events function.
@@ -309,24 +316,80 @@ export const declareInstructionOrExpressionMetadata = (
extension: gdPlatformExtension,
eventsFunctionsExtension: gdEventsFunctionsExtension,
eventsFunction: gdEventsFunction
): gdInstructionMetadata | gdExpressionMetadata => {
):
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata => {
const functionType = eventsFunction.getFunctionType();
if (functionType === gd.EventsFunction.Expression) {
return extension.addExpression(
if (eventsFunction.getExpressionType().isNumber()) {
return extension.addExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() || '',
getExtensionIconUrl(extension)
);
} else {
return extension.addStrExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() || '',
getExtensionIconUrl(extension)
);
}
} else if (functionType === gd.EventsFunction.ExpressionAndCondition) {
return extension.addExpressionAndCondition(
gd.ValueTypeMetadata.getPrimitiveValueType(
eventsFunction.getExpressionType().getName()
),
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
removeTrailingDot(eventsFunction.getDescription()) ||
eventsFunction.getFullName(),
// An operator and an operand are inserted before user parameters.
shiftSentenceParamIndexes(eventsFunction.getSentence(), 2),
eventsFunction.getGroup() || '',
getExtensionIconUrl(extension)
);
} else if (functionType === gd.EventsFunction.StringExpression) {
return extension.addStrExpression(
} else if (functionType === gd.EventsFunction.ActionWithOperator) {
const getterFunction = eventsFunctionsExtension.hasEventsFunctionNamed(
eventsFunction.getGetterName()
)
? eventsFunctionsExtension.getEventsFunction(
eventsFunction.getGetterName()
)
: null;
const action = extension.addAction(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() || '',
(getterFunction && getterFunction.getFullName()) ||
eventsFunction.getName(),
'Change ' +
((getterFunction && getterFunction.getDescription()) ||
eventsFunction.getFullName()),
// An operator and an operand are inserted before user parameters.
getterFunction
? shiftSentenceParamIndexes(getterFunction.getSentence(), 2)
: '',
(getterFunction && getterFunction.getGroup()) || '',
getExtensionIconUrl(extension),
getExtensionIconUrl(extension)
);
if (getterFunction) {
action
.getCodeExtraInformation()
.setManipulatedType(
gd.ValueTypeMetadata.getPrimitiveValueType(
getterFunction.getExpressionType().getName()
)
)
// TODO Use an helper method
.setGetter(
getFreeFunctionCodeName(eventsFunctionsExtension, getterFunction)
);
}
return action;
} else if (functionType === gd.EventsFunction.Condition) {
return extension.addCondition(
eventsFunction.getName(),
@@ -350,6 +413,34 @@ export const declareInstructionOrExpressionMetadata = (
}
};
export const shiftSentenceParamIndexes = (
sentence: string,
offset: number
): string => {
const parameterIndexesStrings = sentence.match(/(?<=_PARAM)(\d+)(?=_)/g);
if (!parameterIndexesStrings) {
return sentence;
}
const parameterIndexes = parameterIndexesStrings.map(indexString =>
Number.parseInt(indexString)
);
const sentenceElements = sentence.split(/_PARAM\d+_/);
let shiftedSentence = '';
for (let index = 0; index < parameterIndexes.length; index++) {
shiftedSentence +=
sentenceElements[index] +
'_PARAM' +
(parameterIndexes[index] + offset) +
'_';
}
const sentenceIsEndingWithAnElement =
sentenceElements.length > parameterIndexes.length;
if (sentenceIsEndingWithAnElement) {
shiftedSentence += sentenceElements[sentenceElements.length - 1];
}
return shiftedSentence;
};
/**
* Declare the instruction (action/condition) or expression for the given
* behavior events function.
@@ -359,28 +450,84 @@ export const declareBehaviorInstructionOrExpressionMetadata = (
behaviorMetadata: gdBehaviorMetadata,
eventsBasedBehavior: gdEventsBasedBehavior,
eventsFunction: gdEventsFunction
): gdInstructionMetadata | gdExpressionMetadata => {
):
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata => {
const functionType = eventsFunction.getFunctionType();
if (functionType === gd.EventsFunction.Expression) {
return behaviorMetadata.addExpression(
if (eventsFunction.getExpressionType().isNumber()) {
return behaviorMetadata.addExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedBehavior.getFullName() ||
eventsBasedBehavior.getName(),
getExtensionIconUrl(extension)
);
} else {
return behaviorMetadata.addStrExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedBehavior.getFullName() ||
eventsBasedBehavior.getName(),
getExtensionIconUrl(extension)
);
}
} else if (functionType === gd.EventsFunction.ExpressionAndCondition) {
return behaviorMetadata.addExpressionAndCondition(
gd.ValueTypeMetadata.getPrimitiveValueType(
eventsFunction.getExpressionType().getName()
),
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedBehavior.getFullName() ||
eventsBasedBehavior.getName(),
removeTrailingDot(eventsFunction.getDescription()) ||
eventsFunction.getFullName(),
// An operator and an operand are inserted before user parameters.
shiftSentenceParamIndexes(eventsFunction.getSentence(), 2),
eventsFunction.getGroup() || '',
getExtensionIconUrl(extension)
);
} else if (functionType === gd.EventsFunction.StringExpression) {
return behaviorMetadata.addStrExpression(
} else if (functionType === gd.EventsFunction.ActionWithOperator) {
const eventsFunctionsContainer = eventsBasedBehavior.getEventsFunctions();
const getterFunction = eventsFunctionsContainer.hasEventsFunctionNamed(
eventsFunction.getGetterName()
)
? eventsFunctionsContainer.getEventsFunction(
eventsFunction.getGetterName()
)
: null;
const action = behaviorMetadata.addScopedAction(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
(getterFunction && getterFunction.getFullName()) ||
eventsFunction.getName(),
'Change ' +
((getterFunction && getterFunction.getDescription()) ||
eventsFunction.getFullName()),
// An operator and an operand are inserted before user parameters.
getterFunction
? shiftSentenceParamIndexes(getterFunction.getSentence(), 2)
: '',
(getterFunction && getterFunction.getGroup()) ||
eventsBasedBehavior.getFullName() ||
eventsBasedBehavior.getName(),
getExtensionIconUrl(extension),
getExtensionIconUrl(extension)
);
if (getterFunction) {
action
.getCodeExtraInformation()
.setManipulatedType(
gd.ValueTypeMetadata.getPrimitiveValueType(
getterFunction.getExpressionType().getName()
)
)
.setGetter(getterFunction.getName());
}
return action;
} else if (functionType === gd.EventsFunction.Condition) {
// Use the new "scoped" way to declare an instruction, because
// we want to prevent any conflict between free functions and
@@ -423,28 +570,84 @@ export const declareObjectInstructionOrExpressionMetadata = (
objectMetadata: gdObjectMetadata,
eventsBasedObject: gdEventsBasedObject,
eventsFunction: gdEventsFunction
): gdInstructionMetadata | gdExpressionMetadata => {
):
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata => {
const functionType = eventsFunction.getFunctionType();
if (functionType === gd.EventsFunction.Expression) {
return objectMetadata.addExpression(
if (eventsFunction.getExpressionType().isNumber()) {
return objectMetadata.addExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedObject.getFullName() ||
eventsBasedObject.getName(),
getExtensionIconUrl(extension)
);
} else {
return objectMetadata.addStrExpression(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedObject.getFullName() ||
eventsBasedObject.getName(),
getExtensionIconUrl(extension)
);
}
} else if (functionType === gd.EventsFunction.ExpressionAndCondition) {
return objectMetadata.addExpressionAndCondition(
gd.ValueTypeMetadata.getPrimitiveValueType(
eventsFunction.getExpressionType().getName()
),
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
eventsBasedObject.getFullName() ||
eventsBasedObject.getName(),
removeTrailingDot(eventsFunction.getDescription()) ||
eventsFunction.getFullName(),
// An operator and an operand are inserted before user parameters.
shiftSentenceParamIndexes(eventsFunction.getSentence(), 2),
eventsFunction.getGroup() || '',
getExtensionIconUrl(extension)
);
} else if (functionType === gd.EventsFunction.StringExpression) {
return objectMetadata.addStrExpression(
} else if (functionType === gd.EventsFunction.ActionWithOperator) {
const eventsFunctionsContainer = eventsBasedObject.getEventsFunctions();
const getterFunction = eventsFunctionsContainer.hasEventsFunctionNamed(
eventsFunction.getGetterName()
)
? eventsFunctionsContainer.getEventsFunction(
eventsFunction.getGetterName()
)
: null;
const action = objectMetadata.addScopedAction(
eventsFunction.getName(),
eventsFunction.getFullName() || eventsFunction.getName(),
eventsFunction.getDescription() || eventsFunction.getFullName(),
eventsFunction.getGroup() ||
(getterFunction && getterFunction.getFullName()) ||
eventsFunction.getName(),
'Change ' +
((getterFunction && getterFunction.getDescription()) ||
eventsFunction.getFullName()),
// An operator and an operand are inserted before user parameters.
getterFunction
? shiftSentenceParamIndexes(getterFunction.getSentence(), 2)
: '',
(getterFunction && getterFunction.getGroup()) ||
eventsBasedObject.getFullName() ||
eventsBasedObject.getName(),
getExtensionIconUrl(extension),
getExtensionIconUrl(extension)
);
if (getterFunction) {
action
.getCodeExtraInformation()
.setManipulatedType(
gd.ValueTypeMetadata.getPrimitiveValueType(
getterFunction.getExpressionType().getName()
)
)
.setGetter(getterFunction.getName());
}
return action;
} else if (functionType === gd.EventsFunction.Condition) {
// Use the new "scoped" way to declare an instruction, because
// we want to prevent any conflict between free functions and
@@ -498,19 +701,17 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
instructionOrExpression: T
): T => {
// By convention, first parameter is always the object:
instructionOrExpression.addParameter(
'object',
'Object',
'', // See below for adding the extra information
false
);
// Manually add the "extra info" without relying on addParameter
// as this method is prefixing the value passed with the extension namespace (this
// was done to ease extension declarations when dealing with object).
instructionOrExpression
.getParameter(instructionOrExpression.getParametersCount() - 1)
.setExtraInfo(eventsBasedBehavior.getObjectType());
.addParameter(
'object',
'Object',
'', // See below for adding the extra information
false
)
// Manually add the "extra info" without relying on addParameter
// as this method is prefixing the value passed with the extension namespace (this
// was done to ease extension declarations when dealing with object).
.setParameterExtraInfo(eventsBasedBehavior.getObjectType());
// By convention, second parameter is always the behavior:
instructionOrExpression.addParameter(
@@ -912,36 +1113,75 @@ export const declareObjectPropertiesInstructionAndExpressions = (
* expected by the events function.
*/
export const declareEventsFunctionParameters = (
eventsFunctionsContainer: gdEventsFunctionsContainer,
eventsFunction: gdEventsFunction,
instructionOrExpression: gdInstructionMetadata | gdExpressionMetadata
instructionOrExpression:
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata,
userDefinedFirstParameterIndex: number
) => {
mapVector(
eventsFunction.getParameters(),
(parameter: gdParameterMetadata) => {
if (!parameter.isCodeOnly()) {
instructionOrExpression.addParameter(
const addParameter = (parameter: gdParameterMetadata) => {
if (!parameter.isCodeOnly()) {
instructionOrExpression
.addParameter(
parameter.getType(),
parameter.getDescription(),
'', // See below for adding the extra information
parameter.isOptional()
);
instructionOrExpression.setParameterLongDescription(
parameter.getLongDescription()
);
instructionOrExpression.setDefaultValue(parameter.getDefaultValue());
} else {
instructionOrExpression.addCodeOnlyParameter(
parameter.getType(),
'' // See below for adding the extra information
);
}
// Manually add the "extra info" without relying on addParameter (or addCodeOnlyParameter)
// as these methods are prefixing the value passed with the extension namespace (this
// was done to ease extension declarations when dealing with object).
instructionOrExpression
.getParameter(instructionOrExpression.getParametersCount() - 1)
.setExtraInfo(parameter.getExtraInfo());
)
// Manually add the "extra info" without relying on addParameter (or addCodeOnlyParameter)
// as these methods are prefixing the value passed with the extension namespace (this
// was done to ease extension declarations when dealing with object).
.setParameterExtraInfo(parameter.getExtraInfo());
instructionOrExpression.setParameterLongDescription(
parameter.getLongDescription()
);
instructionOrExpression.setDefaultValue(parameter.getDefaultValue());
} else {
instructionOrExpression.addCodeOnlyParameter(
parameter.getType(),
parameter.getExtraInfo()
);
}
};
const functionType = eventsFunction.getFunctionType();
const getterFunction = eventsFunctionsContainer.hasEventsFunctionNamed(
eventsFunction.getGetterName()
)
? eventsFunctionsContainer.getEventsFunction(eventsFunction.getGetterName())
: null;
// This is used instead of getParametersForEvents because the Value parameter
// is already add by useStandardOperatorParameters.
const parameters = (functionType === gd.EventsFunction.ActionWithOperator &&
getterFunction
? getterFunction
: eventsFunction
).getParameters();
mapVector(
parameters,
(parameter: gdParameterMetadata, index: number) =>
index < userDefinedFirstParameterIndex && addParameter(parameter)
);
if (functionType === gd.EventsFunction.ExpressionAndCondition) {
((instructionOrExpression: any): gdMultipleInstructionMetadata).useStandardParameters(
eventsFunction ? eventsFunction.getExpressionType().getName() : 'string',
eventsFunction ? eventsFunction.getExpressionType().getExtraInfo() : ''
);
} else if (functionType === gd.EventsFunction.ActionWithOperator) {
((instructionOrExpression: any): gdInstructionMetadata).useStandardOperatorParameters(
getterFunction ? getterFunction.getExpressionType().getName() : 'string',
getterFunction ? getterFunction.getExpressionType().getExtraInfo() : ''
);
}
mapVector(
parameters,
(parameter: gdParameterMetadata, index: number) =>
index >= userDefinedFirstParameterIndex && addParameter(parameter)
);
// By convention, latest parameter is always the eventsFunctionContext of the calling function

View File

@@ -0,0 +1,91 @@
// @flow
import { shiftSentenceParamIndexes } from './MetadataDeclarationHelpers';
describe('shiftSentenceParamIndexes', () => {
it('give back the sentence when there is no parameters', () => {
expect(shiftSentenceParamIndexes('Make an action', 2)).toBe(
'Make an action'
);
});
it('can shift a parameter at the end', () => {
expect(shiftSentenceParamIndexes('Change the speed to _PARAM2_', 2)).toBe(
'Change the speed to _PARAM4_'
);
});
it('can shift a parameter at the beginning', () => {
expect(shiftSentenceParamIndexes('_PARAM2_ is moving', 2)).toBe(
'_PARAM4_ is moving'
);
});
it('can shift a parameter alone', () => {
expect(shiftSentenceParamIndexes('_PARAM2_', 2)).toBe('_PARAM4_');
});
it("won't shift an ill-formed parameter", () => {
expect(
shiftSentenceParamIndexes(
'The speed is greater than PARAM2_ pixels per second',
2
)
).toBe('The speed is greater than PARAM2_ pixels per second');
expect(
shiftSentenceParamIndexes(
'The speed is greater than _PARAM2 pixels per second',
2
)
).toBe('The speed is greater than _PARAM2 pixels per second');
expect(
shiftSentenceParamIndexes(
'The speed is greater than PARAM2 pixels per second',
2
)
).toBe('The speed is greater than PARAM2 pixels per second');
expect(
shiftSentenceParamIndexes(
'The speed is greater than _param2_ pixels per second',
2
)
).toBe('The speed is greater than _param2_ pixels per second');
expect(
shiftSentenceParamIndexes(
'The speed is greater than 2 pixels per second',
2
)
).toBe('The speed is greater than 2 pixels per second');
});
[2, 0, -2].forEach(indexOffset => {
it(`can shift 1 parameter by ${indexOffset}`, () => {
expect(
shiftSentenceParamIndexes(
'The speed is greater than _PARAM2_ pixels per second',
indexOffset
)
).toBe(
'The speed is greater than _PARAM' +
(2 + indexOffset) +
'_ pixels per second'
);
});
it(`can shift 2 parameters by ${indexOffset}`, () => {
expect(
shiftSentenceParamIndexes(
'The speed is between _PARAM1_ and _PARAM2_ pixels per second',
indexOffset
)
).toBe(
`The speed is between _PARAM${1 + indexOffset}_ and _PARAM${2 +
indexOffset}_ pixels per second`
);
});
it(`can shift 2 parameters with jumbled indexes by ${indexOffset}`, () => {
expect(
shiftSentenceParamIndexes(
'The speed is between _PARAM3_ and _PARAM2_ pixels per second',
indexOffset
)
).toBe(
`The speed is between _PARAM${3 + indexOffset}_ and _PARAM${2 +
indexOffset}_ pixels per second`
);
});
});
});

View File

@@ -50,6 +50,12 @@ const mangleName = (name: string) => {
return caseSensitiveSlug(name, '_', []);
};
const getExtensionCodeNamespacePrefix = (
eventsFunctionsExtension: gdEventsFunctionsExtension
) => {
return 'gdjs.evtsExt__' + mangleName(eventsFunctionsExtension.getName());
};
/** Generate the namespace for a free function. */
const getFreeFunctionCodeNamespace = (
eventsFunction: gdEventsFunction,
@@ -58,6 +64,18 @@ const getFreeFunctionCodeNamespace = (
return codeNamespacePrefix + '__' + mangleName(eventsFunction.getName());
};
export const getFreeFunctionCodeName = (
eventsFunctionsExtension: gdEventsFunctionsExtension,
eventsFunction: gdEventsFunction
) => {
return (
getFreeFunctionCodeNamespace(
eventsFunction,
getExtensionCodeNamespacePrefix(eventsFunctionsExtension)
) + '.func'
);
};
/** Generate the namespace for a behavior function. */
const getBehaviorFunctionCodeNamespace = (
eventsBasedBehavior: gdEventsBasedBehavior,
@@ -248,7 +266,10 @@ const generateFreeFunction = (
codeGenerationContext: CodeGenerationContext
): Promise<{
functionFile: string,
functionMetadata: gdInstructionMetadata | gdExpressionMetadata,
functionMetadata:
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata,
}> => {
const instructionOrExpression = declareInstructionOrExpressionMetadata(
extension,
@@ -257,7 +278,12 @@ const generateFreeFunction = (
);
// By convention, first parameter is always the Runtime Scene.
instructionOrExpression.addCodeOnlyParameter('currentScene', '');
declareEventsFunctionParameters(eventsFunction, instructionOrExpression);
declareEventsFunctionParameters(
eventsFunctionsExtension,
eventsFunction,
instructionOrExpression,
0
);
// Hide "lifecycle" functions as they are called automatically by
// the game engine.
@@ -272,17 +298,16 @@ const generateFreeFunction = (
);
const functionName = codeNamespace + '.func';
const codeExtraInformation = instructionOrExpression.getCodeExtraInformation();
const functionFile = options.eventsFunctionCodeWriter.getIncludeFileFor(
functionName
);
codeExtraInformation
instructionOrExpression
.setIncludeFile(functionFile)
.setFunctionName(functionName);
// Always include the extension include files when using a free function.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
codeExtraInformation.addIncludeFile(includeFile);
instructionOrExpression.addIncludeFile(includeFile);
});
if (!options.skipCodeGeneration) {
@@ -291,6 +316,7 @@ const generateFreeFunction = (
project
);
const code = eventsFunctionsExtensionCodeGenerator.generateFreeEventsFunctionCompleteCode(
eventsFunctionsExtension,
eventsFunction,
codeNamespace,
includeFiles,
@@ -307,7 +333,7 @@ const generateFreeFunction = (
.toNewVectorString()
.toJSArray()
.forEach((includeFile: string) => {
codeExtraInformation.addIncludeFile(includeFile);
instructionOrExpression.addIncludeFile(includeFile);
});
includeFiles.delete();
@@ -338,7 +364,10 @@ const generateFreeFunction = (
const applyFunctionIncludeFilesDependencyTransitivity = (
functionInfos: Array<{
functionFile: string,
functionMetadata: gdInstructionMetadata | gdExpressionMetadata,
functionMetadata:
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata,
}>
): void => {
// Note that the iteration order doesn't matter, for instance for:
@@ -362,12 +391,7 @@ const applyFunctionIncludeFilesDependencyTransitivity = (
// c -> d
const includeFileSets = functionInfos.map(
functionInfo =>
new Set(
functionInfo.functionMetadata
.getCodeExtraInformation()
.getIncludeFiles()
.toJSArray()
)
new Set(functionInfo.functionMetadata.getIncludeFiles().toJSArray())
);
// For any function A of the extension...
for (let index = 0; index < functionInfos.length; index++) {
@@ -376,9 +400,7 @@ const applyFunctionIncludeFilesDependencyTransitivity = (
// ...and any function B of the extension...
for (let otherIndex = 0; otherIndex < functionInfos.length; otherIndex++) {
const otherCodeExtraInformation = functionInfos[
otherIndex
].functionMetadata.getCodeExtraInformation();
const otherFunctionMetadata = functionInfos[otherIndex].functionMetadata;
const otherIncludeFileSet = includeFileSets[otherIndex];
// ...where function B depends on function A...
if (otherIncludeFileSet.has(functionIncludeFile)) {
@@ -386,7 +408,7 @@ const applyFunctionIncludeFilesDependencyTransitivity = (
includeFiles.forEach(includeFile => {
if (!otherIncludeFileSet.has(includeFile)) {
otherIncludeFileSet.add(includeFile);
otherCodeExtraInformation.addIncludeFile(includeFile);
otherFunctionMetadata.addIncludeFile(includeFile);
}
});
}
@@ -450,7 +472,12 @@ function generateBehavior(
eventsBasedBehavior,
eventsFunction
);
declareEventsFunctionParameters(eventsFunction, instructionOrExpression);
declareEventsFunctionParameters(
eventsFunctionsContainer,
eventsFunction,
instructionOrExpression,
2
);
// Hide "lifecycle" methods as they are called automatically by
// the game engine.
@@ -460,8 +487,7 @@ function generateBehavior(
if (eventsFunction.isPrivate()) instructionOrExpression.setPrivate();
const codeExtraInformation = instructionOrExpression.getCodeExtraInformation();
codeExtraInformation
instructionOrExpression
.setIncludeFile(includeFile)
.setFunctionName(eventsFunctionMangledName);
});
@@ -567,7 +593,12 @@ function generateObject(
eventsBasedObject,
eventsFunction
);
declareEventsFunctionParameters(eventsFunction, instructionOrExpression);
declareEventsFunctionParameters(
eventsFunctionsContainer,
eventsFunction,
instructionOrExpression,
1
);
// Hide "lifecycle" methods as they are called automatically by
// the game engine.
@@ -577,8 +608,7 @@ function generateObject(
if (eventsFunction.isPrivate()) instructionOrExpression.setPrivate();
const codeExtraInformation = instructionOrExpression.getCodeExtraInformation();
codeExtraInformation
instructionOrExpression
.setIncludeFile(includeFile)
.setFunctionName(eventsFunctionMangledName);
});

View File

@@ -145,6 +145,7 @@ export default class EventsFunctionsList extends React.Component<Props, State> {
default:
return 'res/functions/function.svg';
case gd.EventsFunction.Action:
case gd.EventsFunction.ActionWithOperator:
switch (eventsFunction.getName()) {
default:
return 'res/functions/action.svg';
@@ -178,8 +179,7 @@ export default class EventsFunctionsList extends React.Component<Props, State> {
case gd.EventsFunction.Condition:
return 'res/functions/condition.svg';
case gd.EventsFunction.Expression:
return 'res/functions/expression.svg';
case gd.EventsFunction.StringExpression:
case gd.EventsFunction.ExpressionAndCondition:
return 'res/functions/expression.svg';
}
};

View File

@@ -286,9 +286,11 @@ export default class EventsFunctionExtractorDialog extends React.Component<
) : null}
</Column>
<EventsFunctionPropertiesEditor
project={project}
eventsFunction={eventsFunction}
eventsBasedBehavior={null}
eventsBasedObject={null}
eventsFunctionsContainer={null}
onConfigurationUpdated={() => {
// Force re-running logic to see if Create button is disabled.
this.forceUpdate();
@@ -302,6 +304,7 @@ export default class EventsFunctionExtractorDialog extends React.Component<
eventsFunction={eventsFunction}
eventsBasedBehavior={null}
eventsBasedObject={null}
eventsFunctionsContainer={null}
onParametersUpdated={() => {
// Force the dialog to adapt its size
this.forceUpdate();

View File

@@ -3,13 +3,16 @@ import { mapVector } from '../../Utils/MapFor';
const gd: libGDevelop = global.gd;
export const enumerateParametersUsableInExpressions = (
eventsFunctionsContainer: gdEventsFunctionsContainer,
eventsFunction: gdEventsFunction
): Array<gdParameterMetadata> => {
return mapVector(eventsFunction.getParameters(), parameterMetadata =>
parameterMetadata.isCodeOnly() ||
gd.ParameterMetadata.isObject(parameterMetadata.getType()) ||
gd.ParameterMetadata.isBehavior(parameterMetadata.getType())
? null
: parameterMetadata
return mapVector(
eventsFunction.getParametersForEvents(eventsFunctionsContainer),
parameterMetadata =>
parameterMetadata.isCodeOnly() ||
gd.ParameterMetadata.isObject(parameterMetadata.getType()) ||
gd.ParameterMetadata.isBehavior(parameterMetadata.getType())
? null
: parameterMetadata
).filter(Boolean);
};

View File

@@ -16,15 +16,22 @@ export default class FunctionParameterNameField extends Component<
}
render() {
const parameterNames: Array<ExpressionAutocompletion> = this.props.scope
.eventsFunction
? enumerateParametersUsableInExpressions(
this.props.scope.eventsFunction
).map(parameterMetadata => ({
kind: 'Text',
completion: `"${parameterMetadata.getName()}"`,
}))
: [];
const eventsBasedEntity =
this.props.scope.eventsBasedBehavior ||
this.props.scope.eventsBasedObject;
const functionsContainer = eventsBasedEntity
? eventsBasedEntity.getEventsFunctions()
: this.props.scope.eventsFunctionsExtension;
const parameterNames: Array<ExpressionAutocompletion> =
this.props.scope.eventsFunction && functionsContainer
? enumerateParametersUsableInExpressions(
functionsContainer,
this.props.scope.eventsFunction
).map(parameterMetadata => ({
kind: 'Text',
completion: `"${parameterMetadata.getName()}"`,
}))
: [];
return (
<GenericExpressionField

View File

@@ -357,8 +357,14 @@ const getAutocompletionsForText = function(
return [];
}
} else if (type === 'functionParameterName') {
if (scope.eventsFunction) {
const eventsBasedEntity =
scope.eventsBasedBehavior || scope.eventsBasedObject;
const functionsContainer = eventsBasedEntity
? eventsBasedEntity.getEventsFunctions()
: scope.eventsFunctionsExtension;
if (scope.eventsFunction && functionsContainer) {
autocompletionTexts = enumerateParametersUsableInExpressions(
functionsContainer,
scope.eventsFunction
).map(parameterMetadata => `"${parameterMetadata.getName()}"`);
}

View File

@@ -13,11 +13,13 @@ import Add from '@material-ui/icons/Add';
type StringArrayEditorProps = {|
extraInfo: Array<string>,
setExtraInfo: (Array<string>) => void,
disabled?: boolean,
|};
const StringArrayEditor = ({
extraInfo,
setExtraInfo,
disabled,
}: StringArrayEditorProps) => {
const updateExtraInfo = () => setExtraInfo(extraInfo);
@@ -27,6 +29,7 @@ const StringArrayEditor = ({
{extraInfo.map((item, index) => (
<Line key={index} justifyContent="flex-end" expand>
<SemiControlledTextField
disabled={disabled}
commitOnBlur
value={item}
onChange={text => {
@@ -36,6 +39,7 @@ const StringArrayEditor = ({
fullWidth
/>
<IconButton
disabled={disabled}
tooltip={t`Delete option`}
onClick={() => {
extraInfo.splice(index, 1);
@@ -49,6 +53,7 @@ const StringArrayEditor = ({
<Line justifyContent="flex-end" expand>
<RaisedButton
disabled={disabled}
primary
onClick={() => {
extraInfo.push('New Option');

View File

@@ -1,11 +1,11 @@
// @flow
// Note: this file does not use export/imports and use Flow comments to allow its usage from Node.js
const mapFor = /*:: <T>*/ (
const mapFor = /*:: <T> */ (
start /*: number */,
end /*: number */,
func /*: (number) => T */
) /*:Array<T> */ => {
) /*: Array<T> */ => {
const result = [];
for (let i = start; i < end; i++) {
result.push(func(i));
@@ -13,11 +13,11 @@ const mapFor = /*:: <T>*/ (
return result;
};
const mapReverseFor = /*:: <T>*/ (
const mapReverseFor = /*:: <T> */ (
start /*: number */,
end /*: number */,
func /*: (number) => T */
) /*:Array<T> */ => {
) /*: Array<T> */ => {
const result = [];
for (let i = end - 1; i >= start; i--) {
result.push(func(i));
@@ -32,10 +32,10 @@ type CppVector<T> = {
}
*/
const mapVector = /*:: <T, U>*/ (
const mapVector = /*:: <T, U> */ (
cppVector /*: CppVector<T> */,
func /*: (T, number) => U */
) /*:Array<U> */ => {
) /*: Array<U> */ => {
return mapFor(0, cppVector.size(), i => func(cppVector.at(i), i));
};

View File

@@ -27,6 +27,7 @@ export const DefaultFreeFunction = () => (
eventsFunction={testProject.testEventsFunction}
eventsBasedBehavior={null}
eventsBasedObject={null}
eventsFunctionsContainer={testProject.testEventsFunctionsExtension}
onParametersOrGroupsUpdated={action('Parameters or groups were updated')}
/>
</FixedHeightFlexContainer>
@@ -42,6 +43,7 @@ export const DefaultBehaviorFunction = () => (
eventsFunction={testProject.testBehaviorEventsFunction}
eventsBasedBehavior={testProject.testEventsBasedBehavior}
eventsBasedObject={null}
eventsFunctionsContainer={testProject.testEventsBasedBehavior.getEventsFunctions()}
onParametersOrGroupsUpdated={action('Parameters or groups were updated')}
/>
</FixedHeightFlexContainer>
@@ -57,6 +59,7 @@ export const DefaultBehaviorLifecycleFunction = () => (
eventsFunction={testProject.testBehaviorLifecycleEventsFunction}
eventsBasedBehavior={testProject.testEventsBasedBehavior}
eventsBasedObject={null}
eventsFunctionsContainer={testProject.testEventsBasedBehavior.getEventsFunctions()}
onParametersOrGroupsUpdated={action('Parameters or groups were updated')}
/>
</FixedHeightFlexContainer>
@@ -72,6 +75,7 @@ export const DefaultObjectFunction = () => (
eventsFunction={testProject.testObjectEventsFunction}
eventsBasedBehavior={null}
eventsBasedObject={testProject.testEventsBasedObject}
eventsFunctionsContainer={testProject.testEventsBasedObject.getEventsFunctions()}
onParametersOrGroupsUpdated={action('Parameters or groups were updated')}
/>
</FixedHeightFlexContainer>

View File

@@ -88,21 +88,14 @@ import PlaceholderLoader from '../UI/PlaceholderLoader';
import ColorField from '../UI/ColorField';
import EmptyMessage from '../UI/EmptyMessage';
import BackgroundText from '../UI/BackgroundText';
import EventsFunctionConfigurationEditor from '../EventsFunctionsExtensionEditor/EventsFunctionConfigurationEditor';
import EventsFunctionsList from '../EventsFunctionsList';
import EventsFunctionsExtensionEditor from '../EventsFunctionsExtensionEditor';
import OptionsEditorDialog from '../EventsFunctionsExtensionEditor/OptionsEditorDialog';
import ProjectManager from '../ProjectManager';
import AlertMessage from '../UI/AlertMessage';
import ChangelogRenderer from '../MainFrame/Changelog/ChangelogRenderer';
import ChangelogDialog from '../MainFrame/Changelog/ChangelogDialog';
import EventsFunctionExtractorDialog from '../EventsSheet/EventsFunctionExtractor/EventsFunctionExtractorDialog';
import FixedHeightFlexContainer from './FixedHeightFlexContainer';
import EventsBasedBehaviorEditor from '../EventsBasedBehaviorEditor';
import EventsBasedBehaviorEditorDialog from '../EventsBasedBehaviorEditor/EventsBasedBehaviorEditorDialog';
import BehaviorTypeSelector from '../BehaviorTypeSelector';
import ObjectTypeSelector from '../ObjectTypeSelector';
import EventsFunctionsExtensionsProvider from '../EventsFunctionsExtensionsLoader/EventsFunctionsExtensionsProvider';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import SemiControlledAutoComplete from '../UI/SemiControlledAutoComplete';
import SemiControlledMultiAutoComplete from '../UI/SemiControlledMultiAutoComplete';
@@ -121,7 +114,6 @@ import EditorMosaic from '../UI/EditorMosaic';
import FlatButton from '../UI/FlatButton';
import EditorMosaicPlayground from './EditorMosaicPlayground';
import EditorNavigator from '../UI/EditorMosaic/EditorNavigator';
import ChooseEventsFunctionsExtensionEditor from '../EventsFunctionsExtensionEditor/ChooseEventsFunctionsExtensionEditor';
import PropertiesEditor from '../PropertiesEditor';
import { OpenConfirmDialog } from '../ProjectsStorage/OpenConfirmDialog';
import BrowserPreviewErrorDialog from '../Export/BrowserExporters/BrowserS3PreviewLauncher/BrowserPreviewErrorDialog';