Add autocompletion for timers, tweens and other extensions identifiers (#4386)

This commit is contained in:
D8H
2022-10-13 13:39:53 +02:00
committed by GitHub
parent 7d892fd976
commit df3433c55f
22 changed files with 1522 additions and 275 deletions

View File

@@ -886,7 +886,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("expression", _("Time in seconds"))
.SetHidden();
@@ -900,7 +900,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.AddParameter("relationalOperator", _("Sign of the test"), "time")
.AddParameter("expression", _("Time in seconds"))
.SetManipulatedType("number");
@@ -913,7 +913,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/conditions/timerPaused24.png",
"res/conditions/timerPaused.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction(
@@ -926,7 +926,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "objectTimer");
obj.AddAction("PauseObjectTimer",
_("Pause an object timer"),
@@ -936,7 +936,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/pauseTimer24.png",
"res/actions/pauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction("UnPauseObjectTimer",
@@ -947,7 +947,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/unPauseTimer24.png",
"res/actions/unPauseTimer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddAction("RemoveObjectTimer",
@@ -958,7 +958,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "objectTimer")
.MarkAsAdvanced();
obj.AddExpression("X",
@@ -1127,7 +1127,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
_("Object timers"),
"res/actions/time.png")
.AddParameter("object", _("Object"))
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "objectTimer");
obj.AddExpression("AngleToObject",
_("Angle between two objects"),

View File

@@ -36,7 +36,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("expression", _("Time in seconds"))
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.SetHidden();
extension
@@ -50,7 +50,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timer24.png",
"res/conditions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.AddParameter("relationalOperator", _("Sign of the test"), "time")
.AddParameter("expression", _("Time in seconds"))
.SetManipulatedType("number");
@@ -78,7 +78,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/conditions/timerPaused24.png",
"res/conditions/timerPaused.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -93,7 +93,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
extension
.AddAction("PauseTimer",
@@ -105,7 +105,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/pauseTimer24.png",
"res/actions/pauseTimer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -118,7 +118,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/unPauseTimer24.png",
"res/actions/unPauseTimer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -131,7 +131,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"res/actions/timer24.png",
"res/actions/timer.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"))
.AddParameter("identifier", _("Timer's name"), "sceneTimer")
.MarkAsAdvanced();
extension
@@ -191,7 +191,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
"",
"res/actions/time.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Timer's name"));
.AddParameter("identifier", _("Timer's name"), "sceneTimer");
extension
.AddExpression("TimeFromStart",

View File

@@ -199,7 +199,8 @@ class GD_CORE_API ParameterMetadata {
parameterType == "objectAnimationName" ||
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId";
parameterType == "leaderboardId" ||
parameterType == "identifier";
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";

View File

@@ -73,4 +73,54 @@ bool ArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction,
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {}
ReadOnlyArbitraryEventsWorker::~ReadOnlyArbitraryEventsWorker() {}
void ReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList& events) {
DoVisitEventList(events);
for (std::size_t i = 0; i < events.size(); ++i) {
VisitEvent(events[i]);
if (events[i].CanHaveSubEvents()) {
VisitEventList(events[i].GetSubEvents());
}
}
}
void ReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& event) {
DoVisitEvent(event);
const vector<const gd::InstructionsList*> conditionsVectors =
event.GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
VisitInstructionList(*conditionsVectors[j], true);
}
const vector<const gd::InstructionsList*> actionsVectors = event.GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
VisitInstructionList(*actionsVectors[j], false);
}
}
void ReadOnlyArbitraryEventsWorker::VisitInstructionList(
const gd::InstructionsList& instructions, bool areConditions) {
DoVisitInstructionList(instructions, areConditions);
for (std::size_t i = 0; i < instructions.size(); ++i) {
VisitInstruction(instructions[i], areConditions);
if (!instructions[i].GetSubInstructions().empty()) {
VisitInstructionList(instructions[i].GetSubInstructions(),
areConditions);
}
}
}
void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& instruction,
bool isCondition) {
DoVisitInstruction(instruction, isCondition);
}
ReadOnlyArbitraryEventsWorkerWithContext::~ReadOnlyArbitraryEventsWorkerWithContext() {}
} // namespace gd

View File

