Compare commits

..

44 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
D8H
71f20d7852 Refactor to make some ParameterMetadata attributes private (type, supplementaryInformation and optional) (#4437) 2022-10-29 13:02:18 +02:00
D8H
9d121d0085 Suffix properties private instruction names with "property" to avoid them to overlap public ones (#4436) 2022-10-29 13:01:07 +02:00
Clément Pasteau
6292e338bc Fix importing path correctly on web (#4458) 2022-10-28 17:58:24 +02:00
Clément Pasteau
c5eb0bcc00 Bump version to 5.1.149 (#4451) 2022-10-28 11:23:50 +02:00
github-actions[bot]
a732fda4d9 Update translations [skip ci] (#4430)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2022-10-28 11:20:33 +02:00
Clément Pasteau
398bff8492 Fix input not being positioned properly (#4450) 2022-10-28 11:11:24 +02:00
D8H
f30e92a953 Make scene properties look the same as behavior properties (#4448)
* For instance, scene properties are used by the Physics2 behavior.
2022-10-27 21:48:52 +02:00
AlexandreS
8210c25acb UI improvements (#4440)
- Make some popovers and panels more discernable from the background
- Use the same drag and drop behavior for layers list as for the objects list on the scene editor
- Add object icons in the list of objects in a group
2022-10-27 10:23:05 +02:00
AlexandreS
6a13940e17 Add possibility to send instances to back or to front (Z order) in context menu (#4443) 2022-10-27 09:47:54 +02:00
AlexandreS
622aa7c08c Add warning message when updating liluo.io thumbnail from the project icons dialog (#4438) 2022-10-26 11:52:49 +02:00
Clément Pasteau
a71558a490 Create condition to know when a draggable object was just dropped (#4441) 2022-10-26 09:31:14 +02:00
Clément Pasteau
37539aa788 Rename Panel actions for consistency (#4439) 2022-10-25 18:45:23 +02:00
AlexandreS
789f819f25 Fix events sheet not wrapping on small screens (#4434) 2022-10-25 10:43:27 +02:00
AlexandreS
52ebfb8100 Remove starting value in tween variable actions
Also:
- Change phrasing for object tweens
2022-10-25 08:53:37 +02:00
Clément Pasteau
ecc5c689d2 Revert service worker update (#4432)
Do not show in changelog
2022-10-24 18:48:30 +02:00
Florian Rival
5b1e169557 Show variables that were used in the events, but not defined, in the autocompletions by default
* If you've not activated this since this was introduced, you can do so in the preferences.
2022-10-24 17:43:30 +02:00
170 changed files with 3826 additions and 1402 deletions

View File

@@ -43,7 +43,7 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
std::size_t relationalOperatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "relationalOperator")
if (instrInfos.parameters[i].GetType() == "relationalOperator")
relationalOperatorIndex = i;
}
// Ensure that there is at least one parameter after the relational operator
@@ -95,7 +95,7 @@ gd::String EventsCodeGenerator::GenerateOperatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -164,7 +164,7 @@ gd::String EventsCodeGenerator::GenerateCompoundOperatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -215,7 +215,7 @@ gd::String EventsCodeGenerator::GenerateMutatorCall(
std::size_t operatorIndex = instrInfos.parameters.size();
for (std::size_t i = startFromArgument; i < instrInfos.parameters.size();
++i) {
if (instrInfos.parameters[i].type == "operator") operatorIndex = i;
if (instrInfos.parameters[i].GetType() == "operator") operatorIndex = i;
}
// Ensure that there is at least one parameter after the operator
@@ -293,7 +293,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
// Verify that there are no mismatchs between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].type)) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter =
condition.GetParameter(pNb).GetPlainString();
@@ -303,11 +303,11 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
}
@@ -485,7 +485,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
// Verify that there are no mismatchs between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].type)) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
@@ -493,11 +493,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
objectInParameter)) {
return "/* Unknown object - skipped. */";
} else if (!instrInfos.parameters[pNb].supplementaryInformation.empty() &&
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
GetObjectsAndGroups(),
objectInParameter) !=
instrInfos.parameters[pNb].supplementaryInformation) {
instrInfos.parameters[pNb].GetExtraInfo()) {
return "/* Mismatched object type - skipped. */";
}
}
@@ -679,21 +679,21 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
supplementaryParametersTypes) {
gd::String argOutput;
if (ParameterMetadata::IsExpression("number", metadata.type)) {
if (ParameterMetadata::IsExpression("number", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "number", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("string", metadata.type)) {
} else if (ParameterMetadata::IsExpression("string", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, "string", parameter, lastObjectName);
} else if (ParameterMetadata::IsExpression("variable", metadata.type)) {
} else if (ParameterMetadata::IsExpression("variable", metadata.GetType())) {
argOutput = gd::ExpressionCodeGenerator::GenerateExpressionCode(
*this, context, metadata.type, parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.type)) {
*this, context, metadata.GetType(), parameter, lastObjectName);
} else if (ParameterMetadata::IsObject(metadata.GetType())) {
// It would be possible to run a gd::ExpressionCodeGenerator if later
// objects can have nested objects, or function returning objects.
argOutput =
GenerateObject(parameter.GetPlainString(), metadata.type, context);
} else if (metadata.type == "relationalOperator") {
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
} else if (metadata.GetType() == "relationalOperator") {
auto parameterString = parameter.GetPlainString();
argOutput += parameterString == "=" ? "==" : parameterString;
if (argOutput != "==" && argOutput != "<" && argOutput != ">" &&
@@ -703,7 +703,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
}
argOutput = "\"" + argOutput + "\"";
} else if (metadata.type == "operator") {
} else if (metadata.GetType() == "operator") {
argOutput += parameter.GetPlainString();
if (argOutput != "=" && argOutput != "+" && argOutput != "-" &&
argOutput != "/" && argOutput != "*") {
@@ -712,28 +712,28 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
}
argOutput = "\"" + argOutput + "\"";
} else if (ParameterMetadata::IsBehavior(metadata.type)) {
} else if (ParameterMetadata::IsBehavior(metadata.GetType())) {
argOutput = GenerateGetBehaviorNameCode(parameter.GetPlainString());
} else if (metadata.type == "key") {
} else if (metadata.GetType() == "key") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "audioResource" ||
metadata.type == "bitmapFontResource" ||
metadata.type == "fontResource" ||
metadata.type == "imageResource" ||
metadata.type == "jsonResource" ||
metadata.type == "videoResource" ||
} else if (metadata.GetType() == "audioResource" ||
metadata.GetType() == "bitmapFontResource" ||
metadata.GetType() == "fontResource" ||
metadata.GetType() == "imageResource" ||
metadata.GetType() == "jsonResource" ||
metadata.GetType() == "videoResource" ||
// Deprecated, old parameter names:
metadata.type == "password" || metadata.type == "musicfile" ||
metadata.type == "soundfile" || metadata.type == "police") {
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "mouse") {
} else if (metadata.GetType() == "mouse") {
argOutput = "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
} else if (metadata.type == "yesorno") {
} else if (metadata.GetType() == "yesorno") {
auto parameterString = parameter.GetPlainString();
argOutput += (parameterString == "yes" || parameterString == "oui")
? GenerateTrue()
: GenerateFalse();
} else if (metadata.type == "trueorfalse") {
} else if (metadata.GetType() == "trueorfalse") {
auto parameterString = parameter.GetPlainString();
// This is duplicated in AdvancedExtension.cpp for GDJS
argOutput += (parameterString == "True" || parameterString == "Vrai")
@@ -741,21 +741,21 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
: GenerateFalse();
}
// Code only parameter type
else if (metadata.type == "inlineCode") {
argOutput += metadata.supplementaryInformation;
else if (metadata.GetType() == "inlineCode") {
argOutput += metadata.GetExtraInfo();
} else {
// Try supplementary types if provided
if (supplementaryParametersTypes) {
for (std::size_t i = 0; i < supplementaryParametersTypes->size(); ++i) {
if ((*supplementaryParametersTypes)[i].first == metadata.type)
if ((*supplementaryParametersTypes)[i].first == metadata.GetType())
argOutput += (*supplementaryParametersTypes)[i].second;
}
}
// Type unknown
if (argOutput.empty()) {
if (!metadata.type.empty())
cout << "Warning: Unknown type of parameter \"" << metadata.type
if (!metadata.GetType().empty())
cout << "Warning: Unknown type of parameter \"" << metadata.GetType()
<< "\"." << std::endl;
argOutput += "\"" + ConvertToString(parameter.GetPlainString()) + "\"";
}
@@ -1030,7 +1030,7 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
for (std::size_t i = 0; i < instrInfos.parameters.size();
++i) // Some conditions already have a "conditionInverted" parameter
{
if (instrInfos.parameters[i].type == "conditionInverted")
if (instrInfos.parameters[i].GetType() == "conditionInverted")
conditionAlreadyTakeCareOfInversion = true;
}
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
@@ -1051,7 +1051,7 @@ gd::String EventsCodeGenerator::GenerateObjectCondition(
// Prepare call
// Add a static_cast if necessary
gd::String objectFunctionCallNamePart =
(!instrInfos.parameters[0].supplementaryInformation.empty())
(!instrInfos.parameters[0].GetExtraInfo().empty())
? "static_cast<" + objInfo.className + "*>(" +
GetObjectListName(objectName, context) + "[i])->" +
instrInfos.codeExtraInformation.functionCallName

View File

@@ -184,8 +184,8 @@ void EventsListSerialization::UpdateInstructionsFromGD2x(
for (std::size_t j = 0;
j < parameters.size() && j < metadata.parameters.size();
++j) {
if (metadata.parameters[j].type == "relationalOperator" ||
metadata.parameters[j].type == "operator") {
if (metadata.parameters[j].GetType() == "relationalOperator" ||
metadata.parameters[j].GetType() == "operator") {
if (j == parameters.size() - 1) {
std::cout << "ERROR: No more parameters after a [relational]operator "
"when trying to update an instruction from GD2.x";

View File

@@ -37,22 +37,24 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
gd::ParameterMetadata info;
info.type = type;
info.SetType(type);
info.description = description;
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
info.SetOptional(parameterIsOptional);
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 +
supplementaryInformation //... so prefix it with the extension
// namespace.
)
: supplementaryInformation; // Otherwise don't change anything
: supplementaryInformation); // Otherwise don't change anything
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -64,9 +66,9 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
gd::ExpressionMetadata& ExpressionMetadata::AddCodeOnlyParameter(
const gd::String& type, const gd::String& supplementaryInformation) {
gd::ParameterMetadata info;
info.type = type;
info.SetType(type);
info.codeOnly = true;
info.supplementaryInformation = supplementaryInformation;
info.SetExtraInfo(supplementaryInformation);
parameters.push_back(info);
return *this;

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

@@ -55,15 +55,17 @@ InstructionMetadata& InstructionMetadata::AddParameter(
const gd::String& supplementaryInformation,
bool parameterIsOptional) {
ParameterMetadata info;
info.type = type;
info.SetType(type);
info.description = description;
info.codeOnly = false;
info.optional = parameterIsOptional;
info.supplementaryInformation =
info.SetOptional(parameterIsOptional);
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 +
@@ -71,7 +73,7 @@ InstructionMetadata& InstructionMetadata::AddParameter(
// extension
// namespace.
)
: supplementaryInformation; // Otherwise don't change anything
: supplementaryInformation); // Otherwise don't change anything
// TODO: Assert against supplementaryInformation === "emsc" (when running with
// Emscripten), and warn about a missing argument when calling addParameter.
@@ -83,17 +85,19 @@ InstructionMetadata& InstructionMetadata::AddParameter(
InstructionMetadata& InstructionMetadata::AddCodeOnlyParameter(
const gd::String& type, const gd::String& supplementaryInformation) {
ParameterMetadata info;
info.type = type;
info.SetType(type);
info.codeOnly = true;
info.supplementaryInformation = supplementaryInformation;
info.SetExtraInfo(supplementaryInformation);
parameters.push_back(info);
return *this;
}
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
*/
@@ -233,14 +229,12 @@ class GD_CORE_API ParameterMetadata {
// TODO: Deprecated public fields. Any direct usage should be moved to
// getter/setter.
gd::String type; ///< Parameter type
gd::String supplementaryInformation; ///< Used if needed
bool optional; ///< True if the parameter is optional
gd::String description; ///< Description shown in editor
bool codeOnly; ///< True if parameter is relative to code generation only,
///< i.e. must not be shown in editor
private:
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

@@ -85,7 +85,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
const gd::Expression& parameterValue =
pNb < parameters.size() ? parameters[pNb].GetPlainString() : "";
const gd::Expression& parameterValueOrDefault =
parameterValue.GetPlainString().empty() && parameterMetadata.optional
parameterValue.GetPlainString().empty() && parameterMetadata.IsOptional()
? Expression(parameterMetadata.GetDefaultValue())
: parameterValue;

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

@@ -120,7 +120,7 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
const gd::Expression& parameterValue,
size_t parameterIndex,
const gd::String& lastObjectName) {
const gd::String& type = parameterMetadata.type;
const gd::String& type = parameterMetadata.GetType();
if (gd::ParameterMetadata::IsBehavior(type)) {
if (lastObjectName == objectName) {

View File

@@ -149,8 +149,8 @@ class GD_CORE_API IdentifierFinderEventWorker
platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].type == "identifier"
&& instrInfos.parameters[pNb].supplementaryInformation == identifierType) {
if (instrInfos.parameters[pNb].GetType() == "identifier"
&& instrInfos.parameters[pNb].GetExtraInfo() == identifierType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName) {
results.insert(instruction.GetParameter(pNb).GetPlainString());
@@ -158,9 +158,9 @@ class GD_CORE_API IdentifierFinderEventWorker
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
"number", instrInfos.parameters[pNb].GetType()) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = instruction.GetParameter(pNb).GetRootNode();
IdentifierFinderExpressionNodeWorker searcher(
@@ -174,7 +174,7 @@ class GD_CORE_API IdentifierFinderEventWorker
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
instrInfos.parameters[pNb].GetType())) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}

View File

@@ -30,7 +30,7 @@ bool EventsLeaderboardsLister::DoVisitInstruction(gd::Instruction& instruction,
for (int i = 0; i < instruction.GetParametersCount() &&
i < instrInfo.GetParametersCount();
++i)
if (instrInfo.GetParameter(i).type == "leaderboardId") {
if (instrInfo.GetParameter(i).GetType() == "leaderboardId") {
leaderboardIds.insert(instruction.GetParameter(i).GetPlainString());
}
return false;

View File

@@ -32,7 +32,7 @@ bool EventsLeaderboardsRenamer::DoVisitInstruction(gd::Instruction& instruction,
++i) {
const gd::ParameterMetadata parameter = instrInfo.GetParameter(i);
if (parameter.type == "leaderboardId") {
if (parameter.GetType() == "leaderboardId") {
const gd::String leaderboardId =
instruction.GetParameter(i).GetPlainString();

View File

@@ -237,12 +237,12 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
actions[aId].GetParameter(pNb).GetPlainString() == oldName)
actions[aId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
@@ -252,7 +252,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
@@ -291,12 +291,12 @@ bool EventsRefactorer::RenameObjectInConditions(
conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Replace object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
conditions[cId].GetParameter(pNb).GetPlainString() == oldName)
conditions[cId].SetParameter(pNb, gd::Expression(newName));
// Replace object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
@@ -306,7 +306,7 @@ bool EventsRefactorer::RenameObjectInConditions(
}
// Replace object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
@@ -425,14 +425,14 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
MetadataProvider::GetActionMetadata(platform, actions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
actions[aId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
@@ -442,7 +442,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
}
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = actions[aId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
@@ -485,14 +485,14 @@ bool EventsRefactorer::RemoveObjectInConditions(
conditions[cId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// Find object's name in parameters
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].type) &&
if (gd::ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType()) &&
conditions[cId].GetParameter(pNb).GetPlainString() == name) {
deleteMe = true;
break;
}
// Find object's name in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type)) {
"number", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
@@ -502,7 +502,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
}
// Find object's name in text expressions
else if (ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {

View File

@@ -148,16 +148,16 @@ class GD_CORE_API VariableFinderEventWorker
platform, instruction.GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].type == parameterType) {
if (instrInfos.parameters[pNb].GetType() == parameterType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName)
results.insert(instruction.GetParameter(pNb).GetPlainString());
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
"number", instrInfos.parameters[pNb].GetType()) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
"string", instrInfos.parameters[pNb].GetType())) {
auto node = instruction.GetParameter(pNb).GetRootNode();
VariableFinderExpressionNodeWorker searcher(
@@ -171,7 +171,7 @@ class GD_CORE_API VariableFinderEventWorker
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
instrInfos.parameters[pNb].GetType())) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}

View File

@@ -36,7 +36,7 @@ size_t GetMinimumParametersNumber(
size_t initialParameterIndex) {
size_t nb = 0;
for (std::size_t i = initialParameterIndex; i < parameters.size(); ++i) {
if (!parameters[i].optional && !parameters[i].codeOnly) nb++;
if (!parameters[i].IsOptional() && !parameters[i].codeOnly) nb++;
}
return nb;

View File

@@ -150,7 +150,7 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
for (std::size_t pNb = 0; pNb < metadata.parameters.size() &&
pNb < instruction.GetParametersCount();
++pNb) {
const gd::String& type = metadata.parameters[pNb].type;
const gd::String& type = metadata.parameters[pNb].GetType();
const gd::Expression& expression = instruction.GetParameter(pNb);
auto node = expression.GetRootNode();

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

@@ -244,6 +244,15 @@ class GD_CORE_API HighestZOrderFinder : public gd::InitialInstanceFunctor {
*/
size_t GetInstancesCount() const { return instancesCount; }
void Reset() {
highestZOrder = 0;
lowestZOrder = 0;
instancesCount = 0;
firstCall = true;
layerRestricted = false;
layerName.clear();
}
private:
int highestZOrder;
int lowestZOrder;

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

@@ -28,13 +28,11 @@ class GD_EXTENSION_API DraggableBehavior : public gd::Behavior {
return new DraggableBehavior(*this);
}
#if defined(GD_IDE_ONLY)
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(
const gd::SerializerElement& behaviorContent) const override;
virtual bool UpdateProperty(gd::SerializerElement& behaviorContent,
const gd::String& name,
const gd::String& value) override;
#endif
virtual void InitializeContent(
gd::SerializerElement& behaviorContent) override;

View File

@@ -34,10 +34,9 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
std::make_shared<DraggableBehavior>(),
std::shared_ptr<gd::BehaviorsSharedData>());
#if defined(GD_IDE_ONLY)
aut.AddCondition("Dragged",
_("Being dragged"),
_("Check if the object is being dragged"),
_("Check if the object is being dragged."),
_("_PARAM0_ is being dragged"),
"",
"CppPlatform/Extensions/draggableicon24.png",
@@ -46,5 +45,16 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "Draggable")
.SetFunctionName("IsDragged");
#endif
aut.AddCondition("Dropped",
_("Was just dropped"),
_("Check if the object was just dropped after being dragged."),
_("_PARAM0_ was just dropped"),
"",
"CppPlatform/Extensions/draggableicon24.png",
"CppPlatform/Extensions/draggableicon16.png")
.AddParameter("object", _("Object"))
.AddParameter("behavior", _("Behavior"), "Draggable")
.SetFunctionName("WasJustDropped");
}

View File

@@ -4,7 +4,6 @@ GDevelop - Draggable Behavior Extension
Copyright (c) 2014-2016 Florian Rival (Florian.Rival@gmail.com)
This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Tools/Localization.h"
@@ -32,6 +31,11 @@ class DraggableBehaviorJsExtension : public gd::PlatformExtension {
.SetFunctionName("isDragged")
.SetIncludeFile(
"Extensions/DraggableBehavior/draggableruntimebehavior.js");
GetAllConditionsForBehavior(
"DraggableBehavior::Draggable")["DraggableBehavior::Dropped"]
.SetFunctionName("wasJustDropped")
.SetIncludeFile(
"Extensions/DraggableBehavior/draggableruntimebehavior.js");
GD_COMPLETE_EXTENSION_COMPILATION_INFORMATION();
};
};
@@ -49,4 +53,3 @@ extern "C" gd::PlatformExtension* GD_EXTENSION_API CreateGDJSExtension() {
return new DraggableBehaviorJsExtension;
}
#endif
#endif

View File

@@ -15,6 +15,7 @@ namespace gdjs {
*/
_draggedByDraggableManager: DraggableManager | null = null;
_checkCollisionMask: boolean;
_justDropped = false;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
@@ -41,6 +42,7 @@ namespace gdjs {
_endDrag() {
if (this._draggedByDraggableManager) {
this._draggedByDraggableManager.endDrag();
this._justDropped = true;
}
this._draggedByDraggableManager = null;
}
@@ -126,11 +128,17 @@ namespace gdjs {
.getGame()
.getInputManager()
.isMouseButtonPressed(0);
this._justDropped = false;
}
isDragged(instanceContainer: gdjs.RuntimeInstanceContainer): boolean {
isDragged(): boolean {
return !!this._draggedByDraggableManager;
}
wasJustDropped(): boolean {
return this._justDropped;
}
}
/**

View File

@@ -169,22 +169,20 @@ namespace gdjs {
// Position the input on the container on top of the canvas.
workingPoint[0] = canvasLeft;
workingPoint[1] = canvasRight;
workingPoint[1] = canvasTop;
const topLeftPageCoordinates = runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint,
workingPoint
);
const pageLeft = workingPoint[0];
const pageTop = workingPoint[1];
const pageLeft = topLeftPageCoordinates[0];
const pageTop = topLeftPageCoordinates[1];
workingPoint[0] = canvasRight;
workingPoint[1] = canvasBottom;
const bottomRightPageCoordinates = runtimeGameRenderer.convertCanvasToDomElementContainerCoords(
workingPoint,
workingPoint
);
const pageRight = workingPoint[0];
const pageBottom = workingPoint[1];
const pageRight = bottomRightPageCoordinates[0];
const pageBottom = bottomRightPageCoordinates[1];
const widthInContainer = pageRight - pageLeft;
const heightInContainer = pageBottom - pageTop;

View File

@@ -75,8 +75,9 @@ module.exports = {
)
.setCategory('Visual effect')
.setExtensionHelpPath('/behaviors/tween');
extension.addInstructionOrExpressionGroupMetadata(_("Tweening"))
.setIcon("JsPlatform/Extensions/tween_behavior32.png");
extension
.addInstructionOrExpressionGroupMetadata(_('Tweening'))
.setIcon('JsPlatform/Extensions/tween_behavior32.png');
extension
.addExpression(
@@ -105,7 +106,7 @@ module.exports = {
"Tweens a scene variable's numeric value from one number to another."
),
_(
'Tween variable _PARAM2_ from _PARAM3_ to _PARAM4_ for _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
'Tween variable _PARAM2_ from _PARAM3_ to _PARAM4_ over _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
@@ -118,19 +119,46 @@ module.exports = {
.addParameter('expression', _('Final value'), '', false)
.addParameter('expression', _('Duration'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setHidden()
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenVariableNumber');
extension
.addAction(
'TweenSceneVariableNumber2',
_('Tween a number in a scene variable'),
_(
"Tweens a scene variable's numeric value from its current value to a new one."
),
_(
'Tween variable _PARAM2_ to _PARAM3_ over _PARAM4_ms with easing _PARAM5_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('scenevar', _('The variable to tween'), '', false)
.addParameter('expression', _('Final value'), '', false)
.addParameter('expression', _('Duration'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
.addIncludeFile('Extensions/TweenBehavior/tweentools.js')
.setFunctionName('gdjs.evtTools.tween.tweenVariableNumber2');
extension
.addAction(
'TweenCameraPosition',
_('Tween the camera position'),
_('Tweens the camera position from the current one to a new one.'),
_(
'Tween camera on layer _PARAM4_ to _PARAM2_;_PARAM3_ for _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
'Tween camera on layer _PARAM4_ to _PARAM2_;_PARAM3_ over _PARAM5_ms with easing _PARAM6_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
@@ -155,7 +183,7 @@ module.exports = {
_('Tween the camera zoom'),
_('Tweens the camera zoom from the current zoom factor to a new one.'),
_(
'Tween the zoom of camera on layer _PARAM3_ to _PARAM2_ for _PARAM4_ms with easing _PARAM5_ as _PARAM1_'
'Tween the zoom of camera on layer _PARAM3_ to _PARAM2_ over _PARAM4_ms with easing _PARAM5_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
@@ -179,7 +207,7 @@ module.exports = {
_('Tween the camera rotation'),
_('Tweens the camera rotation from the current angle to a new one.'),
_(
'Tween the rotation of camera on layer _PARAM3_ to _PARAM2_ for _PARAM4_ms with easing _PARAM5_ as _PARAM1_'
'Tween the rotation of camera on layer _PARAM3_ to _PARAM2_ over _PARAM4_ms with easing _PARAM5_ as _PARAM1_'
),
_('Scene Tweens'),
'JsPlatform/Extensions/tween_behavior24.png',
@@ -393,14 +421,47 @@ module.exports = {
false
)
.setDefaultValue('no')
.setHidden()
.getCodeExtraInformation()
.setFunctionName('addVariableTween');
behavior
.addAction(
'AddObjectVariableTween2',
_('Tween a number in an object variable'),
_(
"Tweens an object variable's numeric value from its current value to a new one."
),
_(
'Tween the variable _PARAM3_ of _PARAM0_ to _PARAM4_ with easing _PARAM5_ over _PARAM6_ms as _PARAM2_'
),
_('Variables'),
'JsPlatform/Extensions/tween_behavior24.png',
'JsPlatform/Extensions/tween_behavior32.png'
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('objectvar', _('Object variable'), '', false)
.addParameter('expression', _('To value'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
.addParameter('expression', _('Duration, in milliseconds'), '', false)
.addParameter(
'yesorno',
_('Destroy this object when tween finishes'),
'',
false
)
.setDefaultValue('no')
.getCodeExtraInformation()
.setFunctionName('addVariableTween2');
behavior
.addAction(
'AddObjectPositionTween',
_('Add object position tween'),
_('Add a tween animation for the object position.'),
_('Tween object position'),
_('Tweens an object position from its current position to a new one.'),
_(
'Tween the position of _PARAM0_ to x: _PARAM3_, y: _PARAM4_ with easing _PARAM5_ over _PARAM6_ms as _PARAM2_'
),
@@ -429,8 +490,8 @@ module.exports = {
behavior
.addAction(
'AddObjectPositionXTween',
_('Add object position X tween'),
_('Add a tween animation for the object X position.'),
_('Tween object X position'),
_('Tweens an object X position from its current X position to a new one.'),
_(
'Tween the X position of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
),
@@ -458,8 +519,8 @@ module.exports = {
behavior
.addAction(
'AddObjectWidthTween',
_('Add object width tween'),
_('Add a tween animation for the object width.'),
_('Tween object width'),
_('Tweens an object width from its current width to a new one.'),
_(
'Tween the width of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
),
@@ -487,8 +548,8 @@ module.exports = {
behavior
.addAction(
'AddObjectHeightTween',
_('Add object height tween'),
_('Add a tween animation for the object height.'),
_('Tween object height'),
_('Tweens an object height from its current height to a new one.'),
_(
'Tween the height of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
),
@@ -516,8 +577,8 @@ module.exports = {
behavior
.addAction(
'AddObjectPositionYTween',
_('Add object position Y tween'),
_('Add a tween animation for the object Y position.'),
_('Tween object Y position'),
_('Tweens an object Y position from its current Y position to a new one.'),
_(
'Tween the Y position of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
),
@@ -545,8 +606,8 @@ module.exports = {
behavior
.addAction(
'AddObjectAngleTween',
_('Add object angle tween'),
_('Add a tween animation for the object angle.'),
_('Tween object angle'),
_('Tweens an object angle from its current angle to a new one.'),
_(
'Tween the angle of _PARAM0_ to _PARAM3_° with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
),
@@ -574,9 +635,9 @@ module.exports = {
behavior
.addAction(
'AddObjectScaleTween',
_('Add object scale tween'),
_('Tween object scale'),
_(
'Add a tween animation for the object scale (Note: the scale can never be less than 0).'
'Tweens an object scale from its current scale to a new one (note: the scale can never be less than 0).'
),
_(
'Tween the scale of _PARAM0_ to X-scale: _PARAM3_, Y-scale: _PARAM4_ (from center: _PARAM8_) with easing _PARAM5_ over _PARAM6_ms as _PARAM2_'
@@ -608,9 +669,9 @@ module.exports = {
behavior
.addAction(
'AddObjectScaleXTween',
_('Add object X-scale tween'),
_('Tween object X-scale'),
_(
'Add a tween animation for the object X-scale (Note: the scale can never be less than 0).'
'Tweens an object X-scale from its current value to a new one (note: the scale can never be less than 0).'
),
_(
'Tween the X-scale of _PARAM0_ to _PARAM3_ (from center: _PARAM7_) with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
@@ -641,9 +702,9 @@ module.exports = {
behavior
.addAction(
'AddObjectScaleYTween',
_('Add object Y-scale tween'),
_('Tween object Y-scale'),
_(
'Add a tween animation for the object Y-scale (Note: the scale can never be less than 0).'
'Tweens an object Y-scale from its current value to a new one (note: the scale can never be less than 0).'
),
_(
'Tween the Y-scale of _PARAM0_ to _PARAM3_ (from center: _PARAM7_) with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
@@ -674,9 +735,9 @@ module.exports = {
behavior
.addAction(
'AddTextObjectCharacterSizeTween',
_('Add text size tween'),
_('Tween text size'),
_(
'Add a tween animation for the text object character size (Note: the size can never be less than 1).'
'Tweens the text object character size from its current value to a new one (note: the size can never be less than 1).'
),
_(
'Tween the character size of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
@@ -705,9 +766,9 @@ module.exports = {
behavior
.addAction(
'AddObjectOpacityTween',
_('Add object opacity tween'),
_('Tween object opacity'),
_(
'Add a tween animation for the object opacity (Value between 0 and 255).'
'Tweens the object opacity from its current value to a new one (note: the value shall stay between 0 and 255).'
),
_(
'Tween the opacity of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
@@ -736,9 +797,9 @@ module.exports = {
behavior
.addAction(
'AddObjectColorTween',
_('Add object color tween'),
_('Tween object color'),
_(
'Add a tween animation for the object color. Format: "128;200;255" with values between 0 and 255 for red, green and blue'
'Tweens the object color from its current value to a new one. Format: "128;200;255" with values between 0 and 255 for red, green and blue'
),
_(
'Tween the color of _PARAM0_ to _PARAM3_ with easing _PARAM4_ over _PARAM5_ms as _PARAM2_'
@@ -777,9 +838,9 @@ module.exports = {
behavior
.addAction(
'AddObjectColorHSLTween',
_('Add object HSL color tween'),
_('Tween object HSL color'),
_(
'Add a tween animation for the object color using Hue/Saturation/Lightness. Hue can be any number, Saturation and Lightness are between 0 and 100. Use -1 for Saturation and Lightness to let them unchanged.'
'Tweens the object color using Hue/Saturation/Lightness. Hue can be any number, Saturation and Lightness are between 0 and 100. Use -1 for Saturation and Lightness to let them unchanged.'
),
_(
'Tween the color of _PARAM0_ using HSL to H: _PARAM3_ (_PARAM4_), S: _PARAM5_, L: _PARAM6_ with easing _PARAM7_ over _PARAM8_ms as _PARAM2_'

View File

@@ -206,6 +206,13 @@ namespace gdjs {
/**
* Add an object variable tween.
* @deprecated Use addVariableTween2 instead.
* This function is misleading since one could think that the tween starts
* right at the moment this function is called whereas the value of the variable
* will change at the next frame only. Moreover, the variable will not start from
* the start value exactly since time will have passed at the moment the next
* frame is rendered.
* See https://github.com/4ian/GDevelop/issues/4270
* @param identifier Unique id to identify the tween
* @param variable The object variable to store the tweened value
* @param fromValue Start value
@@ -240,7 +247,40 @@ namespace gdjs {
}
/**
* Add an object position tween.
* Tween an object variable.
* @param identifier Unique id to identify the tween
* @param variable The object variable to store the tweened value
* @param toValue End value
* @param easingValue Type of easing
* @param durationValue Duration in milliseconds
* @param destroyObjectWhenFinished Destroy this object when the tween ends
*/
addVariableTween2(
identifier: string,
variable: gdjs.Variable,
toValue: float,
easingValue: string,
durationValue: float,
destroyObjectWhenFinished: boolean
) {
this._addTween(
identifier,
easingValue,
{
from: { value: variable.getValue() },
to: { value: toValue },
duration: durationValue,
easing: easingValue,
render: (state) => variable.setNumber(state.value),
},
this._runtimeScene.getTimeManager().getTimeFromStart(),
durationValue,
destroyObjectWhenFinished
);
}
/**
* Tween an object position.
* @param identifier Unique id to identify the tween
* @param toX The target X position
* @param toY The target Y position
@@ -276,7 +316,7 @@ namespace gdjs {
}
/**
* Add an object X position tween.
* Tween an object X position.
* @param identifier Unique id to identify the tween
* @param toX The target X position
* @param easingValue Type of easing
@@ -307,7 +347,7 @@ namespace gdjs {
}
/**
* Add an object Y position tween.
* Tween an object Y position.
* @param identifier Unique id to identify the tween
* @param toY The target Y position
* @param easingValue Type of easing
@@ -338,7 +378,7 @@ namespace gdjs {
}
/**
* Add an object angle tween.
* Tween an object angle.
* @param identifier Unique id to identify the tween
* @param toAngle The target angle
* @param easingValue Type of easing
@@ -371,7 +411,7 @@ namespace gdjs {
}
/**
* Add an object scale tween.
* Tween an object scale.
* @param identifier Unique id to identify the tween
* @param toScaleX The target X-scale
* @param toScaleY The target Y-scale
@@ -429,7 +469,7 @@ namespace gdjs {
}
/**
* Add an object X-scale tween.
* Tween an object X-scale.
* @param identifier Unique id to identify the tween
* @param toScaleX The target X-scale
* @param easingValue Type of easing
@@ -474,7 +514,7 @@ namespace gdjs {
}
/**
* Add an object scale y tween.
* Tween an object Y-scale.
* @param identifier Unique id to identify the tween
* @param toScaleY The target Y-scale
* @param easingValue Type of easing
@@ -519,7 +559,7 @@ namespace gdjs {
}
/**
* Add an object opacity tween.
* Tween an object opacity.
* @param identifier Unique id to identify the tween
* @param toOpacity The target opacity
* @param easingValue Type of easing
@@ -555,9 +595,9 @@ namespace gdjs {
}
/**
* Add an object color tween.
* Tween an object color.
* @param identifier Unique id to identify the tween
* @param toColorStr The target color
* @param toColorStr The target RGB color (format "128;200;255" with values between 0 and 255 for red, green and blue)
* @param easingValue Type of easing
* @param durationValue Duration in milliseconds
* @param destroyObjectWhenFinished Destroy this object when the tween ends
@@ -658,7 +698,7 @@ namespace gdjs {
}
/**
* Add an object color HSL tween, with the "to" color given using HSL (H: any number, S and L: 0-100).
* Tween an object HSL color, with the "to" color given using HSL (H: any number, S and L: 0-100).
* @param identifier Unique id to identify the tween
* @param toHue The target hue, or the same as the from color's hue if blank
* @param animateHue, include hue in calculations, as can't set this to -1 as default to ignore
@@ -738,7 +778,7 @@ namespace gdjs {
}
/**
* Add a text object character size tween.
* Tween a text object character size.
* @param identifier Unique id to identify the tween
* @param toSize The target character size
* @param easingValue Type of easing
@@ -774,7 +814,7 @@ namespace gdjs {
}
/**
* Add an object width tween.
* Tween an object width.
* @param identifier Unique id to identify the tween
* @param toWidth The target width
* @param easingValue Type of easing
@@ -807,7 +847,7 @@ namespace gdjs {
}
/**
* Add an object height tween.
* Tween an object height.
* @param identifier Unique id to identify the tween
* @param toHeight The target height
* @param easingValue Type of easing

View File

@@ -137,6 +137,15 @@ namespace gdjs {
tween.stop().dispose();
};
/**
* @deprecated Use tweenVariableNumber2 instead.
* This function is misleading since one could think that the tween starts
* right at the moment this function is called whereas the value of the variable
* will change at the next frame only. Moreover, the variable will not start from
* the start value exactly since time will have passed at the moment the next
* frame is rendered.
* See https://github.com/4ian/GDevelop/issues/4270
*/
export const tweenVariableNumber = (
runtimeScene: RuntimeScene,
identifier: string,
@@ -158,6 +167,26 @@ namespace gdjs {
getShiftyScene(runtimeScene).add(tween);
};
export const tweenVariableNumber2 = (
runtimeScene: RuntimeScene,
identifier: string,
variable: Variable,
to: number,
duration: number,
easing: shifty.easingFunction
) => {
const tween = shifty.tween({
from: { value: variable.getValue() },
to: { value: to },
easing,
duration,
render: ({ value }) => variable.setNumber(value),
});
getTweensMap(runtimeScene).set(identifier, tween);
getShiftyScene(runtimeScene).add(tween);
};
export const tweenCamera = (
runtimeScene: RuntimeScene,
identifier: string,

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;";
}
@@ -683,7 +692,7 @@ gd::String EventsCodeGenerator::GenerateFreeCondition(
for (std::size_t i = 0; i < instrInfos.parameters.size();
++i) // Some conditions already have a "conditionInverted" parameter
{
if (instrInfos.parameters[i].type == "conditionInverted")
if (instrInfos.parameters[i].GetType() == "conditionInverted")
conditionAlreadyTakeCareOfInversion = true;
}
if (!conditionAlreadyTakeCareOfInversion && conditionInverted)
@@ -1100,17 +1109,17 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
gd::String argOutput;
// Code only parameter type
if (metadata.type == "currentScene") {
if (metadata.GetType() == "currentScene") {
argOutput = "runtimeScene";
}
// Code only parameter type
else if (metadata.type == "objectsContext") {
else if (metadata.GetType() == "objectsContext") {
argOutput =
"(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext "
": runtimeScene)";
}
// Code only parameter type
else if (metadata.type == "eventsFunctionContext") {
else if (metadata.GetType() == "eventsFunctionContext") {
argOutput =
"(typeof eventsFunctionContext !== 'undefined' ? eventsFunctionContext "
": undefined)";

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

@@ -409,24 +409,20 @@ namespace gdjs {
* Convert a point from the canvas coordinates to the dom element container coordinates.
*
* @param canvasCoords The point in the canvas coordinates.
* @param result The point to return.
* @returns The point in the dom element container coordinates.
*/
convertCanvasToDomElementContainerCoords(
canvasCoords: FloatPoint,
result: FloatPoint
canvasCoords: FloatPoint
): FloatPoint {
const pageCoords = result || [0, 0];
const pageCoords: FloatPoint = [0, 0];
const gameResolutionWidth = this._game.getGameResolutionWidth();
const canvasWidth = this._canvasWidth || 1;
const gameResolutionHeight = this._game.getGameResolutionHeight();
const canvasHeight = this._canvasHeight || 1;
// Handle the fact that the game is stretched to fill the canvas.
pageCoords[0] =
canvasCoords[0] /
this._game.getGameResolutionWidth() /
(this._canvasWidth || 1);
pageCoords[1] =
canvasCoords[1] /
this._game.getGameResolutionHeight() /
(this._canvasHeight || 1);
pageCoords[0] = (canvasCoords[0] * canvasWidth) / gameResolutionWidth;
pageCoords[1] = (canvasCoords[1] * canvasHeight) / gameResolutionHeight;
return pageCoords;
}

View File

@@ -1010,6 +1010,7 @@ interface HighestZOrderFinder {
void RestrictSearchToLayer([Const] DOMString layer);
long GetHighestZOrder();
long GetLowestZOrder();
void Reset();
unsigned long GetInstancesCount();
};
@@ -1199,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();
@@ -1210,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 {
@@ -1245,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 {
@@ -1261,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();
@@ -1271,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 {
@@ -1316,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);
@@ -1324,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();
@@ -1342,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);
};
@@ -2242,7 +2290,8 @@ enum EventsFunction_FunctionType {
"EventsFunction::Action",
"EventsFunction::Condition",
"EventsFunction::Expression",
"EventsFunction::StringExpression"
"EventsFunction::ExpressionAndCondition",
"EventsFunction::ActionWithOperator"
};
interface EventsFunction {
@@ -2259,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);
@@ -2946,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

@@ -4,6 +4,7 @@ declare class gdHighestZOrderFinder extends gdInitialInstanceFunctor {
restrictSearchToLayer(layer: string): void;
getHighestZOrder(): number;
getLowestZOrder(): number;
reset(): void;
getInstancesCount(): number;
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

@@ -30035,7 +30035,8 @@
"node_modules/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"devOptional": true
},
"node_modules/minipass": {
"version": "3.1.3",
@@ -46567,6 +46568,7 @@
},
"@lingui/react": {
"version": "git+ssh://git@github.com/4ian/lingui-react.git#dc6b1e013470d952cf85f96cc4affdd28e29634a",
"integrity": "sha512-eoYJ8TI+8IolPh4fue9aIwX2OVp0YrPnV86QBZLfGhxknodVeNmx+4Ic4ym7rI5/davbk9AUZHcssiH+YZWVxw==",
"from": "@lingui/react@github:4ian/lingui-react#master",
"requires": {
"@lingui/core": "2.7.3",
@@ -65573,7 +65575,8 @@
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"devOptional": true
},
"minipass": {
"version": "3.1.3",
@@ -66891,6 +66894,7 @@
},
"pixi-simple-gesture": {
"version": "git+ssh://git@github.com/4ian/pixi-simple-gesture.git#c84e0cc3c62edeca019e708d9897ef6b97a0d18a",
"integrity": "sha512-DG1BxP8SK2iPMYWMOPGz5gKDXFmA8JPUpcyyNyIH55fpQraenuLYlosYFFMTRXEy0RZViTUu11H3VrYlfG2CgA==",
"from": "pixi-simple-gesture@github:4ian/pixi-simple-gesture#v0.3.3"
},
"pixi.js": {
@@ -69300,6 +69304,7 @@
},
"react-mosaic-component": {
"version": "git+ssh://git@github.com/4ian/react-mosaic.git#d5ef155119d786c08c7c72e34997dcef2f01f98b",
"integrity": "sha512-Izfw/EkG1g39nrZbOqzY52rqFkVFA1SUSv1TLwk7soS1Wy7iHm6zrUgzJdfwKRC2GaDn9WAfSe5ZQ2vIJ/mu5A==",
"from": "react-mosaic-component@github:4ian/react-mosaic#v3.1.0",
"requires": {
"classnames": "^2.2.6",

View File

@@ -206,23 +206,23 @@ const commandsList: { [CommandName]: CommandMetadata } = {
// Scene editor toolbar commands
OPEN_OBJECTS_PANEL: {
area: 'SCENE',
displayText: t`Open the objects editor`,
displayText: t`Open Objects Panel`,
},
OPEN_OBJECT_GROUPS_PANEL: {
area: 'SCENE',
displayText: t`Open the object groups editor`,
displayText: t`Open Object Groups Panel`,
},
OPEN_PROPERTIES_PANEL: {
area: 'SCENE',
displayText: t`Open the properties panel`,
displayText: t`Open Properties Panel`,
},
TOGGLE_INSTANCES_PANEL: {
area: 'SCENE',
displayText: t`Open the list of instances`,
displayText: t`Open Instances List Panel`,
},
TOGGLE_LAYERS_PANEL: {
area: 'SCENE',
displayText: t`Open the layers editor`,
displayText: t`Open Layers Panel`,
},
SCENE_EDITOR_UNDO: {
area: 'SCENE',

View File

@@ -45,6 +45,8 @@ import { ResponsiveLineStackLayout } from '../UI/Layout';
import Text from '../UI/Text';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
const DragSourceAndDropTarget = makeDragSourceAndDropTarget('effects-list');
const styles = {
rowContainer: {
display: 'flex',
@@ -94,10 +96,6 @@ export default function EffectsList(props: Props) {
const [nameErrors, setNameErrors] = React.useState<{ [number]: React.Node }>(
{}
);
const DragSourceAndDropTarget = React.useMemo(
() => makeDragSourceAndDropTarget('effects-list'),
[]
);
const allEffectMetadata = React.useMemo(
() => enumerateEffectsMetadata(props.project),

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(
@@ -536,8 +737,9 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
const setterName = gd.BehaviorCodeGenerator.getBehaviorPropertySetterName(
propertyName
);
const propertyLabel =
property.getLabel() || i18n._(t`${propertyName} property`);
const propertyLabel = i18n._(
t`${property.getLabel() || propertyName} property`
);
if (propertyType === 'String' || propertyType === 'Choice') {
addObjectAndBehaviorParameters(
@@ -660,7 +862,7 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
behaviorMetadata.addScopedCondition(
gd.EventsBasedBehavior.getPropertyConditionName(propertyName),
propertyLabel,
i18n._(t`Check the color ${propertyLabel}`),
i18n._(t`Check the color of ${propertyLabel}`),
i18n._(t`Color ${propertyName}`),
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
getExtensionIconUrl(extension),
@@ -740,8 +942,9 @@ export const declareObjectPropertiesInstructionAndExpressions = (
const setterName = gd.ObjectCodeGenerator.getObjectPropertySetterName(
propertyName
);
const propertyLabel =
property.getLabel() || i18n._(t`${propertyName} property`);
const propertyLabel = i18n._(
t`${property.getLabel() || propertyName} property`
);
if (propertyType === 'String' || propertyType === 'Choice') {
addObjectParameter(
@@ -864,7 +1067,7 @@ export const declareObjectPropertiesInstructionAndExpressions = (
objectMetadata.addScopedCondition(
gd.EventsBasedObject.getPropertyConditionName(propertyName),
propertyLabel,
i18n._(t`Check the color ${propertyLabel}`),
i18n._(t`Check the color of ${propertyLabel}`),
i18n._(t`Color ${propertyName}`),
eventsBasedObject.getFullName() || eventsBasedObject.getName(),
getExtensionIconUrl(extension),
@@ -910,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

@@ -1,3 +1,5 @@
// @flow
import { type ComponentType } from 'react';
import UnknownEvent from './Renderers/UnknownEvent';
import StandardEvent from './Renderers/StandardEvent';
import GroupEvent from './Renderers/GroupEvent';
@@ -8,6 +10,7 @@ import RepeatEvent from './Renderers/RepeatEvent';
import WhileEvent from './Renderers/WhileEvent';
import LinkEvent from './Renderers/LinkEvent';
import JsCodeEvent from './Renderers/JsCodeEvent';
import { type EventRendererProps } from './Renderers/EventRenderer';
const EventsRenderingService = {
components: {
@@ -22,12 +25,17 @@ const EventsRenderingService = {
'BuiltinCommonInstructions::Link': LinkEvent,
'BuiltinCommonInstructions::JsCode': JsCodeEvent,
},
getEventComponent: function(event) {
getEventComponent: function(
event: gdBaseEvent
): ComponentType<EventRendererProps> {
if (this.components.hasOwnProperty(event.getType()))
return this.components[event.getType()];
else return this.components.unknownEvent;
},
registerEvent: function(eventType, renderFunction) {
registerEvent: function(
eventType: string,
renderFunction: ComponentType<EventRendererProps>
) {
if (!this.components.hasOwnProperty(eventType)) {
console.warn(
'Tried to register renderer for events "' +

View File

@@ -120,6 +120,7 @@ type EventsContainerProps = {|
eventsSheetHeight: number,
connectDragSource: ConnectDragSource,
windowWidth: WidthType,
|};
/**
@@ -158,7 +159,7 @@ class EventContainer extends Component<EventsContainerProps, {||}> {
onClick={this.props.onEventClick}
onContextMenu={this._onEventContextMenu}
>
{EventComponent && (
{!!EventComponent && (
<div style={styles.eventComponentContainer}>
{this.props.connectDragSource(<div className={handle} />)}
<div style={styles.container}>
@@ -192,6 +193,7 @@ class EventContainer extends Component<EventsContainerProps, {||}> {
renderObjectThumbnail={this.props.renderObjectThumbnail}
screenType={this.props.screenType}
eventsSheetHeight={this.props.eventsSheetHeight}
windowWidth={this.props.windowWidth}
/>
</div>
</div>
@@ -781,6 +783,7 @@ export default class ThemableEventsTree extends Component<
screenType={this.props.screenType}
eventsSheetHeight={this.props.eventsSheetHeight}
connectDragSource={connectDragSource}
windowWidth={this.props.windowWidth}
/>
{this.state.draggedNode && (
<DropContainer

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

@@ -14,6 +14,7 @@ import { type ParameterRenderingServiceType } from '../ParameterFieldCommons';
import { type EnumeratedInstructionOrExpressionMetadata } from '../../../InstructionOrExpression/EnumeratedInstructionOrExpressionMetadata';
import { Column, Line, Spacer } from '../../../UI/Grid';
import ObjectsRenderingService from '../../../ObjectsRendering/ObjectsRenderingService';
import GDevelopThemeContext from '../../../UI/Theme/ThemeContext';
const defaultTextStyle = {
// Break words if they are too long to fit on a single line.
@@ -273,6 +274,7 @@ export default function ExpressionAutocompletionsDisplayer({
onScroll,
parameterRenderingService,
}: Props) {
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const scrollView = React.useRef((null: ?ScrollViewInterface));
const selectedAutocompletionElement = React.useRef(
(null: ?React$Component<any, any>)
@@ -303,7 +305,14 @@ export default function ExpressionAutocompletionsDisplayer({
false
}
>
<Paper variant="outlined" square style={styles.container}>
<Paper
variant="outlined"
square
style={{
...styles.container,
backgroundColor: gdevelopTheme.palette.alternateCanvasColor,
}}
>
<ScrollView ref={scrollView} onScroll={onScroll}>
{expressionAutocompletions.map(
(expressionAutocompletion, index) => {
@@ -365,7 +374,14 @@ export default function ExpressionAutocompletionsDisplayer({
expressionAutocompletions[selectedCompletionIndex].kind ===
'Expression' &&
!expressionAutocompletions[selectedCompletionIndex].isExact && (
<Paper variant="outlined" square style={styles.container}>
<Paper
variant="outlined"
square
style={{
...styles.container,
backgroundColor: gdevelopTheme.palette.alternateCanvasColor,
}}
>
<ScrollView autoHideScrollbar>
<Column>
<Line noMargin expand alignItems="center">

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

@@ -67,6 +67,7 @@ import LeaderboardSortOptionsDialog from './LeaderboardSortOptionsDialog';
import { type LeaderboardSortOption } from '../../Utils/GDevelopServices/Play';
import { formatScore } from '../../Leaderboard/LeaderboardScoreFormatter';
import Toggle from '../../UI/Toggle';
import GDevelopThemeContext from '../../UI/Theme/ThemeContext';
type Props = {|
onLoading: boolean => void,
@@ -183,6 +184,7 @@ export const LeaderboardAdmin = ({
leaderboardIdToSelectAtOpening,
}: Props) => {
const isOnline = useOnlineStatus();
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const windowWidth = useResponsiveWindowWidth();
const [isEditingAppearance, setIsEditingAppearance] = React.useState<boolean>(
false
@@ -839,7 +841,13 @@ export const LeaderboardAdmin = ({
<>
<ResponsiveLineStackLayout noMargin expand noColumnMargin>
<div style={styles.leftColumn}>
<Paper elevation={5} style={styles.leaderboardConfigurationPaper}>
<Paper
elevation={5}
style={{
...styles.leaderboardConfigurationPaper,
backgroundColor: gdevelopTheme.palette.alternateCanvasColor,
}}
>
<Column>
<Line>
{currentLeaderboard && leaderboards ? (

View File

@@ -1,9 +1,8 @@
// @flow
import * as React from 'react';
import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
import { t } from '@lingui/macro';
import { Trans } from '@lingui/macro';
import React from 'react';
import { t, Trans } from '@lingui/macro';
import { TreeTableRow, TreeTableCell } from '../UI/TreeTable';
import InlineCheckbox from '../UI/InlineCheckbox';
import Visibility from '@material-ui/icons/Visibility';
@@ -11,21 +10,32 @@ import VisibilityOff from '@material-ui/icons/VisibilityOff';
import FlareIcon from '@material-ui/icons/Flare';
import IconButton from '../UI/IconButton';
import Delete from '@material-ui/icons/Delete';
import TextField from '../UI/TextField';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import DragHandle from '../UI/DragHandle';
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
import MoreVert from '@material-ui/icons/MoreVert';
import EmojiObjectsIcon from '@material-ui/icons/EmojiObjects';
import EditIcon from '@material-ui/icons/Edit';
import Badge from '../UI/Badge';
import { makeDragSourceAndDropTarget } from '../UI/DragAndDrop/DragSourceAndDropTarget';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
const DragSourceAndDropTarget = makeDragSourceAndDropTarget('layers-list');
export const styles = {
dropIndicator: {
outline: '1px solid white',
},
};
type Props = {|
layerName: string,
nameError: boolean,
onBlur: () => void,
layer: gdLayer,
nameError: React.Node,
onBlur: string => void,
onRemove: () => void,
onBeginDrag: () => void,
onDrop: () => void,
isVisible: boolean,
isLightingLayer: boolean,
onChangeVisibility: boolean => void,
effectsCount: number,
onEditEffects: () => void,
@@ -34,7 +44,7 @@ type Props = {|
|};
const LayerRow = ({
layerName,
layer,
nameError,
onBlur,
onRemove,
@@ -42,111 +52,153 @@ const LayerRow = ({
effectsCount,
onEditEffects,
onChangeVisibility,
onBeginDrag,
onDrop,
width,
isLightingLayer,
onEdit,
}: Props) => (
<I18n>
{({ i18n }) => (
<TreeTableRow>
<TreeTableCell>
<DragHandle />
</TreeTableCell>
<TreeTableCell expand>
<TextField
margin="none"
defaultValue={layerName || i18n._(t`Base layer`)}
id={layerName}
errorText={
nameError ? <Trans>This name is already taken</Trans> : undefined
}
disabled={!layerName}
onBlur={onBlur}
fullWidth
/>
</TreeTableCell>
<TreeTableCell>
{width < 350 ? (
<ElementWithMenu
element={
<IconButton size="small">
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [
{
label: isLightingLayer
? i18n._(t`Edit lighting properties`)
: i18n._(t`Edit properties`),
click: onEdit,
},
{
label: i18n._(t`Edit effects (${effectsCount})`),
click: onEditEffects,
},
{
type: 'checkbox',
label: i18n._(t`Visible`),
checked: isVisible,
click: () => onChangeVisibility(!isVisible),
},
{ type: 'separator' },
{
label: i18n._(t`Delete`),
enabled: !!layerName,
click: onRemove,
},
]}
/>
) : (
<React.Fragment>
<InlineCheckbox
checked={isVisible}
checkedIcon={<Visibility />}
uncheckedIcon={<VisibilityOff />}
onCheck={(e, value) => onChangeVisibility(value)}
tooltipOrHelperText={
isVisible ? (
<Trans>Hide layer</Trans>
) : (
<Trans>Show layer</Trans>
)
}
/>
<IconButton
size="small"
onClick={onEditEffects}
tooltip={t`Edit effects (${effectsCount})`}
>
<Badge badgeContent={effectsCount} color="primary">
<FlareIcon />
</Badge>
</IconButton>
<IconButton
size="small"
onClick={onEdit}
tooltip={
isLightingLayer
? t`Edit lighting properties`
: t`Edit properties`
}
>
{isLightingLayer ? <EmojiObjectsIcon /> : <EditIcon />}
</IconButton>
<IconButton
size="small"
onClick={onRemove}
disabled={!layerName}
tooltip={t`Delete the layer`}
>
<Delete />
</IconButton>
</React.Fragment>
)}
</TreeTableCell>
</TreeTableRow>
)}
</I18n>
);
}: Props) => {
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const layerName = layer.getName();
const isLightingLayer = layer.isLightingLayer();
const isBaseLayer = !layerName;
return (
<I18n>
{({ i18n }) => (
<DragSourceAndDropTarget
key={layer.ptr}
beginDrag={() => {
onBeginDrag();
return {};
}}
canDrag={() => true}
canDrop={() => true}
drop={onDrop}
>
{({ connectDragSource, connectDropTarget, isOver, canDrop }) =>
connectDropTarget(
<div>
{isOver && (
<div
style={{
...styles.dropIndicator,
outlineColor: gdevelopTheme.dropIndicator.canDrop,
}}
/>
)}
<TreeTableRow>
<TreeTableCell>
{connectDragSource(
<span>
<DragHandle />
</span>
)}
</TreeTableCell>
<TreeTableCell expand>
<SemiControlledTextField
margin="none"
value={isBaseLayer ? i18n._(t`Base layer`) : layerName}
id={layerName}
errorText={nameError}
disabled={isBaseLayer}
onChange={onBlur}
commitOnBlur
fullWidth
/>
</TreeTableCell>
<TreeTableCell>
{width < 350 ? (
<ElementWithMenu
element={
<IconButton size="small">
<MoreVert />
</IconButton>
}
buildMenuTemplate={(i18n: I18nType) => [
{
label: isLightingLayer
? i18n._(t`Edit lighting properties`)
: i18n._(t`Edit properties`),
click: onEdit,
},
{
label: i18n._(t`Edit effects (${effectsCount})`),
click: onEditEffects,
},
{
type: 'checkbox',
label: i18n._(t`Visible`),
checked: isVisible,
click: () => onChangeVisibility(!isVisible),
},
{ type: 'separator' },
{
label: i18n._(t`Delete`),
enabled: !isBaseLayer,
click: onRemove,
},
]}
/>
) : (
<React.Fragment>
<InlineCheckbox
checked={isVisible}
checkedIcon={<Visibility />}
uncheckedIcon={<VisibilityOff />}
onCheck={(e, value) => onChangeVisibility(value)}
tooltipOrHelperText={
isVisible ? (
<Trans>Hide layer</Trans>
) : (
<Trans>Show layer</Trans>
)
}
/>
<IconButton
size="small"
onClick={onEditEffects}
tooltip={t`Edit effects (${effectsCount})`}
>
<Badge badgeContent={effectsCount} color="primary">
<FlareIcon />
</Badge>
</IconButton>
<IconButton
size="small"
onClick={onEdit}
tooltip={
isLightingLayer
? t`Edit lighting properties`
: t`Edit properties`
}
>
{isLightingLayer ? (
<EmojiObjectsIcon />
) : (
<EditIcon />
)}
</IconButton>
<IconButton
size="small"
onClick={onRemove}
disabled={isBaseLayer}
tooltip={t`Delete the layer`}
>
<Delete />
</IconButton>
</React.Fragment>
)}
</TreeTableCell>
</TreeTableRow>
</div>
)
}
</DragSourceAndDropTarget>
)}
</I18n>
);
};
export default LayerRow;

View File

@@ -1,10 +1,9 @@
// @flow
import { t, Trans } from '@lingui/macro';
import React, { Component } from 'react';
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
import * as React from 'react';
import newNameGenerator from '../Utils/NewNameGenerator';
import { mapReverseFor } from '../Utils/MapFor';
import LayerRow from './LayerRow';
import LayerRow, { styles } from './LayerRow';
import BackgroundColorRow from './BackgroundColorRow';
import { Column, Line } from '../UI/Grid';
import Add from '@material-ui/icons/Add';
@@ -20,96 +19,149 @@ import Background from '../UI/Background';
import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewButton';
import RaisedButtonWithSplitMenu from '../UI/RaisedButtonWithSplitMenu';
import useForceUpdate from '../Utils/UseForceUpdate';
import { makeDropTarget } from '../UI/DragAndDrop/DropTarget';
import GDevelopThemeContext from '../UI/Theme/ThemeContext';
const SortableLayerRow = SortableElement(LayerRow);
const DropTarget = makeDropTarget('layers-list');
type LayersListBodyState = {|
nameErrors: { [string]: boolean },
type LayersListBodyProps = {|
layersContainer: gdLayout,
unsavedChanges?: ?UnsavedChanges,
onRemoveLayer: (layerName: string, cb: (done: boolean) => void) => void,
onRenameLayer: (
oldName: string,
newName: string,
cb: (done: boolean) => void
) => void,
onEditEffects: (layer: ?gdLayer) => void,
onEdit: (layer: ?gdLayer) => void,
width: number,
|};
class LayersListBody extends Component<*, LayersListBodyState> {
state = {
nameErrors: {},
const LayersListBody = (props: LayersListBodyProps) => {
const forceUpdate = useForceUpdate();
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const [nameErrors, setNameErrors] = React.useState<{
[key: string]: React.Node,
}>({});
const draggedLayerIndexRef = React.useRef<number | null>(null);
const {
layersContainer,
onEditEffects,
onEdit,
width,
onRenameLayer,
onRemoveLayer,
unsavedChanges,
} = props;
const onLayerModified = () => {
if (unsavedChanges) unsavedChanges.triggerUnsavedChanges();
forceUpdate();
};
_onLayerModified = () => {
if (this.props.unsavedChanges)
this.props.unsavedChanges.triggerUnsavedChanges();
this.forceUpdate();
};
const onDropLayer = (targetIndex: number) => {
const { current: draggedLayerIndex } = draggedLayerIndexRef;
if (draggedLayerIndex === null) return;
render() {
const { layersContainer, onEditEffects, onEdit, width } = this.props;
const layersCount = layersContainer.getLayersCount();
const containerLayersList = mapReverseFor(0, layersCount, i => {
const layer = layersContainer.getLayerAt(i);
const layerName = layer.getName();
const isLightingLayer = layer.isLightingLayer();
return (
<SortableLayerRow
index={layersCount - 1 - i}
key={'layer-' + layerName}
layer={layer}
layerName={layerName}
isLightingLayer={isLightingLayer}
nameError={this.state.nameErrors[layerName]}
effectsCount={layer.getEffects().getEffectsCount()}
onEditEffects={() => onEditEffects(layer)}
onEdit={() => onEdit(layer)}
onBlur={event => {
const newName = event.target.value;
if (layerName === newName) return;
let success = true;
if (layersContainer.hasLayerNamed(newName)) {
success = false;
} else {
this.props.onRenameLayer(layerName, newName, doRename => {
if (doRename)
layersContainer.getLayer(layerName).setName(newName);
});
}
this.setState({
nameErrors: {
...this.state.nameErrors,
[layerName]: !success,
},
});
}}
onRemove={() => {
this.props.onRemoveLayer(layerName, doRemove => {
if (!doRemove) return;
layersContainer.removeLayer(layerName);
this._onLayerModified();
});
}}
isVisible={layer.getVisibility()}
onChangeVisibility={visible => {
layer.setVisibility(visible);
this._onLayerModified();
}}
width={width}
/>
if (targetIndex !== draggedLayerIndex) {
layersContainer.moveLayer(
draggedLayerIndex,
targetIndex < draggedLayerIndex ? targetIndex + 1 : targetIndex
);
});
onLayerModified();
}
draggedLayerIndexRef.current = null;
};
const layersCount = layersContainer.getLayersCount();
const containerLayersList = mapReverseFor(0, layersCount, i => {
const layer = layersContainer.getLayerAt(i);
const layerName = layer.getName();
return (
<Column noMargin expand>
{containerLayersList}
<BackgroundColorRow
layout={layersContainer}
onBackgroundColorChanged={() => this._onLayerModified()}
/>
</Column>
);
}
}
<LayerRow
key={'layer-' + layer.ptr}
layer={layer}
nameError={nameErrors[layerName]}
effectsCount={layer.getEffects().getEffectsCount()}
onEditEffects={() => onEditEffects(layer)}
onEdit={() => onEdit(layer)}
onBeginDrag={() => {
draggedLayerIndexRef.current = i;
}}
onDrop={() => onDropLayer(i)}
onBlur={newName => {
setNameErrors(currentValue => ({
...currentValue,
[layerName]: null,
}));
const SortableLayersListBody = SortableContainer(LayersListBody);
if (layerName === newName) return;
const isNameAlreadyTaken = layersContainer.hasLayerNamed(newName);
if (isNameAlreadyTaken) {
setNameErrors(currentValue => ({
...currentValue,
[layerName]: <Trans>The name {newName} is already taken</Trans>,
}));
} else {
onRenameLayer(layerName, newName, doRename => {
if (doRename)
layersContainer.getLayer(layerName).setName(newName);
});
}
}}
onRemove={() => {
onRemoveLayer(layerName, doRemove => {
if (!doRemove) return;
layersContainer.removeLayer(layerName);
onLayerModified();
});
}}
isVisible={layer.getVisibility()}
onChangeVisibility={visible => {
layer.setVisibility(visible);
onLayerModified();
}}
width={width}
/>
);
});
return (
<Column noMargin expand>
{containerLayersList}
<DropTarget
canDrop={() => true}
drop={() => {
onDropLayer(-1);
}}
>
{({ connectDropTarget, isOver, canDrop }) =>
connectDropTarget(
<div>
{isOver && (
<div
style={{
...styles.dropIndicator,
outlineColor: gdevelopTheme.dropIndicator.canDrop,
}}
/>
)}
<BackgroundColorRow
layout={layersContainer}
onBackgroundColorChanged={onLayerModified}
/>
</div>
)
}
</DropTarget>
</Column>
);
};
type Props = {|
project: gdProject,
@@ -195,23 +247,13 @@ const LayersList = React.forwardRef<Props, LayersListInterface>(
{({ width }) => (
// TODO: The list is costly to render when there are many layers, consider
// using SortableVirtualizedItemList.
<SortableLayersListBody
<LayersListBody
key={listKey}
layersContainer={props.layersContainer}
onEditEffects={props.onEditLayerEffects}
onEdit={props.onEditLayer}
onRemoveLayer={props.onRemoveLayer}
onRenameLayer={props.onRenameLayer}
onSortEnd={({ oldIndex, newIndex }) => {
const layersCount = props.layersContainer.getLayersCount();
props.layersContainer.moveLayer(
layersCount - 1 - oldIndex,
layersCount - 1 - newIndex
);
onLayerModified();
}}
helperClass="sortable-helper"
useDragHandle
unsavedChanges={props.unsavedChanges}
width={width}
/>

View File

@@ -16,6 +16,10 @@ import {
type ClosableTabProps,
} from '../../UI/ClosableTabs';
const DragSourceAndDropTarget = makeDragSourceAndDropTarget<EditorTab>(
'draggable-closable-tab'
);
type DraggableEditorTabsProps = {|
hideLabels?: boolean,
editorTabs: EditorTabsState,
@@ -96,10 +100,6 @@ export function DraggableClosableTab({
onBeginDrag,
onDrop,
}: DraggableClosableTabProps) {
const DragSourceAndDropTarget = makeDragSourceAndDropTarget<EditorTab>(
'draggable-closable-tab'
);
return (
<ScreenTypeMeasurer>
{screenType => (

View File

@@ -289,7 +289,7 @@ export const initialPreferences = {
eventsSheetShowObjectThumbnails: true,
autosaveOnPreview: true,
useNewInstructionEditorDialog: true,
useUndefinedVariablesInAutocompletion: false,
useUndefinedVariablesInAutocompletion: true,
useGDJSDevelopmentWatcher: true,
eventsSheetUseAssignmentOperators: false,
eventsSheetZoomLevel: 14,

View File

@@ -1,9 +1,8 @@
// @flow
// See ElectronEventsBridge, AboutDialog and electron-app/main.js for handling the updates.
import { t, Trans } from '@lingui/macro';
import { Trans } from '@lingui/macro';
import React from 'react';
import useAlertDialog from '../UI/Alert/useAlertDialog';
export type ElectronUpdateStatus = {
message: string,
@@ -97,53 +96,6 @@ export const useServiceWorkerUpdateStatus = () => {
return serviceWorkerUpdateStatus;
};
export const useServiceWorkerCheckAndAskToUpdate = () => {
const serviceWorkerUpdateStatus = useServiceWorkerUpdateStatus();
const { showConfirmation, showAlert } = useAlertDialog();
React.useEffect(
() => {
if (serviceWorkerUpdateStatus === 'update-ready') {
const timeoutId = setTimeout(() => {
(async () => {
const answer = await showConfirmation({
title: t`New update available!`,
message: t`A new version of GDevelop is available. Would you like to update now? The app will be reloaded.`,
});
if (answer) {
try {
await clearServiceWorkerAndForceReload();
} catch (error) {
console.error('Unable to update service worker', error);
await showAlert({
title: t`Unable to update`,
message: t`Unable to update the app. Please close all your tabs and try again.`,
});
}
}
})();
}, 3000); // Let a bit of time for the app to load before showing the update dialog.
return () => clearTimeout(timeoutId);
}
},
[serviceWorkerUpdateStatus, showConfirmation, showAlert]
);
};
const clearServiceWorkerAndForceReload = async () => {
const { serviceWorker } = navigator;
if (!serviceWorker) {
throw new Error('Service worker not available');
}
const registration = await serviceWorker.getRegistration();
if (!registration) {
throw new Error('Service worker registration not available');
}
await registration.unregister();
const cacheKeys = await caches.keys();
await Promise.all(cacheKeys.map(key => caches.delete(key)));
window.location.reload();
};
export const getServiceWorkerStatusLabel = (
status: ServiceWorkerUpdateStatus
) => {

View File

@@ -71,7 +71,6 @@ import {
getElectronUpdateNotificationTitle,
getElectronUpdateNotificationBody,
type ElectronUpdateStatus,
useServiceWorkerCheckAndAskToUpdate,
} from './UpdaterTools';
import { showWarningBox } from '../UI/Messages/MessageBox';
import EmptyMessage from '../UI/EmptyMessage';
@@ -665,8 +664,6 @@ const MainFrame = (props: Props) => {
useDiscordRichPresence(currentProject);
useServiceWorkerCheckAndAskToUpdate();
const closeProject = React.useCallback(
(): Promise<void> => {
setHasProjectOpened(false);

View File

@@ -1,12 +1,15 @@
// @flow
import * as React from 'react';
import { t } from '@lingui/macro';
import * as React from 'react';
import { List, ListItem } from '../UI/List';
import ObjectSelector from '../ObjectsList/ObjectSelector';
import EmptyMessage from '../UI/EmptyMessage';
import { Column } from '../UI/Grid';
import { Paper } from '@material-ui/core';
import ListIcon from '../UI/ListIcon';
import ObjectsRenderingService from '../ObjectsRendering/ObjectsRenderingService';
import getObjectByName from '../Utils/GetObjectByName';
const gd: libGDevelop = global.gd;
const styles = {
@@ -78,12 +81,28 @@ const ObjectGroupEditor = ({
.getAllObjectsNames()
.toJSArray()
.map(objectName => {
let object = getObjectByName(
globalObjectsContainer,
objectsContainer,
objectName
);
const icon =
project && object ? (
<ListIcon
iconSize={24}
src={ObjectsRenderingService.getThumbnail(
project,
object.getConfiguration()
)}
/>
) : null;
return (
<ListItem
key={objectName}
primaryText={objectName}
displayRemoveButton
onRemove={() => removeObject(objectName)}
leftIcon={icon}
/>
);
})}

View File

@@ -20,6 +20,7 @@ import { showErrorBox } from '../UI/Messages/MessageBox';
import optionalRequire from '../Utils/OptionalRequire';
import Text from '../UI/Text';
import { ColumnStackLayout } from '../UI/Layout';
import AlertMessage from '../UI/AlertMessage';
const path = optionalRequire('path');
const gd: libGDevelop = global.gd;
@@ -39,6 +40,7 @@ type State = {|
androidIconResourceNames: Array<string>,
androidWindowSplashScreenAnimatedIconResourceName: string,
iosIconResourceNames: Array<string>,
displayLiluoThumbnailWarning: boolean,
|};
const desktopSizes = [512];
@@ -96,6 +98,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
iosIconResourceNames: iosSizes.map(size =>
platformSpecificAssets.get('ios', `icon-${size}`)
),
displayLiluoThumbnailWarning: false,
};
}
@@ -299,6 +302,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
androidIconResourceNames,
androidWindowSplashScreenAnimatedIconResourceName,
iosIconResourceNames,
displayLiluoThumbnailWarning,
} = this.state;
return (
@@ -324,9 +328,23 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
onChange={resourceName => {
this.setState({
thumbnailResourceName: resourceName,
displayLiluoThumbnailWarning:
resourceName !== this.state.thumbnailResourceName,
});
}}
/>
{displayLiluoThumbnailWarning ? (
<Line>
<AlertMessage kind="warning">
<Trans>
You're about to change the thumbnail displayed on Liluo.io for
your game. Once you have applied changes here, you will then
need to publish a new version of your game on Liluo.io so that
this new thumbnail is used.
</Trans>
</AlertMessage>
</Line>
) : null}
<Line justifyContent="center">
{isResizeSupported() ? (
<RaisedButton

View File

@@ -96,6 +96,7 @@ const AchievementList = ({
>
<Text
noMargin
size="sub-title"
style={
achievementWithBadgeData.unlockedAt
? styles.unlockedAchievement

View File

@@ -649,6 +649,9 @@ export default class ProjectManager extends React.Component<Props, State> {
eventsFunctionsExtensionsError,
onReloadEventsFunctionsExtensions,
onInstallExtension,
resourceSources,
onChooseResource,
resourceExternalEditors,
} = this.props;
const {
renamedItemKind,
@@ -1137,6 +1140,9 @@ export default class ProjectManager extends React.Component<Props, State> {
);
this._onOpenLayoutProperties(null);
}}
resourceSources={resourceSources}
resourceExternalEditors={resourceExternalEditors}
onChooseResource={onChooseResource}
/>
)}
{!!this.state.editedVariablesLayout && (

View File

@@ -0,0 +1,53 @@
// @flow
import { Trans } from '@lingui/macro';
import * as React from 'react';
import PropertiesEditor from '../PropertiesEditor';
import propertiesMapToSchema from '../PropertiesEditor/PropertiesMapToSchema';
import EmptyMessage from '../UI/EmptyMessage';
import { Column } from '../UI/Grid';
import {
type ResourceSource,
type ChooseResourceFunction,
} from '../ResourcesList/ResourceSource';
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
type Props = {|
behaviorSharedData: gdBehaviorsSharedData,
project: gdProject,
resourceSources: Array<ResourceSource>,
onChooseResource: ChooseResourceFunction,
resourceExternalEditors: Array<ResourceExternalEditor>,
|};
export default class BehaviorSharedPropertiesEditor extends React.Component<Props> {
render() {
const { behaviorSharedData } = this.props;
const propertiesSchema = propertiesMapToSchema(
behaviorSharedData.getProperties(),
behavior => behavior.getProperties(),
(behavior, name, value) => {
behavior.updateProperty(name, value);
}
);
return (
<Column expand>
{propertiesSchema.length ? (
<PropertiesEditor
schema={propertiesSchema}
instances={[behaviorSharedData]}
/>
) : (
<EmptyMessage>
<Trans>
There is nothing to configure for this behavior. You can still use
events to interact with the object and this behavior.
</Trans>
</EmptyMessage>
)}
</Column>
);
}
}

View File

@@ -7,7 +7,7 @@ import RaisedButton from '../UI/RaisedButton';
import Dialog, { DialogPrimaryButton } from '../UI/Dialog';
import ColorField from '../UI/ColorField';
import EmptyMessage from '../UI/EmptyMessage';
import PropertiesEditor from '../PropertiesEditor';
import BehaviorSharedPropertiesEditor from './BehaviorSharedPropertiesEditor';
import propertiesMapToSchema from '../PropertiesEditor/PropertiesMapToSchema';
import some from 'lodash/some';
import Checkbox from '../UI/Checkbox';
@@ -18,6 +18,19 @@ import {
rgbStringAndAlphaToRGBColor,
type RGBColor,
} from '../Utils/ColorTransformer';
import HelpIcon from '../UI/HelpIcon';
import { Column, Line } from '../UI/Grid';
import DismissableTutorialMessage from '../Hints/DismissableTutorialMessage';
import { Accordion, AccordionHeader, AccordionBody } from '../UI/Accordion';
import { IconContainer } from '../UI/IconContainer';
import { getBehaviorTutorialIds } from '../Utils/GDevelopServices/Tutorial';
import ScrollView from '../UI/ScrollView';
import {
type ResourceSource,
type ChooseResourceFunction,
} from '../ResourcesList/ResourceSource';
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
const gd: libGDevelop = global.gd;
type Props = {|
@@ -28,6 +41,9 @@ type Props = {|
onClose: () => void,
onOpenMoreSettings?: ?() => void,
onEditVariables: () => void,
resourceSources: Array<ResourceSource>,
onChooseResource: ChooseResourceFunction,
resourceExternalEditors: Array<ResourceExternalEditor>,
|};
type State = {|
@@ -102,16 +118,8 @@ export default class ScenePropertiesDialog extends Component<Props, State> {
.toJSArray();
const propertiesEditors = allBehaviorSharedDataNames
.map(name => {
const behaviorSharedData = layout.getBehaviorSharedData(name);
// TODO Check if this extra check is needed.
const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata(
gd.JsPlatform.get(),
behaviorSharedData.getTypeName()
);
if (gd.MetadataProvider.isBadBehaviorMetadata(behaviorMetadata))
return null;
.map(behaviorName => {
const behaviorSharedData = layout.getBehaviorSharedData(behaviorName);
if (isNullPtr(gd, behaviorSharedData)) return null;
@@ -123,14 +131,85 @@ export default class ScenePropertiesDialog extends Component<Props, State> {
behaviorSharedData.updateProperty(name, value);
}
);
const behaviorTypeName = behaviorSharedData.getTypeName();
const behaviorMetadata = gd.MetadataProvider.getBehaviorMetadata(
gd.JsPlatform.get(),
behaviorTypeName
);
const tutorialIds = getBehaviorTutorialIds(behaviorTypeName);
// TODO Make this a functional component to use PreferencesContext
const enabledTutorialIds = [];
const iconUrl = behaviorMetadata.getIconFilename();
return (
!!propertiesSchema.length && (
<PropertiesEditor
key={name}
schema={propertiesSchema}
instances={[behaviorSharedData]}
/>
<Accordion
key={behaviorName}
defaultExpanded
id={`behavior-parameters-${behaviorName}`}
>
<AccordionHeader
actions={[
<HelpIcon
key="help"
size="small"
helpPagePath={behaviorMetadata.getHelpPath()}
/>,
]}
>
{iconUrl ? (
<IconContainer
src={iconUrl}
alt={behaviorMetadata.getFullName()}
size={20}
/>
) : null}
<Column expand>
<TextField
value={behaviorName}
margin="none"
fullWidth
disabled
onChange={(e, text) => {}}
id={`behavior-${behaviorName}-name-text-field`}
/>
</Column>
</AccordionHeader>
<AccordionBody>
<Column
expand
noMargin
// Avoid Physics2 behavior overflow on small screens
noOverflowParent
>
{enabledTutorialIds.length ? (
<Line>
<ColumnStackLayout expand>
{tutorialIds.map(tutorialId => (
<DismissableTutorialMessage
key={tutorialId}
tutorialId={tutorialId}
/>
))}
</ColumnStackLayout>
</Line>
) : null}
<Line>
<BehaviorSharedPropertiesEditor
key={behaviorName}
behaviorSharedData={behaviorSharedData}
project={this.props.project}
resourceSources={this.props.resourceSources}
onChooseResource={this.props.onChooseResource}
resourceExternalEditors={
this.props.resourceExternalEditors
}
/>
</Line>
</Column>
</AccordionBody>
</Accordion>
)
);
})
@@ -156,55 +235,57 @@ export default class ScenePropertiesDialog extends Component<Props, State> {
open={this.props.open}
maxWidth="sm"
>
<ColumnStackLayout expand noMargin>
<TextField
floatingLabelText={<Trans>Window title</Trans>}
fullWidth
type="text"
value={this.state.windowTitle}
onChange={(e, value) => this.setState({ windowTitle: value })}
/>
<Checkbox
checked={this.state.shouldStopSoundsOnStartup}
label={<Trans>Stop music and sounds on startup</Trans>}
onCheck={(e, check) =>
this.setState({
shouldStopSoundsOnStartup: check,
})
}
/>
<ColorField
floatingLabelText={<Trans>Scene background color</Trans>}
fullWidth
disableAlpha
color={rgbColorToRGBString(this.state.backgroundColor)}
onChange={color =>
this.setState({
backgroundColor: rgbStringAndAlphaToRGBColor(color),
})
}
/>
{!some(propertiesEditors) && (
<EmptyMessage>
<Trans>
Any additional properties will appear here if you add behaviors
to objects, like Physics behavior.
</Trans>
</EmptyMessage>
)}
{propertiesEditors}
{this.props.onOpenMoreSettings && (
<RaisedButton
label={<Trans>Open advanced settings</Trans>}
<ScrollView>
<ColumnStackLayout expand noMargin>
<TextField
floatingLabelText={<Trans>Window title</Trans>}
fullWidth
onClick={() => {
if (this.props.onOpenMoreSettings)
this.props.onOpenMoreSettings();
this.props.onClose();
}}
type="text"
value={this.state.windowTitle}
onChange={(e, value) => this.setState({ windowTitle: value })}
/>
)}
</ColumnStackLayout>
<Checkbox
checked={this.state.shouldStopSoundsOnStartup}
label={<Trans>Stop music and sounds on startup</Trans>}
onCheck={(e, check) =>
this.setState({
shouldStopSoundsOnStartup: check,
})
}
/>
<ColorField
floatingLabelText={<Trans>Scene background color</Trans>}
fullWidth
disableAlpha
color={rgbColorToRGBString(this.state.backgroundColor)}
onChange={color =>
this.setState({
backgroundColor: rgbStringAndAlphaToRGBColor(color),
})
}
/>
{!some(propertiesEditors) && (
<EmptyMessage>
<Trans>
Any additional properties will appear here if you add
behaviors to objects, like Physics behavior.
</Trans>
</EmptyMessage>
)}
{propertiesEditors}
{this.props.onOpenMoreSettings && (
<RaisedButton
label={<Trans>Open advanced settings</Trans>}
fullWidth
onClick={() => {
if (this.props.onOpenMoreSettings)
this.props.onOpenMoreSettings();
this.props.onClose();
}}
/>
)}
</ColumnStackLayout>
</ScrollView>
</Dialog>
);
}

View File

@@ -57,27 +57,27 @@ const Toolbar = (props: Props) => {
<ToolbarIcon
onClick={props.openObjectsList}
src="res/ribbon_default/objects64.png"
tooltip={t`Open the objects editor`}
tooltip={t`Open Objects Panel`}
/>
<ToolbarIcon
onClick={props.openObjectGroupsList}
src={'res/ribbon_default/objectsgroups64.png'}
tooltip={t`Open the objects groups editor`}
tooltip={t`Open Object Groups Panel`}
/>
<ToolbarIcon
onClick={props.openProperties}
src="res/ribbon_default/editprop32.png"
tooltip={t`Open the properties panel`}
tooltip={t`Open Properties Panel`}
/>
<ToolbarIcon
onClick={props.toggleInstancesList}
src="res/ribbon_default/ObjectsPositionsList32.png"
tooltip={t`Open the list of instances`}
tooltip={t`Open Instances List Panel`}
/>
<ToolbarIcon
onClick={props.toggleLayersList}
src="res/ribbon_default/layers32.png"
tooltip={t`Open the layers editor`}
tooltip={t`Open Layers Panel`}
/>
<ToolbarSeparator />
<ToolbarIcon

View File

@@ -811,6 +811,44 @@ export default class SceneEditor extends React.Component<Props, State> {
done(true);
};
_onMoveInstancesZOrder = (where: 'front' | 'back') => {
const selectedInstances = this.instancesSelection.getSelectedInstances();
const layerNames = selectedInstances.reduce(
(acc: Set<string>, instance) => {
if (!instance.isLocked()) acc.add(instance.getLayer());
return acc;
},
new Set()
);
const highestZOrderFinder = new gd.HighestZOrderFinder();
const extremeZOrderByLayerName = {};
layerNames.forEach(layerName => {
highestZOrderFinder.reset();
highestZOrderFinder.restrictSearchToLayer(layerName);
this.props.initialInstances.iterateOverInstances(highestZOrderFinder);
extremeZOrderByLayerName[layerName] =
where === 'back'
? highestZOrderFinder.getLowestZOrder()
: highestZOrderFinder.getHighestZOrder();
});
highestZOrderFinder.delete();
selectedInstances.forEach(instance => {
if (!instance.isLocked()) {
const extremeZOrder = extremeZOrderByLayerName[instance.getLayer()];
// If instance is already at the extreme z order, do nothing.
if (instance.getZOrder() === extremeZOrder) return;
instance.setZOrder(extremeZOrder + (where === 'front' ? 1 : -1));
}
});
this.forceUpdateInstancesList();
this.forceUpdatePropertiesEditor();
};
_onDeleteGroup = (
groupWithContext: GroupWithContext,
done: boolean => void
@@ -1013,11 +1051,27 @@ export default class SceneEditor extends React.Component<Props, State> {
},
{
label: i18n._(t`Duplicate`),
enabled: this.instancesSelection.hasSelectedInstances(),
click: () => {
this.duplicateSelection();
},
},
{ type: 'separator' },
{
label: i18n._(t`Bring to front`),
enabled: this.instancesSelection.hasSelectedInstances(),
click: () => {
this._onMoveInstancesZOrder('front');
},
},
{
label: i18n._(t`Send to back`),
enabled: this.instancesSelection.hasSelectedInstances(),
click: () => {
this._onMoveInstancesZOrder('back');
},
},
{ type: 'separator' },
{
label: i18n._(t`Delete`),
click: () => this.deleteSelection(),
@@ -1324,7 +1378,7 @@ export default class SceneEditor extends React.Component<Props, State> {
},
'instances-list': {
type: 'secondary',
title: t`Instances list`,
title: t`Instances List`,
renderEditor: () => (
<InstancesList
instances={initialInstances}
@@ -1735,6 +1789,9 @@ export default class SceneEditor extends React.Component<Props, State> {
onApply={() => this.openSceneProperties(false)}
onEditVariables={() => this.editLayoutVariables(true)}
onOpenMoreSettings={this.props.onOpenMoreSettings}
resourceSources={resourceSources}
resourceExternalEditors={resourceExternalEditors}
onChooseResource={onChooseResource}
/>
)}
{!!this.state.layoutVariablesDialogOpen && (

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');

Some files were not shown because too many files have changed in this diff Show More