mirror of
https://github.com/4ian/GDevelop.git
synced 2026-10-12 02:01:17 +00:00
Add support for local variables for loop events (While, Repeat, For Each, For Each Child Variable) and loop counter variable (#8273)
- "For Each Child Variable" now also properly support choosing local variables for the variable to iterate on, and for the variable where to store the child and the optional variable where to store the name. - The loop counter starts from 0 and is automatically increased by 1 each time the event is repeated.
This commit is contained in:
@@ -13,7 +13,11 @@ using namespace std;
|
||||
namespace gd {
|
||||
|
||||
ForEachChildVariableEvent::ForEachChildVariableEvent()
|
||||
: BaseEvent(), valueIteratorVariableName("child"), keyIteratorVariableName(""), iterableVariableName("") {}
|
||||
: BaseEvent(),
|
||||
valueIteratorVariableName("child"),
|
||||
keyIteratorVariableName(""),
|
||||
iterableVariableName(""),
|
||||
variables(gd::VariablesContainer::SourceType::Local) {}
|
||||
|
||||
vector<gd::InstructionsList*> ForEachChildVariableEvent::GetAllConditionsVectors() {
|
||||
vector<gd::InstructionsList*> allConditions;
|
||||
@@ -88,6 +92,12 @@ void ForEachChildVariableEvent::SerializeTo(SerializerElement& element) const {
|
||||
if (!events.IsEmpty())
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
if (HasVariables()) {
|
||||
variables.SerializeTo(element.AddChild("variables"));
|
||||
}
|
||||
if (!loopIndexVariableName.empty()) {
|
||||
element.AddChild("loopIndexVariable").SetStringValue(loopIndexVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
void ForEachChildVariableEvent::UnserializeFrom(gd::Project& project,
|
||||
@@ -105,6 +115,16 @@ void ForEachChildVariableEvent::UnserializeFrom(gd::Project& project,
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
||||
variables.Clear();
|
||||
if (element.HasChild("variables")) {
|
||||
variables.UnserializeFrom(element.GetChild("variables"));
|
||||
}
|
||||
|
||||
loopIndexVariableName =
|
||||
element.HasChild("loopIndexVariable")
|
||||
? element.GetChild("loopIndexVariable").GetStringValue()
|
||||
: "";
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
@@ -34,6 +35,12 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
virtual bool CanHaveVariables() const { return true; }
|
||||
virtual const gd::VariablesContainer& GetVariables() const {
|
||||
return variables;
|
||||
};
|
||||
virtual gd::VariablesContainer& GetVariables() { return variables; };
|
||||
|
||||
const gd::InstructionsList& GetConditions() const { return conditions; };
|
||||
gd::InstructionsList& GetConditions() { return conditions; };
|
||||
|
||||
@@ -86,6 +93,9 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
*/
|
||||
void SetKeyIteratorVariableName(gd::String newName) { keyIteratorVariableName = newName; };
|
||||
|
||||
const gd::String& GetLoopIndexVariableName() const { return loopIndexVariableName; }
|
||||
void SetLoopIndexVariableName(const gd::String& name) { loopIndexVariableName = name; }
|
||||
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
const;
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllActionsVectors() const;
|
||||
@@ -109,6 +119,8 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
gd::EventsList events;
|
||||
VariablesContainer variables;
|
||||
gd::String loopIndexVariableName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -14,7 +14,9 @@ using namespace std;
|
||||
namespace gd {
|
||||
|
||||
ForEachEvent::ForEachEvent()
|
||||
: BaseEvent(), objectsToPick("") {}
|
||||
: BaseEvent(),
|
||||
objectsToPick(""),
|
||||
variables(gd::VariablesContainer::SourceType::Local) {}
|
||||
|
||||
vector<gd::InstructionsList*> ForEachEvent::GetAllConditionsVectors() {
|
||||
vector<gd::InstructionsList*> allConditions;
|
||||
@@ -77,6 +79,12 @@ void ForEachEvent::SerializeTo(SerializerElement& element) const {
|
||||
if (!events.IsEmpty())
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
if (HasVariables()) {
|
||||
variables.SerializeTo(element.AddChild("variables"));
|
||||
}
|
||||
if (!loopIndexVariableName.empty()) {
|
||||
element.AddChild("loopIndexVariable").SetStringValue(loopIndexVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
void ForEachEvent::UnserializeFrom(gd::Project& project,
|
||||
@@ -93,6 +101,16 @@ void ForEachEvent::UnserializeFrom(gd::Project& project,
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
||||
variables.Clear();
|
||||
if (element.HasChild("variables")) {
|
||||
variables.UnserializeFrom(element.GetChild("variables"));
|
||||
}
|
||||
|
||||
loopIndexVariableName =
|
||||
element.HasChild("loopIndexVariable")
|
||||
? element.GetChild("loopIndexVariable").GetStringValue()
|
||||
: "";
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
@@ -36,6 +37,12 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
virtual bool CanHaveVariables() const { return true; }
|
||||
virtual const gd::VariablesContainer& GetVariables() const {
|
||||
return variables;
|
||||
};
|
||||
virtual gd::VariablesContainer& GetVariables() { return variables; };
|
||||
|
||||
const gd::InstructionsList& GetConditions() const { return conditions; };
|
||||
gd::InstructionsList& GetConditions() { return conditions; };
|
||||
|
||||
@@ -49,6 +56,9 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
objectsToPick = gd::Expression(objectsToPick_);
|
||||
};
|
||||
|
||||
const gd::String& GetLoopIndexVariableName() const { return loopIndexVariableName; }
|
||||
void SetLoopIndexVariableName(const gd::String& name) { loopIndexVariableName = name; }
|
||||
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
const;
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllActionsVectors() const;
|
||||
@@ -69,6 +79,8 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
gd::EventsList events;
|
||||
VariablesContainer variables;
|
||||
gd::String loopIndexVariableName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace gd {
|
||||
RepeatEvent::RepeatEvent()
|
||||
: BaseEvent(),
|
||||
repeatNumberExpression(""),
|
||||
variables(gd::VariablesContainer::SourceType::Local),
|
||||
repeatNumberExpressionSelected(false) {}
|
||||
|
||||
vector<gd::InstructionsList*> RepeatEvent::GetAllConditionsVectors() {
|
||||
@@ -79,6 +80,12 @@ void RepeatEvent::SerializeTo(SerializerElement& element) const {
|
||||
if (!events.IsEmpty())
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
if (HasVariables()) {
|
||||
variables.SerializeTo(element.AddChild("variables"));
|
||||
}
|
||||
if (!loopIndexVariableName.empty()) {
|
||||
element.AddChild("loopIndexVariable").SetStringValue(loopIndexVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
void RepeatEvent::UnserializeFrom(gd::Project& project,
|
||||
@@ -97,6 +104,16 @@ void RepeatEvent::UnserializeFrom(gd::Project& project,
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
||||
variables.Clear();
|
||||
if (element.HasChild("variables")) {
|
||||
variables.UnserializeFrom(element.GetChild("variables"));
|
||||
}
|
||||
|
||||
loopIndexVariableName =
|
||||
element.HasChild("loopIndexVariable")
|
||||
? element.GetChild("loopIndexVariable").GetStringValue()
|
||||
: "";
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
@@ -30,6 +31,12 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
virtual bool CanHaveVariables() const { return true; }
|
||||
virtual const gd::VariablesContainer& GetVariables() const {
|
||||
return variables;
|
||||
};
|
||||
virtual gd::VariablesContainer& GetVariables() { return variables; };
|
||||
|
||||
const gd::InstructionsList& GetConditions() const { return conditions; };
|
||||
gd::InstructionsList& GetConditions() { return conditions; };
|
||||
|
||||
@@ -43,6 +50,9 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
|
||||
repeatNumberExpression = gd::Expression(repeatNumberExpression_);
|
||||
};
|
||||
|
||||
const gd::String& GetLoopIndexVariableName() const { return loopIndexVariableName; }
|
||||
void SetLoopIndexVariableName(const gd::String& name) { loopIndexVariableName = name; }
|
||||
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
@@ -63,6 +73,8 @@ class GD_CORE_API RepeatEvent : public gd::BaseEvent {
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
EventsList events;
|
||||
VariablesContainer variables;
|
||||
gd::String loopIndexVariableName;
|
||||
|
||||
bool repeatNumberExpressionSelected;
|
||||
};
|
||||
|
||||
@@ -56,6 +56,12 @@ void WhileEvent::SerializeTo(SerializerElement& element) const {
|
||||
if (!events.IsEmpty())
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
if (HasVariables()) {
|
||||
variables.SerializeTo(element.AddChild("variables"));
|
||||
}
|
||||
if (!loopIndexVariableName.empty()) {
|
||||
element.AddChild("loopIndexVariable").SetStringValue(loopIndexVariableName);
|
||||
}
|
||||
}
|
||||
|
||||
void WhileEvent::UnserializeFrom(gd::Project& project,
|
||||
@@ -76,6 +82,16 @@ void WhileEvent::UnserializeFrom(gd::Project& project,
|
||||
gd::EventsListSerialization::UnserializeEventsFrom(
|
||||
project, events, element.GetChild("events", 0, "Events"));
|
||||
}
|
||||
|
||||
variables.Clear();
|
||||
if (element.HasChild("variables")) {
|
||||
variables.UnserializeFrom(element.GetChild("variables"));
|
||||
}
|
||||
|
||||
loopIndexVariableName =
|
||||
element.HasChild("loopIndexVariable")
|
||||
? element.GetChild("loopIndexVariable").GetStringValue()
|
||||
: "";
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_WHILEEVENT_H
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
@@ -25,7 +26,10 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API WhileEvent : public gd::BaseEvent {
|
||||
public:
|
||||
WhileEvent() : infiniteLoopWarning(true), justCreatedByTheUser(true){};
|
||||
WhileEvent()
|
||||
: infiniteLoopWarning(true),
|
||||
justCreatedByTheUser(true),
|
||||
variables(gd::VariablesContainer::SourceType::Local){};
|
||||
virtual ~WhileEvent(){};
|
||||
virtual gd::WhileEvent* Clone() const { return new WhileEvent(*this); }
|
||||
|
||||
@@ -35,6 +39,12 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
|
||||
virtual const gd::EventsList& GetSubEvents() const { return events; };
|
||||
virtual gd::EventsList& GetSubEvents() { return events; };
|
||||
|
||||
virtual bool CanHaveVariables() const { return true; }
|
||||
virtual const gd::VariablesContainer& GetVariables() const {
|
||||
return variables;
|
||||
};
|
||||
virtual gd::VariablesContainer& GetVariables() { return variables; };
|
||||
|
||||
const gd::InstructionsList& GetConditions() const { return conditions; };
|
||||
gd::InstructionsList& GetConditions() { return conditions; };
|
||||
|
||||
@@ -51,6 +61,9 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
|
||||
|
||||
bool HasInfiniteLoopWarning() const { return infiniteLoopWarning; }
|
||||
|
||||
const gd::String& GetLoopIndexVariableName() const { return loopIndexVariableName; }
|
||||
void SetLoopIndexVariableName(const gd::String& name) { loopIndexVariableName = name; }
|
||||
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
virtual std::vector<const gd::InstructionsList*> GetAllConditionsVectors()
|
||||
@@ -71,6 +84,8 @@ class GD_CORE_API WhileEvent : public gd::BaseEvent {
|
||||
bool justCreatedByTheUser; ///< Used so as not to show message box to
|
||||
///< de/activate infinite loop warning when the
|
||||
///< user create the event
|
||||
gd::VariablesContainer variables;
|
||||
gd::String loopIndexVariableName;
|
||||
|
||||
int GetConditionsHeight() const;
|
||||
int GetActionsHeight() const;
|
||||
|
||||
@@ -437,6 +437,22 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
gd::String localVariablesInitializationCode = "";
|
||||
if (event_.HasVariables()) {
|
||||
GenerateLocalVariablesInitializationCode(
|
||||
event_.GetVariables(), codeGenerator,
|
||||
localVariablesInitializationCode);
|
||||
}
|
||||
|
||||
const bool hasIndexVariable = !event.GetLoopIndexVariableName().empty();
|
||||
gd::String whileIndexVar;
|
||||
gd::String indexVariableAccessor;
|
||||
if (hasIndexVariable) {
|
||||
whileIndexVar = "whileIndex" + gd::String::From(context.GetContextDepth());
|
||||
indexVariableAccessor = codeGenerator.GenerateAnyOrSceneVariableGetter(
|
||||
event.GetLoopIndexVariableName(), context);
|
||||
}
|
||||
|
||||
// Prepare codes
|
||||
gd::String whileConditionsStr =
|
||||
codeGenerator.GenerateConditionsListCode(event.GetWhileConditions(),
|
||||
@@ -458,9 +474,17 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
// Write final code
|
||||
gd::String whileBoolean =
|
||||
codeGenerator.GenerateBooleanFullName("stopDoWhile", context);
|
||||
outputCode += localVariablesInitializationCode;
|
||||
if (hasIndexVariable) {
|
||||
outputCode += "let " + whileIndexVar + " = 0;\n";
|
||||
}
|
||||
outputCode += "let " + whileBoolean + " = false;\n";
|
||||
outputCode += "do {\n";
|
||||
outputCode += codeGenerator.GenerateObjectsDeclarationCode(context);
|
||||
if (hasIndexVariable) {
|
||||
outputCode +=
|
||||
indexVariableAccessor + ".setNumber(" + whileIndexVar + ");\n";
|
||||
}
|
||||
outputCode += whileConditionsStr;
|
||||
outputCode += "if (" + whileIfPredicate + ") {\n";
|
||||
outputCode += conditionsCode;
|
||||
@@ -474,8 +498,15 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
outputCode += "} //Subevents end.\n";
|
||||
outputCode += "}\n";
|
||||
outputCode += "} else " + whileBoolean + " = true; \n";
|
||||
if (hasIndexVariable) {
|
||||
outputCode += whileIndexVar + "++;\n";
|
||||
}
|
||||
|
||||
outputCode += "} while (!" + whileBoolean + ");\n";
|
||||
if (event_.HasVariables()) {
|
||||
outputCode += codeGenerator.GenerateLocalVariablesStackAccessor() +
|
||||
".pop();\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
});
|
||||
@@ -494,6 +525,13 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
gd::String localVariablesInitializationCode = "";
|
||||
if (event_.HasVariables()) {
|
||||
GenerateLocalVariablesInitializationCode(
|
||||
event_.GetVariables(), codeGenerator,
|
||||
localVariablesInitializationCode);
|
||||
}
|
||||
|
||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(
|
||||
event.GetConditions(), context);
|
||||
gd::String actionsCode =
|
||||
@@ -521,8 +559,21 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
bool valueIteratorExists =
|
||||
!event.GetValueIteratorVariableName().empty();
|
||||
bool keyIteratorExists = !event.GetKeyIteratorVariableName().empty();
|
||||
const bool hasIndexVariable = !event.GetLoopIndexVariableName().empty();
|
||||
gd::String childIndexVar;
|
||||
gd::String indexVariableAccessor;
|
||||
if (hasIndexVariable) {
|
||||
childIndexVar = "childIndex" + gd::String::From(context.GetContextDepth());
|
||||
indexVariableAccessor = codeGenerator.GenerateAnyOrSceneVariableGetter(
|
||||
event.GetLoopIndexVariableName(), parentContext);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
outputCode += localVariablesInitializationCode;
|
||||
if (hasIndexVariable) {
|
||||
outputCode += "let " + childIndexVar + " = 0;\n";
|
||||
}
|
||||
|
||||
// Define references to variables (if they exist)
|
||||
if (keyIteratorExists)
|
||||
outputCode +=
|
||||
@@ -569,6 +620,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
" // Arrays are passed by reference like JS objects\n"
|
||||
" $VALUE_ITERATOR_REFERENCE.replaceChildrenArray($STRUCTURE_CHILD_VARIABLE.getAllChildrenArray());\n"
|
||||
" } else console.warn(\"Cannot identify type: \", type);\n";
|
||||
|
||||
if (hasIndexVariable)
|
||||
outputCode += " " + indexVariableAccessor + ".setNumber(" +
|
||||
childIndexVar + ");\n"
|
||||
" " + childIndexVar + "++;\n";
|
||||
// clang-format on
|
||||
|
||||
// Now do the rest of standard event code generation
|
||||
@@ -619,6 +675,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
iteratorReferenceVariableName);
|
||||
}
|
||||
|
||||
if (event_.HasVariables()) {
|
||||
outputCode += codeGenerator.GenerateLocalVariablesStackAccessor() +
|
||||
".pop();\n";
|
||||
}
|
||||
|
||||
return outputCode
|
||||
.FindAndReplace("$ITERATOR_KEY", iteratorKeyVariableName)
|
||||
.FindAndReplace("$STRUCTURE_CHILD_VARIABLE",
|
||||
@@ -649,6 +710,13 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
context.InheritsFrom(parentContext);
|
||||
context.ForbidReuse();
|
||||
|
||||
gd::String localVariablesInitializationCode = "";
|
||||
if (event_.HasVariables()) {
|
||||
GenerateLocalVariablesInitializationCode(
|
||||
event_.GetVariables(), codeGenerator,
|
||||
localVariablesInitializationCode);
|
||||
}
|
||||
|
||||
// Prepare conditions/actions codes
|
||||
gd::String conditionsCode = codeGenerator.GenerateConditionsListCode(
|
||||
event.GetConditions(), context);
|
||||
@@ -670,11 +738,22 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
"repeatCount" + gd::String::From(context.GetContextDepth());
|
||||
gd::String repeatIndexVar =
|
||||
"repeatIndex" + gd::String::From(context.GetContextDepth());
|
||||
const bool hasIndexVariable = !event.GetLoopIndexVariableName().empty();
|
||||
gd::String indexVariableAccessor;
|
||||
if (hasIndexVariable) {
|
||||
indexVariableAccessor = codeGenerator.GenerateAnyOrSceneVariableGetter(
|
||||
event.GetLoopIndexVariableName(), context);
|
||||
}
|
||||
outputCode += localVariablesInitializationCode;
|
||||
outputCode +=
|
||||
"const " + repeatCountVar + " = " + repeatCountCode + ";\n";
|
||||
outputCode += "for (let " + repeatIndexVar + " = 0;" + repeatIndexVar +
|
||||
" < " + repeatCountVar + ";++" + repeatIndexVar + ") {\n";
|
||||
outputCode += objectDeclaration;
|
||||
if (hasIndexVariable) {
|
||||
outputCode +=
|
||||
indexVariableAccessor + ".setNumber(" + repeatIndexVar + ");\n";
|
||||
}
|
||||
outputCode += conditionsCode;
|
||||
outputCode += "if (" + ifPredicate + ")\n";
|
||||
outputCode += "{\n";
|
||||
@@ -688,6 +767,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
|
||||
outputCode += "}\n";
|
||||
|
||||
if (event_.HasVariables()) {
|
||||
outputCode += codeGenerator.GenerateLocalVariablesStackAccessor() +
|
||||
".pop();\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
});
|
||||
|
||||
@@ -713,6 +797,13 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
context.ForbidReuse(); // TODO: This may not be necessary (to be
|
||||
// investigated/heavily tested).
|
||||
|
||||
gd::String localVariablesInitializationCode = "";
|
||||
if (event_.HasVariables()) {
|
||||
GenerateLocalVariablesInitializationCode(
|
||||
event_.GetVariables(), codeGenerator,
|
||||
localVariablesInitializationCode);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < realObjects.size(); ++i)
|
||||
context.EmptyObjectsListNeeded(realObjects[i]);
|
||||
|
||||
@@ -745,6 +836,14 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
codeGenerator.GetCodeNamespaceAccessor() + "forEachObjects" +
|
||||
gd::String::From(context.GetContextDepth());
|
||||
codeGenerator.AddGlobalDeclaration(forEachObjectsList + " = [];\n");
|
||||
const bool hasIndexVariable = !event.GetLoopIndexVariableName().empty();
|
||||
gd::String indexVariableAccessor;
|
||||
if (hasIndexVariable) {
|
||||
indexVariableAccessor = codeGenerator.GenerateAnyOrSceneVariableGetter(
|
||||
event.GetLoopIndexVariableName(), context);
|
||||
}
|
||||
|
||||
outputCode += localVariablesInitializationCode;
|
||||
|
||||
if (realObjects.size() !=
|
||||
1) //(We write a slightly more simple ( and optimized ) output code
|
||||
@@ -832,6 +931,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasIndexVariable) {
|
||||
outputCode +=
|
||||
indexVariableAccessor + ".setNumber(" + forEachIndexVar + ");\n";
|
||||
}
|
||||
|
||||
outputCode += conditionsCode;
|
||||
outputCode += "if (" + ifPredicate + ") {\n";
|
||||
outputCode += actionsCode;
|
||||
@@ -844,6 +948,11 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
|
||||
|
||||
outputCode += "}\n"; // End of for loop
|
||||
|
||||
if (event_.HasVariables()) {
|
||||
outputCode += codeGenerator.GenerateLocalVariablesStackAccessor() +
|
||||
".pop();\n";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
});
|
||||
|
||||
|
||||
@@ -1496,7 +1496,7 @@ interface InitialInstance {
|
||||
|
||||
boolean HasAnyOverriddenProperty([Const, Ref] gdObject obj);
|
||||
boolean HasAnyOverriddenPropertyForBehavior([Const, Ref] Behavior behavior);
|
||||
|
||||
|
||||
boolean HasBehaviorOverridingNamed([Const] DOMString name);
|
||||
Behavior AddNewBehaviorOverriding([Ref] Project project, [Const] DOMString type, [Const] DOMString name);
|
||||
[Ref] Behavior GetBehaviorOverriding([Const] DOMString name);
|
||||
@@ -2487,6 +2487,8 @@ interface RepeatEvent {
|
||||
[Ref] InstructionsList GetActions();
|
||||
void SetRepeatExpressionPlainString([Const] DOMString expr);
|
||||
[Const, Ref] Expression GetRepeatExpression();
|
||||
[Const, Ref] DOMString GetLoopIndexVariableName();
|
||||
void SetLoopIndexVariableName([Const] DOMString name);
|
||||
};
|
||||
RepeatEvent implements BaseEvent;
|
||||
|
||||
@@ -2496,6 +2498,8 @@ interface WhileEvent {
|
||||
[Ref] InstructionsList GetConditions();
|
||||
[Ref] InstructionsList GetWhileConditions();
|
||||
[Ref] InstructionsList GetActions();
|
||||
[Const, Ref] DOMString GetLoopIndexVariableName();
|
||||
void SetLoopIndexVariableName([Const] DOMString name);
|
||||
};
|
||||
WhileEvent implements BaseEvent;
|
||||
|
||||
@@ -2506,6 +2510,8 @@ interface ForEachEvent {
|
||||
[Const, Ref] DOMString GetObjectToPick();
|
||||
[Ref] InstructionsList GetConditions();
|
||||
[Ref] InstructionsList GetActions();
|
||||
[Const, Ref] DOMString GetLoopIndexVariableName();
|
||||
void SetLoopIndexVariableName([Const] DOMString name);
|
||||
};
|
||||
ForEachEvent implements BaseEvent;
|
||||
|
||||
@@ -2521,6 +2527,8 @@ interface ForEachChildVariableEvent {
|
||||
void SetIterableVariableName([Const] DOMString newName);
|
||||
void SetKeyIteratorVariableName([Const] DOMString newName);
|
||||
void SetValueIteratorVariableName([Const] DOMString newName);
|
||||
[Const, Ref] DOMString GetLoopIndexVariableName();
|
||||
void SetLoopIndexVariableName([Const] DOMString name);
|
||||
};
|
||||
ForEachChildVariableEvent implements BaseEvent;
|
||||
|
||||
|
||||
@@ -176,6 +176,9 @@ class Variable {
|
||||
}
|
||||
|
||||
getAllChildrenArray() {
|
||||
if (this._childrenArray.length > 0) {
|
||||
return this._childrenArray;
|
||||
}
|
||||
return Object.values(this._children);
|
||||
}
|
||||
|
||||
@@ -187,14 +190,32 @@ class Variable {
|
||||
return !!this._children[childName];
|
||||
}
|
||||
|
||||
castTo() {}
|
||||
castTo(newType) {
|
||||
if (newType === 'string' || newType === 'number' || newType === 'boolean') {
|
||||
this._children = {};
|
||||
this._childrenArray = [];
|
||||
} else if (newType === 'structure') {
|
||||
this._childrenArray = [];
|
||||
} else if (newType === 'array') {
|
||||
this._children = {};
|
||||
}
|
||||
}
|
||||
|
||||
isPrimitive() {
|
||||
return this.getAllChildrenArray().length === 0;
|
||||
return (
|
||||
this._childrenArray.length === 0 &&
|
||||
Object.keys(this._children).length === 0
|
||||
);
|
||||
}
|
||||
|
||||
getType() {
|
||||
return this.isPrimitive() ? 'number' : 'structure';
|
||||
if (this._childrenArray.length > 0) {
|
||||
return 'array';
|
||||
}
|
||||
if (Object.keys(this._children).length > 0) {
|
||||
return 'structure';
|
||||
}
|
||||
return 'number';
|
||||
}
|
||||
|
||||
removeChild(childName) {
|
||||
@@ -206,6 +227,43 @@ class Variable {
|
||||
this._childrenArray = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all the children with a new map of children.
|
||||
* @param {Object.<string, Variable>} newChildren
|
||||
*/
|
||||
replaceChildren(newChildren) {
|
||||
this._children = newChildren;
|
||||
this._childrenArray = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all the children with a new array of children.
|
||||
* @param {Variable[]} newChildren
|
||||
*/
|
||||
replaceChildrenArray(newChildren) {
|
||||
this._childrenArray = newChildren;
|
||||
this._children = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this variable into an equivalent JavaScript object.
|
||||
* @returns {any}
|
||||
*/
|
||||
toJSObject() {
|
||||
if (this._childrenArray.length > 0) {
|
||||
return this._childrenArray.map((child) => child.toJSObject());
|
||||
}
|
||||
if (Object.keys(this._children).length > 0) {
|
||||
const obj = {};
|
||||
for (const name in this._children) {
|
||||
if (this._children.hasOwnProperty(name))
|
||||
obj[name] = this._children[name].toJSObject();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
return this._value;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return Variable.copy(this, new Variable());
|
||||
}
|
||||
@@ -303,7 +361,12 @@ class VariablesContainer {
|
||||
const childVariable = variable.getChild(childVariableData.name);
|
||||
setupVariableFromVariableData(childVariable, childVariableData);
|
||||
});
|
||||
variable.setString(variableData.value);
|
||||
} else if (variableData.type === 'array') {
|
||||
variableData.children.forEach((childVariableData) => {
|
||||
const childVariable = new Variable();
|
||||
setupVariableFromVariableData(childVariable, childVariableData);
|
||||
variable._pushVariable(childVariable);
|
||||
});
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unsupported variable type in GDJS Mock:',
|
||||
@@ -966,6 +1029,10 @@ function makeMinimalGDJSMock(options) {
|
||||
wait: () => new FakeAsyncTask(),
|
||||
noop: () => {},
|
||||
},
|
||||
network: {
|
||||
variableStructureToJSON: (variable) =>
|
||||
JSON.stringify(variable.toJSObject()),
|
||||
},
|
||||
common: {
|
||||
resolveAsyncEventsFunction: ({ task }) => task.resolve(),
|
||||
},
|
||||
|
||||
@@ -122,427 +122,6 @@ describe('libGD.js - GDJS Code Generation integration tests', function () {
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(1);
|
||||
});
|
||||
|
||||
it('can generate a While event', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '=', '0'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
{
|
||||
infiniteLoopWarning: true,
|
||||
type: 'BuiltinCommonInstructions::While',
|
||||
whileConditions: [
|
||||
{
|
||||
type: { value: 'VarScene' },
|
||||
parameters: ['Counter', '<', '4'],
|
||||
},
|
||||
],
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', '1'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(4);
|
||||
});
|
||||
|
||||
it('can generate nested While events', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '=', '0'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['CellY', '=', '0'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
{
|
||||
infiniteLoopWarning: true,
|
||||
type: 'BuiltinCommonInstructions::While',
|
||||
whileConditions: [
|
||||
{
|
||||
type: { value: 'VarScene' },
|
||||
parameters: ['CellY', '<', '4'],
|
||||
},
|
||||
],
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['CellX', '=', '0'],
|
||||
},
|
||||
],
|
||||
events: [
|
||||
{
|
||||
infiniteLoopWarning: true,
|
||||
type: 'BuiltinCommonInstructions::While',
|
||||
whileConditions: [
|
||||
{
|
||||
type: { value: 'VarScene' },
|
||||
parameters: ['CellX', '<', '5'],
|
||||
},
|
||||
],
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['CellX', '+', '1'],
|
||||
},
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', '1'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Standard',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['CellY', '+', '1'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(20);
|
||||
});
|
||||
|
||||
it('can generate a Repeat event', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Repeat',
|
||||
repeatExpression: '4',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', '1'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(4);
|
||||
});
|
||||
|
||||
it('can generate a nested Repeat event', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Repeat',
|
||||
repeatExpression: '4',
|
||||
conditions: [],
|
||||
actions: [],
|
||||
events: [
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::Repeat',
|
||||
repeatExpression: '5',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', '1'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(20);
|
||||
});
|
||||
|
||||
it('can generate a "for each object" event', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEach',
|
||||
object: 'MyObject',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', 'MyObject.Variable(MyVariable)'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement,
|
||||
{
|
||||
parameterTypes: {
|
||||
MyObject: 'object',
|
||||
},
|
||||
logCode: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
|
||||
// Create 4 objects with variable values from 1 to 4.
|
||||
const objectLists = new gdjs.Hashtable();
|
||||
const myObjects = [];
|
||||
objectLists.put('MyObject', myObjects);
|
||||
for (let index = 1; index <= 4; index++) {
|
||||
const myObject = runtimeScene.createObject('MyObject');
|
||||
myObject.getVariables().get('MyVariable').setNumber(index);
|
||||
myObjects.push(myObject);
|
||||
}
|
||||
|
||||
runCompiledEvents(gdjs, runtimeScene, [objectLists]);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
1 + 2 + 3 + 4
|
||||
);
|
||||
});
|
||||
|
||||
it('can generate a nested "for each object" event', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEach',
|
||||
object: 'MyObjectA',
|
||||
conditions: [],
|
||||
actions: [],
|
||||
events: [
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEach',
|
||||
object: 'MyObjectB',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: [
|
||||
'Counter',
|
||||
'+',
|
||||
'MyObjectA.Variable(MyVariable) * MyObjectB.Variable(MyVariable)',
|
||||
],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement,
|
||||
{
|
||||
parameterTypes: {
|
||||
MyObjectA: 'object',
|
||||
MyObjectB: 'object',
|
||||
},
|
||||
logCode: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
|
||||
// Create 4 objects with variable values from 1 to 4.
|
||||
const objectALists = new gdjs.Hashtable();
|
||||
const myObjectsA = [];
|
||||
objectALists.put('MyObjectA', myObjectsA);
|
||||
for (let index = 1; index <= 4; index++) {
|
||||
const myObjectA = runtimeScene.createObject('MyObjectA');
|
||||
myObjectA.getVariables().get('MyVariable').setNumber(index);
|
||||
myObjectsA.push(myObjectA);
|
||||
}
|
||||
// Create 3 objects with variable values from 5 to 7.
|
||||
const objectBLists = new gdjs.Hashtable();
|
||||
const myObjectsB = [];
|
||||
objectBLists.put('MyObjectB', myObjectsB);
|
||||
for (let index = 1; index <= 3; index++) {
|
||||
const myObjectB = runtimeScene.createObject('MyObjectB');
|
||||
myObjectB
|
||||
.getVariables()
|
||||
.get('MyVariable')
|
||||
.setNumber(4 + index);
|
||||
myObjectsB.push(myObjectB);
|
||||
}
|
||||
|
||||
runCompiledEvents(gdjs, runtimeScene, [objectALists, objectBLists]);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
(1 + 2 + 3 + 4) * (5 + 6 + 7)
|
||||
);
|
||||
});
|
||||
|
||||
it('can generate a "for each child variable" event with undeclared variables', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEachChildVariable',
|
||||
iterableVariableName: 'MyVariable',
|
||||
valueIteratorVariableName: 'child',
|
||||
keyIteratorVariableName: '',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: ['Counter', '+', 'Variable(child)'],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement,
|
||||
{
|
||||
logCode: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
|
||||
// Create 4 child-variables with values from 1 to 4.
|
||||
const myVariable = runtimeScene.getVariables().get('MyVariable');
|
||||
for (let index = 1; index <= 4; index++) {
|
||||
myVariable.getChild('Child' + index).setNumber(index);
|
||||
}
|
||||
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
1 + 2 + 3 + 4
|
||||
);
|
||||
});
|
||||
|
||||
it('can generate a nested "for each child variable" event with undeclared variables', function () {
|
||||
const serializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEachChildVariable',
|
||||
iterableVariableName: 'MyVariableA',
|
||||
valueIteratorVariableName: 'childA',
|
||||
keyIteratorVariableName: '',
|
||||
conditions: [],
|
||||
actions: [],
|
||||
events: [
|
||||
{
|
||||
type: 'BuiltinCommonInstructions::ForEachChildVariable',
|
||||
iterableVariableName: 'MyVariableB',
|
||||
valueIteratorVariableName: 'childB',
|
||||
keyIteratorVariableName: '',
|
||||
conditions: [],
|
||||
actions: [
|
||||
{
|
||||
type: { value: 'ModVarScene' },
|
||||
parameters: [
|
||||
'Counter',
|
||||
'+',
|
||||
'Variable(childA) * Variable(childB)',
|
||||
],
|
||||
},
|
||||
],
|
||||
events: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const runCompiledEvents = generateCompiledEventsFromSerializedEvents(
|
||||
gd,
|
||||
serializerElement,
|
||||
{
|
||||
logCode: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { gdjs, runtimeScene } = makeMinimalGDJSMock();
|
||||
|
||||
// Create 4 child-variables with values from 1 to 4.
|
||||
const myVariableA = runtimeScene.getVariables().get('MyVariableA');
|
||||
for (let index = 1; index <= 4; index++) {
|
||||
myVariableA.getChild('Child' + index).setNumber(index);
|
||||
}
|
||||
// Create 4 child-variables with values from 5 to 7.
|
||||
const myVariableB = runtimeScene.getVariables().get('MyVariableB');
|
||||
for (let index = 1; index <= 3; index++) {
|
||||
myVariableB.getChild('Child' + index).setNumber(4 + index);
|
||||
}
|
||||
|
||||
runCompiledEvents(gdjs, runtimeScene, []);
|
||||
|
||||
expect(runtimeScene.getVariables().has('Counter')).toBe(true);
|
||||
expect(runtimeScene.getVariables().get('Counter').getAsNumber()).toBe(
|
||||
(1 + 2 + 3 + 4) * (5 + 6 + 7)
|
||||
);
|
||||
});
|
||||
|
||||
it('generates a working function creating objects', function () {
|
||||
const eventsSerializerElement = gd.Serializer.fromJSObject([
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+8
@@ -1913,6 +1913,8 @@ export class RepeatEvent extends BaseEvent {
|
||||
getActions(): InstructionsList;
|
||||
setRepeatExpressionPlainString(expr: string): void;
|
||||
getRepeatExpression(): Expression;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
}
|
||||
|
||||
export class WhileEvent extends BaseEvent {
|
||||
@@ -1920,6 +1922,8 @@ export class WhileEvent extends BaseEvent {
|
||||
getConditions(): InstructionsList;
|
||||
getWhileConditions(): InstructionsList;
|
||||
getActions(): InstructionsList;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
}
|
||||
|
||||
export class ForEachEvent extends BaseEvent {
|
||||
@@ -1928,6 +1932,8 @@ export class ForEachEvent extends BaseEvent {
|
||||
getObjectToPick(): string;
|
||||
getConditions(): InstructionsList;
|
||||
getActions(): InstructionsList;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
}
|
||||
|
||||
export class ForEachChildVariableEvent extends BaseEvent {
|
||||
@@ -1940,6 +1946,8 @@ export class ForEachChildVariableEvent extends BaseEvent {
|
||||
setIterableVariableName(newName: string): void;
|
||||
setKeyIteratorVariableName(newName: string): void;
|
||||
setValueIteratorVariableName(newName: string): void;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
}
|
||||
|
||||
export class CommentEvent extends BaseEvent {
|
||||
|
||||
@@ -9,6 +9,8 @@ declare class gdForEachChildVariableEvent extends gdBaseEvent {
|
||||
setIterableVariableName(newName: string): void;
|
||||
setKeyIteratorVariableName(newName: string): void;
|
||||
setValueIteratorVariableName(newName: string): void;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
||||
@@ -5,6 +5,8 @@ declare class gdForEachEvent extends gdBaseEvent {
|
||||
getObjectToPick(): string;
|
||||
getConditions(): gdInstructionsList;
|
||||
getActions(): gdInstructionsList;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
||||
@@ -5,6 +5,8 @@ declare class gdRepeatEvent extends gdBaseEvent {
|
||||
getActions(): gdInstructionsList;
|
||||
setRepeatExpressionPlainString(expr: string): void;
|
||||
getRepeatExpression(): gdExpression;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
||||
@@ -4,6 +4,8 @@ declare class gdWhileEvent extends gdBaseEvent {
|
||||
getConditions(): gdInstructionsList;
|
||||
getWhileConditions(): gdInstructionsList;
|
||||
getActions(): gdInstructionsList;
|
||||
getLoopIndexVariableName(): string;
|
||||
setLoopIndexVariableName(name: string): void;
|
||||
delete(): void;
|
||||
ptr: number;
|
||||
};
|
||||
@@ -17,6 +17,7 @@ export const instructionParameter = 'instruction-parameter';
|
||||
export const instructionInvalidParameter = 'instruction-invalid-parameter';
|
||||
export const elseTextContainer = 'else-text-container';
|
||||
export const invalidElse = 'invalid-else';
|
||||
export const eventLabel = 'event-label';
|
||||
export const instructionMissingParameter = 'instruction-missing-parameter';
|
||||
export const instructionWarningParameter = 'instruction-warning-parameter';
|
||||
export const instructionDeprecatedParameter =
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import VariableDeclarationsList from '../VariableDeclarationsList';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
selectableArea,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
instructionParameter,
|
||||
nameAndIconContainer,
|
||||
icon,
|
||||
eventLabel,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import AnyVariableField from '../../ParameterFields/AnyVariableField';
|
||||
@@ -56,10 +58,10 @@ const styles = {
|
||||
marginLeft: '3px',
|
||||
marginRight: '2px',
|
||||
},
|
||||
eventLabel: {
|
||||
marginLeft: '5px',
|
||||
whiteSpace: 'normal',
|
||||
wordWrap: 'break-word',
|
||||
labelText: {
|
||||
// Add an extra pixel so that it alignes with variables and other event labels
|
||||
// (which are selectable areas, so has an extra pixel of border)
|
||||
paddingLeft: '1px',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -209,12 +211,26 @@ export default class ForEachChildVariableEvent extends React.Component<
|
||||
[largeSelectedArea]: this.props.selected,
|
||||
})}
|
||||
>
|
||||
<div style={styles.eventLabel}>
|
||||
<VariableDeclarationsList
|
||||
variablesContainer={forEachChildVariableEvent.getVariables()}
|
||||
loopIndexVariableName={forEachChildVariableEvent.getLoopIndexVariableName()}
|
||||
onVariableDeclarationClick={this.props.onVariableDeclarationClick}
|
||||
onVariableDeclarationDoubleClick={
|
||||
this.props.onVariableDeclarationDoubleClick
|
||||
}
|
||||
className={'local-variables-container'}
|
||||
disabled={this.props.disabled}
|
||||
screenType={this.props.screenType}
|
||||
windowSize={this.props.windowSize}
|
||||
idPrefix={this.props.idPrefix}
|
||||
/>
|
||||
<div className={eventLabel}>
|
||||
<Trans>
|
||||
<span
|
||||
className={classNames({
|
||||
[disabledText]: this.props.disabled,
|
||||
})}
|
||||
style={styles.labelText}
|
||||
>
|
||||
For every child in
|
||||
<span
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import VariableDeclarationsList from '../VariableDeclarationsList';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
instructionParameter,
|
||||
nameAndIconContainer,
|
||||
instructionInvalidParameter,
|
||||
eventLabel,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ObjectField from '../../ParameterFields/ObjectField';
|
||||
@@ -118,7 +120,20 @@ export default class ForEachEvent extends React.Component<
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<VariableDeclarationsList
|
||||
variablesContainer={forEachEvent.getVariables()}
|
||||
loopIndexVariableName={forEachEvent.getLoopIndexVariableName()}
|
||||
onVariableDeclarationClick={this.props.onVariableDeclarationClick}
|
||||
onVariableDeclarationDoubleClick={
|
||||
this.props.onVariableDeclarationDoubleClick
|
||||
}
|
||||
className={'local-variables-container'}
|
||||
disabled={this.props.disabled}
|
||||
screenType={this.props.screenType}
|
||||
windowSize={this.props.windowSize}
|
||||
idPrefix={this.props.idPrefix}
|
||||
/>
|
||||
<div className={eventLabel}>
|
||||
<Trans>
|
||||
Repeat for each instance of
|
||||
<span
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import VariableDeclarationsList from '../VariableDeclarationsList';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
disabledText,
|
||||
instructionParameter,
|
||||
instructionInvalidParameter,
|
||||
eventLabel,
|
||||
} from '../ClassNames';
|
||||
import InlinePopover from '../../InlinePopover';
|
||||
import ExpressionField from '../../ParameterFields/ExpressionField';
|
||||
@@ -122,7 +124,20 @@ export default class RepeatEvent extends React.Component<
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<VariableDeclarationsList
|
||||
variablesContainer={repeatEvent.getVariables()}
|
||||
loopIndexVariableName={repeatEvent.getLoopIndexVariableName()}
|
||||
onVariableDeclarationClick={this.props.onVariableDeclarationClick}
|
||||
onVariableDeclarationDoubleClick={
|
||||
this.props.onVariableDeclarationDoubleClick
|
||||
}
|
||||
className={'local-variables-container'}
|
||||
disabled={this.props.disabled}
|
||||
screenType={this.props.screenType}
|
||||
windowSize={this.props.windowSize}
|
||||
idPrefix={this.props.idPrefix}
|
||||
/>
|
||||
<div className={eventLabel}>
|
||||
<span
|
||||
className={classNames({
|
||||
[selectableArea]: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import InstructionsList from '../InstructionsList';
|
||||
import VariableDeclarationsList from '../VariableDeclarationsList';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
largeSelectedArea,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
executableEventContainer,
|
||||
disabledText,
|
||||
conditionsContainer,
|
||||
eventLabel,
|
||||
} from '../ClassNames';
|
||||
import { type EventRendererProps } from './EventRenderer';
|
||||
import ConditionsActionsColumns from '../ConditionsActionsColumns';
|
||||
@@ -40,9 +42,23 @@ export default class WhileEvent extends React.Component<EventRendererProps, *> {
|
||||
[executableEventContainer]: true,
|
||||
})}
|
||||
>
|
||||
<VariableDeclarationsList
|
||||
variablesContainer={whileEvent.getVariables()}
|
||||
loopIndexVariableName={whileEvent.getLoopIndexVariableName()}
|
||||
onVariableDeclarationClick={this.props.onVariableDeclarationClick}
|
||||
onVariableDeclarationDoubleClick={
|
||||
this.props.onVariableDeclarationDoubleClick
|
||||
}
|
||||
className={'local-variables-container'}
|
||||
disabled={this.props.disabled}
|
||||
screenType={this.props.screenType}
|
||||
windowSize={this.props.windowSize}
|
||||
idPrefix={this.props.idPrefix}
|
||||
/>
|
||||
<div
|
||||
className={classNames({
|
||||
[disabledText]: this.props.disabled,
|
||||
[eventLabel]: true,
|
||||
})}
|
||||
>
|
||||
<Trans>While these conditions are true:</Trans>
|
||||
@@ -81,6 +97,7 @@ export default class WhileEvent extends React.Component<EventRendererProps, *> {
|
||||
<div
|
||||
className={classNames({
|
||||
[disabledText]: this.props.disabled,
|
||||
[eventLabel]: true,
|
||||
})}
|
||||
>
|
||||
<Trans>Repeat these:</Trans>
|
||||
|
||||
@@ -108,6 +108,9 @@ ${actions}`,
|
||||
},
|
||||
'BuiltinCommonInstructions::While': ({ event, padding }) => {
|
||||
const whileEvent = gd.asWhileEvent(event);
|
||||
const indexVarText = whileEvent.getLoopIndexVariableName()
|
||||
? ` (loop index variable: \`${whileEvent.getLoopIndexVariableName()}\`)`
|
||||
: '';
|
||||
const whileConditions = renderInstructionsAsText({
|
||||
instructionsList: whileEvent.getWhileConditions(),
|
||||
padding: padding + ' ',
|
||||
@@ -125,7 +128,7 @@ ${actions}`,
|
||||
});
|
||||
|
||||
return {
|
||||
content: `${padding}While these conditions are true:
|
||||
content: `${padding}While these conditions are true${indexVarText}:
|
||||
${whileConditions}
|
||||
${padding}Then do:
|
||||
${padding}Conditions:
|
||||
@@ -136,6 +139,9 @@ ${actions}`,
|
||||
},
|
||||
'BuiltinCommonInstructions::Repeat': ({ event, padding }) => {
|
||||
const repeatEvent = gd.asRepeatEvent(event);
|
||||
const indexVarText = repeatEvent.getLoopIndexVariableName()
|
||||
? ` (loop index variable: \`${repeatEvent.getLoopIndexVariableName()}\`)`
|
||||
: '';
|
||||
const conditions = renderInstructionsAsText({
|
||||
instructionsList: repeatEvent.getConditions(),
|
||||
padding: padding + ' ',
|
||||
@@ -150,7 +156,7 @@ ${actions}`,
|
||||
return {
|
||||
content: `${padding}Repeat \`${repeatEvent
|
||||
.getRepeatExpression()
|
||||
.getPlainString()}\` times these:
|
||||
.getPlainString()}\`${indexVarText} times these:
|
||||
${padding}Conditions:
|
||||
${conditions}
|
||||
${padding}Actions:
|
||||
@@ -159,6 +165,9 @@ ${actions}`,
|
||||
},
|
||||
'BuiltinCommonInstructions::ForEach': ({ event, padding }) => {
|
||||
const forEachEvent = gd.asForEachEvent(event);
|
||||
const indexVarText = forEachEvent.getLoopIndexVariableName()
|
||||
? ` (loop index variable: \`${forEachEvent.getLoopIndexVariableName()}\`)`
|
||||
: '';
|
||||
const conditions = renderInstructionsAsText({
|
||||
instructionsList: forEachEvent.getConditions(),
|
||||
padding: padding + ' ',
|
||||
@@ -171,7 +180,7 @@ ${actions}`,
|
||||
});
|
||||
|
||||
return {
|
||||
content: `${padding}Repeat these separately for each instance of ${forEachEvent.getObjectToPick()}:
|
||||
content: `${padding}Repeat these separately for each instance of ${forEachEvent.getObjectToPick()}${indexVarText}:
|
||||
${padding}Conditions:
|
||||
${conditions}
|
||||
${padding}Actions:
|
||||
@@ -180,6 +189,9 @@ ${actions}`,
|
||||
},
|
||||
'BuiltinCommonInstructions::ForEachChildVariable': ({ event, padding }) => {
|
||||
const forEachChildVariableEvent = gd.asForEachChildVariableEvent(event);
|
||||
const indexVarText = forEachChildVariableEvent.getLoopIndexVariableName()
|
||||
? ` (loop index variable: \`${forEachChildVariableEvent.getLoopIndexVariableName()}\`)`
|
||||
: '';
|
||||
const valueIteratorName = forEachChildVariableEvent.getValueIteratorVariableName();
|
||||
const keyIteratorName = forEachChildVariableEvent.getKeyIteratorVariableName();
|
||||
const iterableName = forEachChildVariableEvent.getIterableVariableName();
|
||||
@@ -198,7 +210,7 @@ ${actions}`,
|
||||
content: `${padding}For each child in \`${iterableName ||
|
||||
'(no variable chosen yet)'}\`, store the child in variable \`${valueIteratorName ||
|
||||
'(ignored)'}\`, the child name in \`${keyIteratorName ||
|
||||
'(ignored)'}\` and do:
|
||||
'(ignored)'}\` and do${indexVarText}:
|
||||
${padding}Conditions:
|
||||
${padding}${conditions}
|
||||
${padding}Actions:
|
||||
|
||||
@@ -30,6 +30,7 @@ export const reactDndInstructionType = 'GD_DRAGGED_VARIABLE_DECLARATION';
|
||||
type Props = {|
|
||||
variableName: string,
|
||||
variable: gdVariable,
|
||||
isLoopIndexVariable?: boolean,
|
||||
onClick: Function,
|
||||
onDoubleClick: () => void,
|
||||
selected: boolean,
|
||||
@@ -78,7 +79,7 @@ const getVariableValueAsString = (variable: gdVariable, i18n: I18nType) => {
|
||||
};
|
||||
|
||||
export const VariableDeclaration = (props: Props) => {
|
||||
const { variableName, variable, id } = props;
|
||||
const { variableName, variable, id, isLoopIndexVariable } = props;
|
||||
/**
|
||||
* Render the different parts of the text of the instruction.
|
||||
* Parameter can have formatting, be hovered and clicked. The rest
|
||||
@@ -89,6 +90,29 @@ export const VariableDeclaration = (props: Props) => {
|
||||
|
||||
const VariableTypeIcon = getVariableTypeIcon(variable.getType());
|
||||
|
||||
if (isLoopIndexVariable) {
|
||||
return (
|
||||
<span
|
||||
className={classNames({
|
||||
[disabledText]: disabled,
|
||||
})}
|
||||
>
|
||||
<Trans>
|
||||
Use{' '}
|
||||
<span>
|
||||
<LocalVariableIcon
|
||||
className={classNames({
|
||||
[icon]: true,
|
||||
})}
|
||||
/>
|
||||
{variableName}
|
||||
</span>{' '}
|
||||
as the loop counter
|
||||
</Trans>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames({
|
||||
|
||||
@@ -8,6 +8,7 @@ import { VariableDeclaration } from './VariableDeclaration';
|
||||
|
||||
type Props = {|
|
||||
variablesContainer: gdVariablesContainer,
|
||||
loopIndexVariableName?: ?string,
|
||||
onVariableDeclarationClick: VariableDeclarationContext => void,
|
||||
onVariableDeclarationDoubleClick: VariableDeclarationContext => void,
|
||||
className?: string,
|
||||
@@ -22,6 +23,7 @@ type Props = {|
|
||||
|
||||
export default function VariableDeclarationsList({
|
||||
variablesContainer,
|
||||
loopIndexVariableName,
|
||||
onVariableDeclarationClick,
|
||||
onVariableDeclarationDoubleClick,
|
||||
className,
|
||||
@@ -38,11 +40,14 @@ export default function VariableDeclarationsList({
|
||||
variablesContainer,
|
||||
variableName,
|
||||
};
|
||||
const isLoopIndexVariable =
|
||||
!!loopIndexVariableName && variableName === loopIndexVariableName;
|
||||
|
||||
return (
|
||||
<VariableDeclaration
|
||||
variableName={variableName}
|
||||
variable={variable}
|
||||
isLoopIndexVariable={isLoopIndexVariable}
|
||||
key={variable.ptr}
|
||||
onClick={() => onVariableDeclarationClick(variableDeclarationContext)}
|
||||
onDoubleClick={() =>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
serializeToJSObject,
|
||||
unserializeFromJSObject,
|
||||
} from '../Utils/Serializer';
|
||||
import newNameGenerator from '../Utils/NewNameGenerator';
|
||||
import {
|
||||
type HistoryState,
|
||||
type RevertableActionType,
|
||||
@@ -127,6 +128,12 @@ import { useHighlightedAiGeneratedEvent } from './UseHighlightedAiGeneratedEvent
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const zoomLevel = { min: 1, max: 50 };
|
||||
const loopEventTypes = [
|
||||
'BuiltinCommonInstructions::While',
|
||||
'BuiltinCommonInstructions::Repeat',
|
||||
'BuiltinCommonInstructions::ForEach',
|
||||
'BuiltinCommonInstructions::ForEachChildVariable',
|
||||
];
|
||||
|
||||
export type ChangeContext = {|
|
||||
events?: Array<EventContext>,
|
||||
@@ -898,6 +905,12 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
this._replaceSelectedEventType('BuiltinCommonInstructions::Standard'),
|
||||
visible: this._selectionIsElseEvent(),
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Remove the Loop Counter Variable`),
|
||||
click: () => this._removeLoopIndexVariable(),
|
||||
visible:
|
||||
this._selectionIsLoopEvent() && this._selectionHasIndexVariable(),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: i18n._(t`Add`),
|
||||
@@ -919,6 +932,9 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
this.props.shortcutMap['ADD_SUBEVENT']
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Local Variable`),
|
||||
click: () => this.addLocalVariable(),
|
||||
@@ -927,6 +943,15 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
this.props.shortcutMap['ADD_LOCAL_VARIABLE']
|
||||
),
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Loop Counter Variable`),
|
||||
click: () => this._addLoopIndexVariable(),
|
||||
visible:
|
||||
this._selectionIsLoopEvent() && !this._selectionHasIndexVariable(),
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Comment`),
|
||||
click: () => {
|
||||
@@ -1037,6 +1062,97 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
);
|
||||
};
|
||||
|
||||
_asLoopEvent = (event: gdBaseEvent): any | null => {
|
||||
const eventType = event.getType();
|
||||
if (eventType === 'BuiltinCommonInstructions::While')
|
||||
return gd.asWhileEvent(event);
|
||||
if (eventType === 'BuiltinCommonInstructions::Repeat')
|
||||
return gd.asRepeatEvent(event);
|
||||
if (eventType === 'BuiltinCommonInstructions::ForEach')
|
||||
return gd.asForEachEvent(event);
|
||||
if (eventType === 'BuiltinCommonInstructions::ForEachChildVariable')
|
||||
return gd.asForEachChildVariableEvent(event);
|
||||
return null;
|
||||
};
|
||||
|
||||
_getLastSelectedLoopEventContext = (): EventContext | null => {
|
||||
const eventContext = getLastSelectedEventContext(this.state.selection);
|
||||
if (!eventContext) return null;
|
||||
if (!loopEventTypes.includes(eventContext.event.getType())) return null;
|
||||
return eventContext;
|
||||
};
|
||||
|
||||
_selectionIsLoopEvent = () => {
|
||||
return !!this._getLastSelectedLoopEventContext();
|
||||
};
|
||||
|
||||
_selectionHasIndexVariable = () => {
|
||||
const eventContext = this._getLastSelectedLoopEventContext();
|
||||
if (!eventContext) return false;
|
||||
const loopEvent = this._asLoopEvent(eventContext.event);
|
||||
return !!loopEvent && loopEvent.getLoopIndexVariableName() !== '';
|
||||
};
|
||||
|
||||
_addLoopIndexVariable = () => {
|
||||
const eventContext = this._getLastSelectedLoopEventContext();
|
||||
if (!eventContext) return;
|
||||
|
||||
const loopEvent = this._asLoopEvent(eventContext.event);
|
||||
if (!loopEvent || loopEvent.getLoopIndexVariableName() !== '') return;
|
||||
|
||||
const projectScopedContainersAccessor =
|
||||
eventContext.projectScopedContainersAccessor;
|
||||
const generatedName = newNameGenerator('LoopIndex', name =>
|
||||
projectScopedContainersAccessor
|
||||
.get()
|
||||
.getVariablesContainersList()
|
||||
.has(name)
|
||||
);
|
||||
|
||||
const variablesContainer = loopEvent.getVariables();
|
||||
variablesContainer
|
||||
.insertNew(generatedName, variablesContainer.count())
|
||||
.setValue(0);
|
||||
loopEvent.setLoopIndexVariableName(generatedName);
|
||||
|
||||
if (this._eventsTree) {
|
||||
this._eventsTree.forceEventsUpdate(() => {
|
||||
const positions = this._getChangedEventRows([eventContext.event]);
|
||||
this._saveChangesToHistory('EDIT', {
|
||||
positionsBeforeAction: positions,
|
||||
positionAfterAction: positions,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_removeLoopIndexVariable = () => {
|
||||
const eventContext = this._getLastSelectedLoopEventContext();
|
||||
if (!eventContext) return;
|
||||
|
||||
const loopEvent = this._asLoopEvent(eventContext.event);
|
||||
if (!loopEvent) return;
|
||||
|
||||
const loopIndexVariableName = loopEvent.getLoopIndexVariableName();
|
||||
if (!loopIndexVariableName) return;
|
||||
|
||||
const variablesContainer = loopEvent.getVariables();
|
||||
if (variablesContainer.has(loopIndexVariableName)) {
|
||||
variablesContainer.remove(loopIndexVariableName);
|
||||
}
|
||||
loopEvent.setLoopIndexVariableName('');
|
||||
|
||||
if (this._eventsTree) {
|
||||
this._eventsTree.forceEventsUpdate(() => {
|
||||
const positions = this._getChangedEventRows([eventContext.event]);
|
||||
this._saveChangesToHistory('EDIT', {
|
||||
positionsBeforeAction: positions,
|
||||
positionAfterAction: positions,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_replaceSelectedEventType = (eventType: string) => {
|
||||
const eventContext = getLastSelectedEventContext(this.state.selection);
|
||||
if (!eventContext) return;
|
||||
@@ -2001,6 +2117,10 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
.editedParameter.eventContext
|
||||
? this.state.editedParameter.eventContext.projectScopedContainersAccessor
|
||||
: projectScopedContainersAccessor;
|
||||
const editedVariableLoopEvent =
|
||||
this.state.editedVariable && this.state.editedVariable.eventContext
|
||||
? this._asLoopEvent(this.state.editedVariable.eventContext.event)
|
||||
: null;
|
||||
|
||||
// Memorize the last size of the container div, that is used to render the events tree.
|
||||
// When the events editor tab is hidden, the container div width/height are 0.
|
||||
@@ -2295,6 +2415,25 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
this.state.editedVariable.shouldCreateVariable
|
||||
}
|
||||
isListLocked={false}
|
||||
loopIndexVariableName={
|
||||
editedVariableLoopEvent
|
||||
? editedVariableLoopEvent.getLoopIndexVariableName()
|
||||
: ''
|
||||
}
|
||||
onRenameLoopIndexVariable={
|
||||
editedVariableLoopEvent
|
||||
? newName => {
|
||||
editedVariableLoopEvent.setLoopIndexVariableName(newName);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
onRemoveLoopIndexVariable={
|
||||
editedVariableLoopEvent
|
||||
? () => {
|
||||
editedVariableLoopEvent.setLoopIndexVariableName('');
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{this.state.layoutVariablesDialogOpen && (
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
.gd-events-sheet .local-variables-container {
|
||||
background: var(--event-sheet-actions-background-color);
|
||||
color: var(--event-sheet-actions-color);
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
/* Prevent container to growing outside its parent. We don't have a way to prevent an infinitely
|
||||
* small variables column, but responsive design should avoid the problem on small screens. */
|
||||
min-width: 0;
|
||||
@@ -232,7 +232,7 @@
|
||||
}
|
||||
|
||||
.gd-events-sheet .else-text-container {
|
||||
padding-left: 1px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.gd-events-sheet .invalid-else {
|
||||
@@ -242,6 +242,12 @@
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.gd-events-sheet .event-label {
|
||||
margin-left: 5px;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search results highlight
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,9 @@ type Props = {|
|
||||
project: gdProject,
|
||||
projectScopedContainersAccessor: ProjectScopedContainersAccessor,
|
||||
variablesContainer: gdVariablesContainer,
|
||||
loopIndexVariableName?: ?string,
|
||||
onRenameLoopIndexVariable?: (newName: string) => void,
|
||||
onRemoveLoopIndexVariable?: () => void,
|
||||
onApply: (selectedVariableName: string | null) => void,
|
||||
onCancel: () => void,
|
||||
initiallySelectedVariableName: string,
|
||||
@@ -20,6 +23,9 @@ const LocalVariablesDialog = ({
|
||||
project,
|
||||
projectScopedContainersAccessor,
|
||||
variablesContainer,
|
||||
loopIndexVariableName,
|
||||
onRenameLoopIndexVariable,
|
||||
onRemoveLoopIndexVariable,
|
||||
open,
|
||||
onCancel,
|
||||
onApply,
|
||||
@@ -34,9 +40,17 @@ const LocalVariablesDialog = ({
|
||||
label: '',
|
||||
variablesContainer,
|
||||
onComputeAllVariableNames: () => [],
|
||||
loopIndexVariableName: loopIndexVariableName || '',
|
||||
onRenameLoopIndexVariable,
|
||||
onRemoveLoopIndexVariable,
|
||||
},
|
||||
],
|
||||
[variablesContainer]
|
||||
[
|
||||
variablesContainer,
|
||||
loopIndexVariableName,
|
||||
onRenameLoopIndexVariable,
|
||||
onRemoveLoopIndexVariable,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -26,6 +26,9 @@ type TabProps = {
|
||||
label: React.Node,
|
||||
variablesContainer: gdVariablesContainer,
|
||||
inheritedVariablesContainer?: gdVariablesContainer,
|
||||
loopIndexVariableName?: string,
|
||||
onRenameLoopIndexVariable?: (newName: string) => void,
|
||||
onRemoveLoopIndexVariable?: () => void,
|
||||
emptyPlaceholderTitle?: React.Node,
|
||||
emptyPlaceholderDescription?: React.Node,
|
||||
/**
|
||||
@@ -267,6 +270,9 @@ const VariablesEditorDialog = ({
|
||||
id,
|
||||
variablesContainer,
|
||||
inheritedVariablesContainer,
|
||||
loopIndexVariableName,
|
||||
onRenameLoopIndexVariable,
|
||||
onRemoveLoopIndexVariable,
|
||||
emptyPlaceholderTitle,
|
||||
emptyPlaceholderDescription,
|
||||
onComputeAllVariableNames,
|
||||
@@ -298,6 +304,9 @@ const VariablesEditorDialog = ({
|
||||
onVariablesUpdated={notifyOfChange}
|
||||
onSelectedVariableChange={onSelectedVariableChange}
|
||||
isListLocked={isListLocked}
|
||||
loopIndexVariableName={loopIndexVariableName}
|
||||
onRenameLoopIndexVariable={onRenameLoopIndexVariable}
|
||||
onRemoveLoopIndexVariable={onRemoveLoopIndexVariable}
|
||||
/>
|
||||
</Column>
|
||||
)
|
||||
|
||||
@@ -84,6 +84,7 @@ import { MultilineVariableEditorDialog } from './MultilineVariableEditorDialog';
|
||||
import { MarkdownText } from '../UI/MarkdownText';
|
||||
import Paper from '../UI/Paper';
|
||||
import { ProjectScopedContainersAccessor } from '../InstructionOrExpression/EventsScope';
|
||||
import ContextMenu, { type ContextMenuInterface } from '../UI/Menu/ContextMenu';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
@@ -117,6 +118,9 @@ type PlaceholderProps =
|
||||
type Props = {|
|
||||
projectScopedContainersAccessor: ProjectScopedContainersAccessor,
|
||||
variablesContainer: gdVariablesContainer,
|
||||
loopIndexVariableName?: string,
|
||||
onRenameLoopIndexVariable?: (newName: string) => void,
|
||||
onRemoveLoopIndexVariable?: () => void,
|
||||
areObjectVariables?: boolean,
|
||||
inheritedVariablesContainer?: gdVariablesContainer,
|
||||
initiallySelectedVariableName?: ?string,
|
||||
@@ -179,6 +183,8 @@ type VariableRowProps = {|
|
||||
type: Variable_Type,
|
||||
typeErrorMessage: MessageDescriptor | null,
|
||||
onChangeType: (string, nodeId: string) => void,
|
||||
isLoopIndexVariable: boolean,
|
||||
onRemoveLoopIndexVariable: () => void,
|
||||
hasMixedValues: boolean,
|
||||
valueAsString: string | null,
|
||||
valueAsBool: boolean | null,
|
||||
@@ -220,6 +226,8 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
isTopLevel,
|
||||
type,
|
||||
typeErrorMessage,
|
||||
isLoopIndexVariable,
|
||||
onRemoveLoopIndexVariable,
|
||||
onChangeType,
|
||||
hasMixedValues,
|
||||
valueAsString,
|
||||
@@ -255,6 +263,7 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
const hasLineBreaks = valueAsString
|
||||
? valueAsString.indexOf('\n') !== -1
|
||||
: false;
|
||||
const contextMenu = React.useRef<?ContextMenuInterface>(null);
|
||||
|
||||
const getContainerYPosition = React.useCallback(() => {
|
||||
if (containerRef.current) {
|
||||
@@ -270,7 +279,7 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
draggedNodeId.current = nodeId;
|
||||
return {};
|
||||
}}
|
||||
canDrag={() => !isInherited}
|
||||
canDrag={() => !isInherited && !isLoopIndexVariable}
|
||||
canDrop={() => canDrop(nodeId)}
|
||||
drop={() => {
|
||||
dropNode(nodeId, whereToDrop);
|
||||
@@ -308,6 +317,14 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
const shouldMultiSelect = event.metaKey || event.ctrlKey;
|
||||
onSelect(shouldMultiSelect, nodeId);
|
||||
}}
|
||||
onContextMenu={event => {
|
||||
if (!isLoopIndexVariable) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (contextMenu.current) {
|
||||
contextMenu.current.open(event.clientX, event.clientY);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{isOver && whereToDrop === 'before' && (
|
||||
<DropIndicator canDrop={canDrop} />
|
||||
@@ -386,7 +403,9 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
<Line noMargin alignItems="center">
|
||||
<Column noMargin>
|
||||
<VariableTypeSelector
|
||||
variableType={type}
|
||||
variableType={
|
||||
isLoopIndexVariable ? gd.Variable.Number : type
|
||||
}
|
||||
onChange={onChangeType}
|
||||
nodeId={nodeId}
|
||||
isHighlighted={isSelected}
|
||||
@@ -395,11 +414,33 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
}
|
||||
id={`variable-${index}-type`}
|
||||
errorMessage={typeErrorMessage}
|
||||
disabled={isTypeLocked}
|
||||
disabled={isTypeLocked || isLoopIndexVariable}
|
||||
/>
|
||||
</Column>
|
||||
<Column expand>
|
||||
{type === gd.Variable.Boolean ? (
|
||||
{isLoopIndexVariable ? (
|
||||
<Line noMargin alignItems="center">
|
||||
<span
|
||||
style={
|
||||
isSelected
|
||||
? {
|
||||
color:
|
||||
gdevelopTheme.listItem
|
||||
.selectedTextColor,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Text
|
||||
displayInlineAsSpan
|
||||
noMargin
|
||||
color="inherit"
|
||||
>
|
||||
<Trans>Counter of the loop</Trans>
|
||||
</Text>
|
||||
</span>
|
||||
</Line>
|
||||
) : type === gd.Variable.Boolean ? (
|
||||
<Line noMargin alignItems="center">
|
||||
<span
|
||||
style={
|
||||
@@ -594,6 +635,17 @@ const VariableRow = React.memo<VariableRowProps>(
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isLoopIndexVariable && (
|
||||
<ContextMenu
|
||||
ref={contextMenu}
|
||||
buildMenuTemplate={i18n => [
|
||||
{
|
||||
label: i18n._(t`Remove this counter of the loop`),
|
||||
click: onRemoveLoopIndexVariable,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -615,6 +667,16 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
})
|
||||
);
|
||||
|
||||
const {
|
||||
historyHandler,
|
||||
onVariablesUpdated,
|
||||
variablesContainer,
|
||||
onRemoveLoopIndexVariable,
|
||||
onRenameLoopIndexVariable,
|
||||
areObjectVariables,
|
||||
projectScopedContainersAccessor,
|
||||
} = props;
|
||||
|
||||
const [searchMatchingNodes, setSearchMatchingNodes] = React.useState<
|
||||
Array<string>
|
||||
>([]);
|
||||
@@ -640,6 +702,17 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
const gdevelopTheme = React.useContext(GDevelopThemeContext);
|
||||
const draggedNodeId = React.useRef<?string>(null);
|
||||
const forceUpdate = useForceUpdate();
|
||||
const [
|
||||
currentIndexVariableName,
|
||||
setCurrentIndexVariableName,
|
||||
] = React.useState(props.loopIndexVariableName || '');
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
setCurrentIndexVariableName(props.loopIndexVariableName || '');
|
||||
},
|
||||
[props.loopIndexVariableName]
|
||||
);
|
||||
|
||||
const [searchText, setSearchText] = React.useState<string>('');
|
||||
const { onComputeAllVariableNames, onSelectedVariableChange } = props;
|
||||
@@ -742,7 +815,6 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
})
|
||||
: [];
|
||||
|
||||
const { historyHandler, onVariablesUpdated, variablesContainer } = props;
|
||||
const _onChange = React.useCallback(
|
||||
() => {
|
||||
if (historyHandler) historyHandler.saveToHistory();
|
||||
@@ -963,12 +1035,18 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
if (nodeId.startsWith(inheritedPrefix)) return false;
|
||||
const { name, lineage } = getVariableContextFromNodeId(
|
||||
nodeId,
|
||||
props.variablesContainer
|
||||
variablesContainer
|
||||
);
|
||||
if (!name) return false;
|
||||
const parentVariable = getDirectParentVariable(lineage);
|
||||
if (!parentVariable) {
|
||||
props.variablesContainer.remove(name);
|
||||
variablesContainer.remove(name);
|
||||
if (name === currentIndexVariableName) {
|
||||
if (onRemoveLoopIndexVariable) {
|
||||
onRemoveLoopIndexVariable();
|
||||
}
|
||||
setCurrentIndexVariableName('');
|
||||
}
|
||||
} else {
|
||||
if (parentVariable.getType() === gd.Variable.Array) {
|
||||
parentVariable.removeAtIndex(parseInt(name, 10));
|
||||
@@ -978,7 +1056,7 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
}
|
||||
return true;
|
||||
},
|
||||
[props.variablesContainer]
|
||||
[currentIndexVariableName, onRemoveLoopIndexVariable, variablesContainer]
|
||||
);
|
||||
|
||||
const deleteNode = React.useCallback(
|
||||
@@ -992,6 +1070,32 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
[_onChange, forceUpdate, _deleteNode]
|
||||
);
|
||||
|
||||
const removeLoopIndexVariable = React.useCallback(
|
||||
(nodeId: string): void => {
|
||||
if (!currentIndexVariableName) return;
|
||||
const { name, depth } = getVariableContextFromNodeId(
|
||||
nodeId,
|
||||
props.variablesContainer
|
||||
);
|
||||
if (depth !== 0 || !name || name !== currentIndexVariableName) return;
|
||||
|
||||
const success = _deleteNode(nodeId);
|
||||
if (!success) return;
|
||||
|
||||
_onChange();
|
||||
setSelectedNodes([]);
|
||||
forceUpdate();
|
||||
},
|
||||
[
|
||||
currentIndexVariableName,
|
||||
forceUpdate,
|
||||
_deleteNode,
|
||||
_onChange,
|
||||
props.variablesContainer,
|
||||
setSelectedNodes,
|
||||
]
|
||||
);
|
||||
|
||||
const deleteSelection = React.useCallback(
|
||||
() => {
|
||||
// Take advantage of the node ids notation to sort them in
|
||||
@@ -1467,6 +1571,11 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
}
|
||||
const parentType = parentVariable ? parentVariable.getType() : null;
|
||||
const isSelected = selectedNodes.includes(nodeId);
|
||||
const isLoopIndexVariable =
|
||||
!isInherited &&
|
||||
isTopLevel &&
|
||||
!!currentIndexVariableName &&
|
||||
name === currentIndexVariableName;
|
||||
const overwritesInheritedVariable =
|
||||
isTopLevel &&
|
||||
!isInherited &&
|
||||
@@ -1547,6 +1656,8 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
isTopLevel={isTopLevel}
|
||||
type={type}
|
||||
typeErrorMessage={typeErrorMessage}
|
||||
isLoopIndexVariable={isLoopIndexVariable}
|
||||
onRemoveLoopIndexVariable={() => removeLoopIndexVariable(nodeId)}
|
||||
variablePointer={variablePointer}
|
||||
onChangeType={onChangeType}
|
||||
hasMixedValues={hasMixedValues}
|
||||
@@ -1625,7 +1736,7 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
|
||||
const { variable, lineage, name } = getVariableContextFromNodeId(
|
||||
nodeId,
|
||||
props.variablesContainer
|
||||
variablesContainer
|
||||
);
|
||||
if (name === null || !variable || newName === name) return;
|
||||
|
||||
@@ -1655,20 +1766,27 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
tentativeNewName =>
|
||||
(parentVariable && parentVariable.hasChild(tentativeNewName)) ||
|
||||
(!parentVariable &&
|
||||
(props.variablesContainer.has(tentativeNewName) ||
|
||||
(!props.areObjectVariables &&
|
||||
props.projectScopedContainersAccessor
|
||||
(variablesContainer.has(tentativeNewName) ||
|
||||
(!areObjectVariables &&
|
||||
projectScopedContainersAccessor
|
||||
.get()
|
||||
.getObjectsContainersList()
|
||||
.hasObjectOrGroupNamed(tentativeNewName))))
|
||||
);
|
||||
|
||||
if (!parentVariable) {
|
||||
props.variablesContainer.rename(name, safeAndUniqueNewName);
|
||||
variablesContainer.rename(name, safeAndUniqueNewName);
|
||||
} else {
|
||||
parentVariable.renameChild(name, safeAndUniqueNewName);
|
||||
}
|
||||
|
||||
if (!parentVariable && name === currentIndexVariableName) {
|
||||
setCurrentIndexVariableName(safeAndUniqueNewName);
|
||||
if (onRenameLoopIndexVariable) {
|
||||
onRenameLoopIndexVariable(safeAndUniqueNewName);
|
||||
}
|
||||
}
|
||||
|
||||
_onChange();
|
||||
updateExpandedAndSelectedNodesFollowingNameChange(
|
||||
nodeId,
|
||||
@@ -1677,10 +1795,12 @@ const VariablesList = React.forwardRef<Props, VariablesListInterface>(
|
||||
refocusNameField({ identifier: variable.ptr, caretPosition });
|
||||
},
|
||||
[
|
||||
props.variablesContainer,
|
||||
props.areObjectVariables,
|
||||
props.projectScopedContainersAccessor,
|
||||
variablesContainer,
|
||||
areObjectVariables,
|
||||
projectScopedContainersAccessor,
|
||||
currentIndexVariableName,
|
||||
_onChange,
|
||||
onRenameLoopIndexVariable,
|
||||
updateExpandedAndSelectedNodesFollowingNameChange,
|
||||
refocusNameField,
|
||||
]
|
||||
|
||||
@@ -473,24 +473,39 @@ export const makeTestProject = (gd /*: libGDevelop */) /*: TestProject */ => {
|
||||
const evt2 = testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Standard', 1);
|
||||
// Valid Else event (follows a Standard event)
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::ForEach', 2);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Else', 2);
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::While', 3);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::ForEach', 3);
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Repeat', 4);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::While', 4);
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Repeat', 5);
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(
|
||||
project,
|
||||
'BuiltinCommonInstructions::ForEachChildVariable',
|
||||
6
|
||||
);
|
||||
// Invalid Else event (follows a ForEachChildVariable, not a Standard event)
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Else', 7);
|
||||
var evt6 = testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Group', 5);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Group', 8);
|
||||
testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Link', 6);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Link', 9);
|
||||
var evt8 = testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::JsCode', 7);
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::JsCode', 10);
|
||||
var evtWithInvalidParameters = testLayout
|
||||
.getEvents()
|
||||
.insertNewEvent(project, 'BuiltinCommonInstructions::Standard', 0);
|
||||
|
||||
Reference in New Issue
Block a user