@@ -121,6 +121,101 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
const gd::ObjectsContainer* currentObjectsContainer;
};
/**
* \brief ReadOnlyArbitraryEventsWorker is an abstract class used to browse events (and
* instructions). It can be used to implement autocompletion for example.
*
* \see gd::ReadOnlyArbitraryEventsWorkerWithContext
*
* \ingroup IDE
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorker(){};
virtual ~ReadOnlyArbitraryEventsWorker();
/**
* \brief Launch the worker on the specified events list.
*/
void Launch(const gd::EventsList& events) { VisitEventList(events); };
private:
void VisitEventList(const gd::EventsList& events);
void VisitEvent(const gd::BaseEvent& event);
void VisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions);
void VisitInstruction(const gd::Instruction& instruction, bool isCondition);
/**
* Called to do some work on an event list.
*/
virtual void DoVisitEventList(const gd::EventsList& events){};
/**
* Called to do some work on an event
*/
virtual void DoVisitEvent(const gd::BaseEvent& event) {};
/**
* Called to do some work on an instruction list
*/
virtual void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions){};
/**
* Called to do some work on an instruction.
*/
virtual void DoVisitInstruction(const gd::Instruction& instruction,
bool isCondition) {};
};
/**
* \brief An events worker that will know about the context (the objects
* container). Useful for workers working on expressions notably.
*
* \see gd::ReadOnlyArbitraryEventsWorker
*
* \ingroup IDE
*/
class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
: public ReadOnlyArbitraryEventsWorker {
public:
ReadOnlyArbitraryEventsWorkerWithContext()
: currentGlobalObjectsContainer(nullptr),
currentObjectsContainer(nullptr){};
virtual ~ReadOnlyArbitraryEventsWorkerWithContext();
/**
* \brief Launch the worker on the specified events list,
* giving the objects container on which the events are applying to.
*/
void Launch(const gd::EventsList& events,
const gd::ObjectsContainer& globalObjectsContainer_,
const gd::ObjectsContainer& objectsContainer_) {
currentGlobalObjectsContainer = &globalObjectsContainer_;
currentObjectsContainer = &objectsContainer_;
ReadOnlyArbitraryEventsWorker::Launch(events);
};
void Launch(gd::EventsList& events) = delete;
protected:
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentGlobalObjectsContainer;
};
const gd::ObjectsContainer& GetObjectsContainer() {
// Pointers are guaranteed to be not nullptr after
// Launch was called.
return *currentObjectsContainer;
};
private:
const gd::ObjectsContainer* currentGlobalObjectsContainer;
const gd::ObjectsContainer* currentObjectsContainer;
};
} // namespace gd
#endif // GDCORE_ARBITRARYEVENTSWORKER_H

View File

@@ -0,0 +1,254 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "EventsIdentifiersFinder.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/IDE/DependenciesAnalyzer.h"
using namespace std;
namespace gd {
namespace {
/**
* \brief Go through the nodes to search for identifier occurrences.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API IdentifierFinderExpressionNodeWorker
: public ExpressionParser2NodeWorker {
public:
IdentifierFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
const gd::String& identifierType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
identifierType(identifierType_),
objectName(objectName_){};
virtual ~IdentifierFinderExpressionNodeWorker(){};
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
node.expression->Visit(*this);
}
void OnVisitOperatorNode(OperatorNode& node) override {
node.leftHandSide->Visit(*this);
node.rightHandSide->Visit(*this);
}
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
node.factor->Visit(*this);
}
void OnVisitNumberNode(NumberNode& node) override {}
void OnVisitTextNode(TextNode& node) override {}
void OnVisitVariableNode(VariableNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
if (node.child) node.child->Visit(*this);
}
void OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) override {
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
}
void OnVisitIdentifierNode(IdentifierNode& node) override {}
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
const bool isObjectFunction = !node.objectName.empty();
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
size_t parameterIndex = 0;
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
auto& parameterMetadata = metadata.parameters[metadataIndex];
if (parameterMetadata.IsCodeOnly()) {
continue;
}
auto& parameterNode = node.parameters[parameterIndex];
++parameterIndex;
if (considerFunction && parameterMetadata.GetType() == "identifier"
&& parameterMetadata.GetExtraInfo() == identifierType) {
// Store the value of the parameter
results.insert(
gd::ExpressionParser2NodePrinter::PrintNode(*parameterNode));
} else {
parameterNode->Visit(*this);
}
}
}
void OnVisitEmptyNode(EmptyNode& node) override {}
private:
const gd::Platform &platform;
const gd::ObjectsContainer &globalObjectsContainer;
const gd::ObjectsContainer &objectsContainer;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String identifierType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
/**
* \brief Go through the events to search for identifier occurrences.
*/
class GD_CORE_API IdentifierFinderEventWorker
: public ReadOnlyArbitraryEventsWorkerWithContext {
public:
IdentifierFinderEventWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::String& identifierType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
identifierType(identifierType_),
objectName(objectName_){};
virtual ~IdentifierFinderEventWorker(){};
void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions) override {
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
auto& instruction = instructions[aId];
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
areConditions ? MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: MetadataProvider::GetActionMetadata(
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) {
//...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) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = instruction.GetParameter(pNb).GetRootNode();
IdentifierFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
identifierType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}
}
}
};
private:
const gd::Platform &platform;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String identifierType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
} // namespace
std::set<gd::String> EventsIdentifiersFinder::FindAllIdentifierExpressions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& contextObjectName) {
std::set<gd::String> results;
const bool isObjectIdentifier = identifierType.find("object") == 0;
// The object from the context is only relevent for object identifiers.
auto& actualObjectName = isObjectIdentifier ? contextObjectName : "";
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
identifierType,
actualObjectName);
return results;
}
void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName) {
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
IdentifierFinderEventWorker eventWorker(results,
platform,
identifierType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
}
}
} // namespace gd

View File

@@ -0,0 +1,81 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EVENTSIDENTIFIERSFINDER_H
#define EVENTSIDENTIFIERSFINDER_H
#include <set>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/String.h"
namespace gd {
class Instruction;
class Platform;
class Object;
class Project;
class Layout;
} // namespace gd
namespace gd {
/**
* \brief Perform a search over a layout, searching for layout or object custom
* identifiers.
*
* \todo Refactor this class using ArbitraryEventsWorker
*
* \ingroup IDE
*/
class EventsIdentifiersFinder {
public:
EventsIdentifiersFinder(){};
virtual ~EventsIdentifiersFinder(){};
/**
* Construct a list containing all the expressions for a given identifier used
* in the layout.
*
* \param project The project to use.
* \param layout The layout to use.
* \param identifierType The identifier type to be analyzed.
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
* \return A std::set containing the names of all identifiers used.
*/
static std::set<gd::String> FindAllIdentifierExpressions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName = "");
private:
/**
* Construct a list containing all the expressions for a given identifier used
* in the layout. It searches in events dependencies.
*
* \param results A std::set to fill with the expressions used for all parameters of the
* specified identifier type
* \param platform The platform of the project
* \param project The project to use.
* \param layout The layout to use.
* \param events The events to be analyzed
* \param identifierType The identifier type to be analyzed
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*/
static void FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& identifierType,
const gd::String& objectName = "");
};
} // namespace gd
#endif // EVENTSIDENTIFIERSFINDER_H

View File

@@ -13,6 +13,7 @@
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
@@ -22,28 +23,28 @@
using namespace std;
namespace gd {
namespace {
/**
* \brief Go through the nodes and change the given object name to a new one.
* \brief Go through the nodes to search for variable occurrences.
*
* \see gd::ExpressionParser2
*/
class GD_CORE_API ExpressionParameterSearcher
class GD_CORE_API VariableFinderExpressionNodeWorker
: public ExpressionParser2NodeWorker {
public:
ExpressionParameterSearcher(const gd::Platform &platform_,
VariableFinderExpressionNodeWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::ObjectsContainer &globalObjectsContainer_,
const gd::ObjectsContainer &objectsContainer_,
std::set<gd::String>& results_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: platform(platform_),
: results(results_),
platform(platform_),
globalObjectsContainer(globalObjectsContainer_),
objectsContainer(objectsContainer_),
results(results_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~ExpressionParameterSearcher(){};
virtual ~VariableFinderExpressionNodeWorker(){};
protected:
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
@@ -74,27 +75,34 @@ class GD_CORE_API ExpressionParameterSearcher
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
bool considerFunction = objectName.empty() || node.objectName == objectName;
const gd::ExpressionMetadata &metadata = node.objectName.empty() ?
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName) :
const bool isObjectFunction = !node.objectName.empty();
const gd::ExpressionMetadata &metadata = isObjectFunction ?
MetadataProvider::GetObjectAnyExpressionMetadata(
platform,
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
node.functionName);
node.functionName):
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
return;
}
for (size_t i = 0; i < node.parameters.size() &&
i < metadata.parameters.size();
++i) {
auto& parameterMetadata = metadata.parameters[i];
size_t parameterIndex = 0;
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
auto& parameterMetadata = metadata.parameters[metadataIndex];
if (parameterMetadata.IsCodeOnly()) {
continue;
}
auto& parameterNode = node.parameters[parameterIndex];
++parameterIndex;
if (considerFunction && parameterMetadata.GetType() == parameterType) {
// Store the value of the parameter
results.insert(
gd::ExpressionParser2NodePrinter::PrintNode(*node.parameters[i]));
gd::ExpressionParser2NodePrinter::PrintNode(*parameterNode));
} else {
node.parameters[i]->Visit(*this);
parameterNode->Visit(*this);
}
}
}
@@ -112,18 +120,87 @@ class GD_CORE_API ExpressionParameterSearcher
///< account only if related to this object.
};
/**
* \brief Go through the events to search for variable occurrences.
*/
class GD_CORE_API VariableFinderEventWorker
: public ReadOnlyArbitraryEventsWorkerWithContext {
public:
VariableFinderEventWorker(std::set<gd::String>& results_,
const gd::Platform &platform_,
const gd::String& parameterType_,
const gd::String& objectName_ = "")
: results(results_),
platform(platform_),
parameterType(parameterType_),
objectName(objectName_){};
virtual ~VariableFinderEventWorker(){};
void DoVisitInstructionList(const gd::InstructionsList& instructions,
bool areConditions) override {
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
auto& instruction = instructions[aId];
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
areConditions ? MetadataProvider::GetConditionMetadata(
platform, instruction.GetType())
: MetadataProvider::GetActionMetadata(
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) {
//...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) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = instruction.GetParameter(pNb).GetRootNode();
VariableFinderExpressionNodeWorker searcher(
results,
platform,
GetGlobalObjectsContainer(),
GetObjectsContainer(),
parameterType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
lastObjectParameter =
instruction.GetParameter(pNb).GetPlainString();
}
}
}
};
private:
const gd::Platform &platform;
std::set<gd::String>& results; ///< Reference to the std::set where argument
///< values must be stored.
gd::String parameterType; ///< The type of the parameters to be searched for.
gd::String objectName; ///< If not empty, parameters will be taken into
///< account only if related to this object.
};
} // namespace
std::set<gd::String> EventsVariablesFinder::FindAllGlobalVariables(
const gd::Platform& platform, const gd::Project& project) {
std::set<gd::String> results;
for (std::size_t i = 0; i < project.GetLayoutsCount(); ++i) {
std::set<gd::String> results2 =
FindArgumentsInEventsAndDependencies(
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
project.GetLayout(i),
"globalvar");
results.insert(results2.begin(), results2.end());
}
return results;
@@ -135,9 +212,12 @@ std::set<gd::String> EventsVariablesFinder::FindAllLayoutVariables(
const gd::Layout& layout) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
platform, project, layout, "scenevar");
results.insert(results2.begin(), results2.end());
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
"scenevar");
return results;
}
@@ -149,159 +229,51 @@ std::set<gd::String> EventsVariablesFinder::FindAllObjectVariables(
const gd::Object& object) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEventsAndDependencies(
FindArgumentsInEventsAndDependencies(
results,
platform,
project,
layout,
"objectvar",
object.GetName());
results.insert(results2.begin(), results2.end());
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInInstructions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::InstructionsList& instructions,
bool instructionsAreConditions,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
for (std::size_t aId = 0; aId < instructions.size(); ++aId) {
gd::String lastObjectParameter = "";
const gd::InstructionMetadata& instrInfos =
instructionsAreConditions ? MetadataProvider::GetConditionMetadata(
platform, instructions[aId].GetType())
: MetadataProvider::GetActionMetadata(
platform, instructions[aId].GetType());
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
// The parameter has the searched type...
if (instrInfos.parameters[pNb].type == parameterType) {
//...remember the value of the parameter.
if (objectName.empty() || lastObjectParameter == objectName)
results.insert(instructions[aId].GetParameter(pNb).GetPlainString());
}
// Search in expressions
else if (ParameterMetadata::IsExpression(
"number", instrInfos.parameters[pNb].type) ||
ParameterMetadata::IsExpression(
"string", instrInfos.parameters[pNb].type)) {
auto node = instructions[aId].GetParameter(pNb).GetRootNode();
ExpressionParameterSearcher searcher(
platform,
project,
layout,
results,
parameterType,
objectName);
node->Visit(searcher);
}
// Remember the value of the last "object" parameter.
else if (gd::ParameterMetadata::IsObject(
instrInfos.parameters[pNb].type)) {
lastObjectParameter =
instructions[aId].GetParameter(pNb).GetPlainString();
}
}
if (!instructions[aId].GetSubInstructions().empty())
FindArgumentsInInstructions(platform,
project,
layout,
instructions[aId].GetSubInstructions(),
instructionsAreConditions,
parameterType);
}
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
std::set<gd::String> results2 = FindArgumentsInEvents(
platform, project, layout, layout.GetEvents(), parameterType, objectName);
results.insert(results2.begin(), results2.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(layout.GetEvents(), project, layout);
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
dependenciesAnalyzer.Analyze();
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, layout, externalEvents.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
std::set<gd::String> results3 = FindArgumentsInEvents(
platform, project, dependencyLayout, dependencyLayout.GetEvents(), parameterType, objectName);
results.insert(results3.begin(), results3.end());
VariableFinderEventWorker eventWorker(results,
platform,
parameterType,
objectName);
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
}
return results;
}
std::set<gd::String> EventsVariablesFinder::FindArgumentsInEvents(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::EventsList& events,
const gd::String& parameterType,
const gd::String& objectName) {
std::set<gd::String> results;
for (std::size_t i = 0; i < events.size(); ++i) {
vector<const gd::InstructionsList*> conditionsVectors =
events[i].GetAllConditionsVectors();
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
std::set<gd::String> results2 =
FindArgumentsInInstructions(platform,
project,
layout,
*conditionsVectors[j],
/*conditions=*/true,
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
vector<const gd::InstructionsList*> actionsVectors =
events[i].GetAllActionsVectors();
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
std::set<gd::String> results2 =
FindArgumentsInInstructions(platform,
project,
layout,
*actionsVectors[j],
/*conditions=*/false,
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
if (events[i].CanHaveSubEvents()) {
std::set<gd::String> results2 =
FindArgumentsInEvents(platform,
project,
layout,
events[i].GetSubEvents(),
parameterType,
objectName);
results.insert(results2.begin(), results2.end());
}
}
return results;
}
} // namespace gd

View File

@@ -23,7 +23,6 @@ namespace gd {
* \brief Perform a search over a project or a layout, searching for layout,
* global or object variables.
*
* \todo Refactor this class using ArbitraryEventsWorker
* \todo Rework this class to return the shapes (maybe even types?) of the
* variables (in particular for structures and arrays), so we can use this
* for better autocompletions in the variables dialogs in the IDE.
@@ -74,34 +73,13 @@ class EventsVariablesFinder {
const gd::Object& object);
private:
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType
*
* \param project The project used
* \param project The layout used
* \param instructions The instructions to be analyzed
* \param instructionsAreConditions True if the instructions are conditions.
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account only
* if the last object parameter is filled with this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInInstructions(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::InstructionsList& instructions,
bool instructionsAreConditions,
const gd::String& parameterType,
const gd::String& objectName = "");
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType. It searchs in events dependencies.
* parameterType. It searches in events dependencies.
*
* \param results A std::set to fill with the values used for all parameters of the
* specified type
* \param platform The platform of the project
* \param project The project used
* \param layout The layout used
@@ -110,40 +88,14 @@ class EventsVariablesFinder {
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInEventsAndDependencies(
static void FindArgumentsInEventsAndDependencies(
std::set<gd::String>& results,
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::String& parameterType,
const gd::String& objectName = "");
/**
* Construct a list of the value of the arguments for parameters of type @
* parameterType. It doesn't search in events dependencies.
*
* \param platform The platform of the project
* \param project The project used
* \param layout The layout used
* \param events The events to be analyzed
* \param parameterType The parameters type to be analyzed
* \param objectName If not empty, parameters will be taken into account
* only if the last object parameter is filled with
* this value.
*
* \return A std::set filled with the values used for all parameters of the
* specified type
*/
static std::set<gd::String> FindArgumentsInEvents(
const gd::Platform& platform,
const gd::Project& project,
const gd::Layout& layout,
const gd::EventsList& events,
const gd::String& parameterType,
const gd::String& objectName);
};
} // namespace gd

View File

@@ -0,0 +1,289 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Events/EventsIdentifiersFinder.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "catch.hpp"
namespace {
const void DeclareTimerExtension(gd::Project &project, gd::Platform &platform) {
std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
gd::BuiltinExtensionsImplementer::ImplementsTimeExtension(*(extension.get()));
gd::BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
*(extension.get()));
// Add an instruction to test expressions.
extension
->AddAction("DoSomething", "Do something", "This does something",
"Do something please", "", "", "")
.AddParameter("expression", "Parameter 1 (a number)");
platform.AddExtension(extension);
project.AddPlatform(platform);
}
const gd::StandardEvent UseSceneTimer(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ResetTimer");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression("scene"));
instruction.SetParameter(1, gd::Expression("\"" + name + "\""));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseObjectTimer(const gd::String &objectName,
const gd::String &timerName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ResetObjectTimer");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(objectName));
instruction.SetParameter(1, gd::Expression("\"" + timerName + "\""));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneTimerInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(
0, gd::Expression("1 + TimerElapsedTime(\"" + name + "\")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent
UseObjectTimerInExpression(const gd::String &objectName,
const gd::String &timerName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0, gd::Expression("1 + " + objectName +
".ObjectTimerElapsedTime(\"" + timerName +
"\")"));
event.GetActions().Insert(instruction);
return event;
}
const void UseExternalEvents(gd::Layout &layout,
gd::ExternalEvents &externalEvents) {
gd::LinkEvent linkEvent;
linkEvent.SetTarget(externalEvents.GetName());
layout.GetEvents().InsertEvent(linkEvent);
}
} // namespace
TEST_CASE("EventsIdentifiersFinder (scene timers)", "[common]") {
SECTION("Can find scene timers in scenes") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneTimer("MySceneTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneTimerInExpression("MySceneTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(UseSceneTimer("MySceneTimer"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimer\"");
}
SECTION("Can find scene timers the right scene") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.GetEvents().InsertEvent(UseSceneTimer("MySceneTimerInLayout1"));
auto &layout2 = project.InsertNewLayout("Layout2", 0);
layout2.GetEvents().InsertEvent(UseSceneTimer("MySceneTimerInLayout2"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout1, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MySceneTimerInLayout1\"");
}
SECTION("Can find scene timers in the right external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &externalEvents1 =
project.InsertNewExternalEvents("ExternalEvents1", 0);
externalEvents1.GetEvents().InsertEvent(
UseSceneTimer("MySceneTimerInExternalEvents1"));
UseExternalEvents(layout1, externalEvents1);
auto &layout2 = project.InsertNewLayout("Layout2", 0);
auto &externalEvents2 =
project.InsertNewExternalEvents("ExternalEvents2", 0);
externalEvents2.GetEvents().InsertEvent(
UseSceneTimer("MySceneTimerInExternalEvents2"));
UseExternalEvents(layout2, externalEvents2);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout1, "sceneTimer");
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) ==
"\"MySceneTimerInExternalEvents1\"");
}
}
TEST_CASE("EventsIdentifiersFinder (object timers)", "[common]") {
SECTION("Can find object timers in scenes") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(UseObjectTimer("MyObject", "MyObjectTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in scene expression") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(UseObjectTimerInExpression("MyObject", "MyObjectTimer"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject", "MyObjectTimer"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer\"");
}
SECTION("Can find object timers in scenes for the right object") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
layout.GetEvents().InsertEvent(
UseObjectTimer("MyObject1", "MyObjectTimer1"));
layout.GetEvents().InsertEvent(
UseObjectTimer("MyObject2", "MyObjectTimer2"));
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object1.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer1\"");
}
SECTION("Can find object timers in external layouts for the right object") {
gd::Project project;
gd::Platform platform;
DeclareTimerExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject1", "MyObjectTimer1"));
externalEvents.GetEvents().InsertEvent(
UseObjectTimer("MyObject2", "MyObjectTimer2"));
UseExternalEvents(layout, externalEvents);
auto identifierExpressions =
gd::EventsIdentifiersFinder::FindAllIdentifierExpressions(
platform, project, layout, "objectTimer", object1.GetName());
REQUIRE(identifierExpressions.size() == 1);
REQUIRE(*(identifierExpressions.begin()) == "\"MyObjectTimer1\"");
}
}

View File

@@ -0,0 +1,360 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering common features of GDevelop Core.
*/
#include "GDCore/IDE/Events/EventsVariablesFinder.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "catch.hpp"
namespace {
const void DeclareVariableExtension(gd::Project &project,
gd::Platform &platform) {
std::shared_ptr<gd::PlatformExtension> extension =
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
gd::BuiltinExtensionsImplementer::ImplementsVariablesExtension(
*(extension.get()));
gd::BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
*(extension.get()));
// Add an instruction to test expressions.
extension
->AddAction("DoSomething", "Do something", "This does something",
"Do something please", "", "", "")
.AddParameter("expression", "Parameter 1 (a number)");
platform.AddExtension(extension);
project.AddPlatform(platform);
}
const gd::StandardEvent UseGlobalVariable(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarGlobal");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(name));
instruction.SetParameter(1, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneVariable(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarScene");
instruction.SetParametersCount(2);
instruction.SetParameter(0, gd::Expression(name));
instruction.SetParameter(1, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseObjectVariable(const gd::String &objectName,
const gd::String &variableName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("ModVarObjet");
instruction.SetParametersCount(3);
instruction.SetParameter(0, gd::Expression(objectName));
instruction.SetParameter(1, gd::Expression(variableName));
instruction.SetParameter(2, gd::Expression("0"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseGlobalVariableInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0,
gd::Expression("1 + GlobalVariable(" + name + ")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent UseSceneVariableInExpression(const gd::String &name) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(0, gd::Expression("1 + Variable(" + name + ")"));
event.GetActions().Insert(instruction);
return event;
}
const gd::StandardEvent
UseObjectVariableInExpression(const gd::String &objectName,
const gd::String &variableName) {
gd::StandardEvent event;
gd::Instruction instruction;
instruction.SetType("DoSomething");
instruction.SetParametersCount(1);
instruction.SetParameter(
0,
gd::Expression("1 + " + objectName + ".Variable(" + variableName + ")"));
event.GetActions().Insert(instruction);
return event;
}
const void UseExternalEvents(gd::Layout &layout,
gd::ExternalEvents &externalEvents) {
gd::LinkEvent linkEvent;
linkEvent.SetTarget(externalEvents.GetName());
layout.GetEvents().InsertEvent(linkEvent);
}
} // namespace
TEST_CASE("EventsVariablesFinder (FindAllGlobalVariables)", "[common]") {
SECTION("Can find global variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseGlobalVariable("MyGlobalVariable"));
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
SECTION("Can find global variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(
UseGlobalVariableInExpression("MyGlobalVariable"));
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
SECTION("Can find global variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseGlobalVariable("MyGlobalVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames =
gd::EventsVariablesFinder::FindAllGlobalVariables(platform, project);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyGlobalVariable");
}
}
TEST_CASE("EventsVariablesFinder (FindAllLayoutVariables)", "[common]") {
SECTION("Can find scene variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(UseSceneVariable("MySceneVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
layout.GetEvents().InsertEvent(
UseSceneVariableInExpression("MySceneVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(UseSceneVariable("MySceneVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariable");
}
SECTION("Can find scene variables the right scene") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
layout1.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInLayout1"));
auto &layout2 = project.InsertNewLayout("Layout2", 0);
layout2.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInLayout2"));
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariableInLayout1");
}
SECTION("Can find scene variables in the right external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &externalEvents1 =
project.InsertNewExternalEvents("ExternalEvents1", 0);
externalEvents1.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInExternalEvents1"));
UseExternalEvents(layout1, externalEvents1);
auto &layout2 = project.InsertNewLayout("Layout2", 0);
auto &externalEvents2 =
project.InsertNewExternalEvents("ExternalEvents2", 0);
externalEvents2.GetEvents().InsertEvent(
UseSceneVariable("MySceneVariableInExternalEvents2"));
UseExternalEvents(layout2, externalEvents2);
auto variableNames = gd::EventsVariablesFinder::FindAllLayoutVariables(
platform, project, layout1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MySceneVariableInExternalEvents1");
}
}
TEST_CASE("EventsVariablesFinder (FindAllObjectVariables)", "[common]") {
SECTION("Can find object variables in scenes") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in scene expressions") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
layout.GetEvents().InsertEvent(
UseObjectVariableInExpression("MyObject", "MyObjectVariable"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in external layouts") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object = layout.InsertNewObject(project, "", "MyObject", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject", "MyObjectVariable"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable");
}
SECTION("Can find object variables in scenes for the right object") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject1", "MyObjectVariable1"));
layout.GetEvents().InsertEvent(
UseObjectVariable("MyObject2", "MyObjectVariable2"));
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");
}
SECTION(
"Can find object variables in external layouts for the right object") {
gd::Project project;
gd::Platform platform;
DeclareVariableExtension(project, platform);
auto &layout = project.InsertNewLayout("Layout1", 0);
auto &object1 = layout.InsertNewObject(project, "", "MyObject1", 0);
auto &object2 = layout.InsertNewObject(project, "", "MyObject2", 0);
auto &externalEvents = project.InsertNewExternalEvents("ExternalEvents", 0);
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject1", "MyObjectVariable1"));
externalEvents.GetEvents().InsertEvent(
UseObjectVariable("MyObject2", "MyObjectVariable2"));
UseExternalEvents(layout, externalEvents);
auto variableNames = gd::EventsVariablesFinder::FindAllObjectVariables(
platform, project, layout, object1);
REQUIRE(variableNames.size() == 1);
REQUIRE(*(variableNames.begin()) == "MyObjectVariable1");
}
}

View File

@@ -112,7 +112,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('scenevar', _('The variable to tween'), '', false)
.addParameter('expression', _('Initial value'), '', false)
.addParameter('expression', _('Final value'), '', false)
@@ -137,7 +137,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('expression', _('Target X position'), '', false)
.addParameter('expression', _('Target Y position'), '', false)
.addParameter('layer', _('Layer'), '', true)
@@ -162,7 +162,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('expression', _('Target zoom'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter('expression', _('Duration'), '', false)
@@ -186,7 +186,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('expression', _('Target rotation'), '', false)
.addParameter('layer', _('Layer'), '', true)
.addParameter('expression', _('Duration'), '', false)
@@ -208,7 +208,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -226,7 +226,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -244,7 +244,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -262,7 +262,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -280,7 +280,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.addParameter('yesorno', _('Jump to the end'), '', false)
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
@@ -299,7 +299,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -319,7 +319,7 @@ module.exports = {
'JsPlatform/Extensions/tween_behavior32.png'
)
.addCodeOnlyParameter('currentScene', '')
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'sceneTween')
.getCodeExtraInformation()
.setIncludeFile('Extensions/TweenBehavior/shifty.js')
.addIncludeFile('Extensions/TweenBehavior/shifty_setup.js')
@@ -379,7 +379,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('objectvar', _('Object variable'), '', false)
.addParameter('expression', _('From value'), '', false)
.addParameter('expression', _('To value'), '', false)
@@ -410,7 +410,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To X'), '', false)
.addParameter('expression', _('To Y'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
@@ -440,7 +440,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To X'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -469,7 +469,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To width'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -498,7 +498,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To height'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -527,7 +527,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To Y'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -556,7 +556,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To angle (in degrees)'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -587,7 +587,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To scale X'), '', false)
.addParameter('expression', _('To scale Y'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
@@ -621,7 +621,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To scale X'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -654,7 +654,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To scale Y'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -687,7 +687,7 @@ module.exports = {
)
.addParameter('object', _('Text object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To character size'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -718,7 +718,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To opacity'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -749,7 +749,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('color', _('To color'), '', false)
.addParameter('stringWithSelector', _('Easing'), easingChoices, false)
.setDefaultValue('linear')
@@ -790,7 +790,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('expression', _('To Hue'), '', false)
.addParameter('yesorno', _('Animate Hue'), '', false)
.setDefaultValue('yes')
@@ -823,7 +823,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('exists');
@@ -839,7 +839,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('isPlaying');
@@ -855,7 +855,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('hasFinished');
@@ -871,7 +871,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('pauseTween');
@@ -887,7 +887,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.addParameter('yesorno', _('Jump to end'), '', false)
.getCodeExtraInformation()
.setFunctionName('stopTween');
@@ -904,7 +904,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('resumeTween');
@@ -920,7 +920,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('removeTween');
@@ -934,7 +934,7 @@ module.exports = {
)
.addParameter('object', _('Object'), '', false)
.addParameter('behavior', _('Behavior'), 'TweenBehavior', false)
.addParameter('string', _('Tween Identifier'), '', false)
.addParameter('identifier', _('Tween Identifier'), 'objectTween')
.getCodeExtraInformation()
.setFunctionName('getProgress');

View File

@@ -135,6 +135,14 @@ interface EventsVariablesFinder {
//Inherited from ExpressionParser2NodeWorker:
};
interface EventsIdentifiersFinder {
void EventsIdentifiersFinder();
[Const, Value] SetString STATIC_FindAllIdentifierExpressions([Const, Ref] Platform platform, [Const, Ref] Project project, [Const, Ref] Layout layout, [Const] DOMString identifierType, [Const] DOMString contextObjectName);
//Inherited from ExpressionParser2NodeWorker:
};
interface InstructionOrExpressionGroupMetadata {
void InstructionOrExpressionGroupMetadata();

View File

@@ -41,6 +41,7 @@
#include <GDCore/IDE/Events/UsedExtensionsFinder.h>
#include <GDCore/IDE/EventsFunctionTools.h>
#include <GDCore/IDE/Events/EventsVariablesFinder.h>
#include <GDCore/IDE/Events/EventsIdentifiersFinder.h>
#include <GDCore/IDE/Project/ArbitraryResourceWorker.h>
#include <GDCore/IDE/Project/ProjectResourcesAdder.h>
#include <GDCore/IDE/Project/ProjectResourcesCopier.h>
@@ -598,6 +599,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
#define STATIC_FindAllGlobalVariables FindAllGlobalVariables
#define STATIC_FindAllLayoutVariables FindAllLayoutVariables
#define STATIC_FindAllObjectVariables FindAllObjectVariables
#define STATIC_FindAllIdentifierExpressions FindAllIdentifierExpressions
#define STATIC_SearchInEvents SearchInEvents
#define STATIC_UnfoldWhenContaining UnfoldWhenContaining
#define STATIC_FoldAll FoldAll

View File

@@ -0,0 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsIdentifiersFinder {
constructor(): void;
static findAllIdentifierExpressions(platform: gdPlatform, project: gdProject, layout: gdLayout, identifierType: string, contextObjectName: string): gdSetString;
delete(): void;
ptr: number;
};

View File

@@ -51,6 +51,7 @@ declare class libGDevelop {
SetString: Class<gdSetString>;
ProjectHelper: Class<gdProjectHelper>;
EventsVariablesFinder: Class<gdEventsVariablesFinder>;
EventsIdentifiersFinder: Class<gdEventsIdentifiersFinder>;
InstructionOrExpressionGroupMetadata: Class<gdInstructionOrExpressionGroupMetadata>;
VersionWrapper: Class<gdVersionWrapper>;
Platform: Class<gdPlatform>;

View File

@@ -110,6 +110,14 @@ const getExtraInfoArray = (parameter: gdParameterMetadata) => {
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
@@ -469,6 +477,10 @@ export default class EventsFunctionParametersEditor extends React.Component<
value="objectAnimationName"
primaryText={t`Object animation (text)`}
/>
<SelectOption
value="identifier"
primaryText={t`Identifier (text)`}
/>
</SelectField>
)}
{gd.ParameterMetadata.isObject(
@@ -550,6 +562,55 @@ export default class EventsFunctionParametersEditor extends React.Component<
/>
</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

View File

@@ -313,6 +313,7 @@ export default function ExpressionAutocompletionsDisplayer({
: undefined;
return expressionAutocompletion.kind === 'Text' ||
expressionAutocompletion.kind === 'FullExpression' ||
expressionAutocompletion.kind === 'Variable' ? (
<DisplayedTextAutocompletion
key={index}

View File

@@ -361,27 +361,34 @@ export default class ExpressionField extends React.Component<Props, State> {
: 0;
const expression = this.state.validatedValue;
const {
expression: newExpression,
caretLocation: newCaretLocation,
} = insertAutocompletionInExpression(
{ expression, caretLocation },
{
completion: expressionAutocompletion.completion,
replacementStartPosition:
expressionAutocompletion.replacementStartPosition,
replacementEndPosition: expressionAutocompletion.replacementEndPosition,
addParenthesis: expressionAutocompletion.addParenthesis,
addDot: expressionAutocompletion.addDot,
addParameterSeparator: expressionAutocompletion.addParameterSeparator,
addNamespaceSeparator: expressionAutocompletion.addNamespaceSeparator,
hasVisibleParameters: expressionAutocompletion.hasVisibleParameters,
shouldConvertToString:
expressionAutocompletion.kind === 'Expression'
? expressionAutocompletion.shouldConvertToString
: null,
}
);
const { expression: newExpression, caretLocation: newCaretLocation } =
expressionAutocompletion.kind === 'FullExpression'
? {
expression: expressionAutocompletion.completion,
caretLocation: expressionAutocompletion.completion.length,
}
: insertAutocompletionInExpression(
{ expression, caretLocation },
{
completion: expressionAutocompletion.completion,
replacementStartPosition:
expressionAutocompletion.replacementStartPosition,
replacementEndPosition:
expressionAutocompletion.replacementEndPosition,
addParenthesis: expressionAutocompletion.addParenthesis,
addDot: expressionAutocompletion.addDot,
addParameterSeparator:
expressionAutocompletion.addParameterSeparator,
addNamespaceSeparator:
expressionAutocompletion.addNamespaceSeparator,
hasVisibleParameters:
expressionAutocompletion.hasVisibleParameters,
shouldConvertToString:
expressionAutocompletion.kind === 'Expression'
? expressionAutocompletion.shouldConvertToString
: null,
}
);
if (this._field) {
this._field.forceSetValue(newExpression);

View File

@@ -0,0 +1,98 @@
// @flow
import React from 'react';
import { type ParameterFieldProps } from './ParameterFieldCommons';
import GenericExpressionField from './GenericExpressionField';
import { type ExpressionAutocompletion } from '../../ExpressionAutocompletion';
import { getLastObjectParameterValue } from './ParameterMetadataTools';
const gd: libGDevelop = global.gd;
type Props = {|
...ParameterFieldProps,
|};
export const IdentifierField = (props: Props) => {
const {
project,
scope,
instructionMetadata,
instruction,
expressionMetadata,
expression,
parameterIndex,
} = props;
const { layout } = scope;
const objectName =
getLastObjectParameterValue({
instructionMetadata,
instruction,
expressionMetadata,
expression,
parameterIndex,
}) || '';
const autocompletionIdentifierNames: ExpressionAutocompletion[] = React.useMemo(
() => {
if (!parameterIndex) {
return [];
}
const parameterMetadata = instructionMetadata
? instructionMetadata.getParameter(parameterIndex)
: expressionMetadata
? expressionMetadata.getParameter(parameterIndex)
: null;
const identifierName = parameterMetadata
? parameterMetadata.getExtraInfo()
: '';
const allIdentifierExpressions =
project && layout
? gd.EventsIdentifiersFinder.findAllIdentifierExpressions(
project.getCurrentPlatform(),
project,
layout,
identifierName,
objectName
)
.toNewVectorString()
.toJSArray()
: [];
return allIdentifierExpressions.map(expression => ({
kind: 'FullExpression',
completion: expression,
}));
},
[
project,
layout,
expressionMetadata,
instructionMetadata,
parameterIndex,
// Users can change the objectName with other fields.
objectName,
]
);
const field = React.useRef<?GenericExpressionField>(null);
React.useEffect(() => {
if (field.current) {
field.current.focus();
}
}, []);
return (
<GenericExpressionField
expressionType="string"
onGetAdditionalAutocompletions={expression =>
autocompletionIdentifierNames.filter(
({ completion }) => completion.indexOf(expression) === 0
)
}
ref={field}
{...props}
/>
);
};

View File

@@ -60,6 +60,8 @@ import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
import LeaderboardIdField, {
renderInlineLeaderboardIdField,
} from './ParameterFields/LeaderboardIdField';
import { IdentifierField } from './ParameterFields/IdentifierField';
const gd: libGDevelop = global.gd;
const components = {
@@ -101,6 +103,7 @@ const components = {
functionParameterName: FunctionParameterNameField,
externalLayoutName: ExternalLayoutNameField,
leaderboardId: LeaderboardIdField,
identifier: IdentifierField,
};
const inlineRenderers: { [string]: ParameterInlineRenderer } = {
default: renderInlineDefaultField,
@@ -151,6 +154,7 @@ const userFriendlyTypeName: { [string]: MessageDescriptor } = {
objectAnimationName: t`Object animation name`,
functionParameterName: t`Parameter name`,
externalLayoutName: t`Name of the external layout`,
identifier: t`Name of the identifier`,
};
const ParameterRenderingService = {

View File

@@ -61,6 +61,10 @@ export type ExpressionAutocompletion =
| {|
...BaseExpressionAutocompletion,
kind: 'Behavior',
|}
| {|
...BaseExpressionAutocompletion,
kind: 'FullExpression',
|};
type ExpressionAutocompletionContext = {|