Files
GDevelop/Core/GDCore/Extensions/Metadata/InstructionMetadata.h

592 lines
19 KiB
C++

/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#pragma once
#include "AbstractFunctionMetadata.h"
#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include "GDCore/Events/Instruction.h"
#include "GDCore/Project/ParameterMetadataContainer.h"
#include "GDCore/String.h"
#include "ParameterMetadata.h"
#include "ParameterOptions.h"
namespace gd {
class Project;
class Layout;
class EventsCodeGenerator;
class EventsCodeGenerationContext;
class SerializerElement;
} // namespace gd
namespace gd {
/**
* \brief Describe user-friendly information about an instruction (action or
* condition), its parameters and the function name as well as other information
* for code generation.
*
* \ingroup Events
*/
class GD_CORE_API InstructionMetadata : public gd::AbstractFunctionMetadata {
public:
/**
* Construct a new instruction metadata.
*/
InstructionMetadata(const gd::String &extensionNamespace,
const gd::String &name,
const gd::String &fullname,
const gd::String &description,
const gd::String &sentence,
const gd::String &group,
const gd::String &icon,
const gd::String &smallIcon);
/**
* Construct an empty InstructionMetadata.
* \warning Don't use this - only here to fullfil std::map requirements.
*/
InstructionMetadata();
virtual ~InstructionMetadata(){};
const gd::String &GetFullName() const { return fullname; }
const gd::String &GetDescription() const { return description; }
const gd::String &GetSentence() const { return sentence; }
const gd::String &GetGroup() const { return group; }
ParameterMetadata &GetParameter(size_t i) { return parameters.GetParameter(i); }
const ParameterMetadata &GetParameter(size_t i) const {
return parameters.GetParameter(i);
}
size_t GetParametersCount() const { return parameters.GetParametersCount(); }
const ParameterMetadataContainer &GetParameters() const {
return parameters;
}
const gd::String &GetIconFilename() const { return iconFilename; }
const gd::String &GetSmallIconFilename() const { return smallIconFilename; }
bool CanHaveSubInstructions() const { return canHaveSubInstructions; }
/**
* Get the help path of the instruction, relative to the GDevelop
* documentation root.
*/
const gd::String &GetHelpPath() const { return helpPath; }
/**
* Set the help path of the instruction, relative to the GDevelop
* documentation root.
*/
InstructionMetadata &SetHelpPath(const gd::String &path) {
helpPath = path;
return *this;
}
/**
* Check if the instruction is private - it can't be used outside of the
* object/ behavior that it is attached too.
*/
bool IsPrivate() const { return isPrivate; }
/**
* Set that the instruction is private - it can't be used outside of the
* object/ behavior that it is attached too.
*/
InstructionMetadata &SetPrivate() override {
isPrivate = true;
return *this;
}
/**
* Check if the instruction can be used in layouts or external events.
*/
bool IsRelevantForLayoutEvents() const {
return relevantContext == "Any" || relevantContext == "Layout";
}
/**
* Check if the instruction can be used in function events.
*/
bool IsRelevantForFunctionEvents() const {
return relevantContext == "Any" || relevantContext == "Function";
}
/**
* Check if the instruction can be used in asynchronous function events.
*/
bool IsRelevantForAsynchronousFunctionEvents() const {
return relevantContext == "Any" || relevantContext == "Function" ||
relevantContext == "AsynchronousFunction";
}
/**
* Check if the instruction can be used in custom object events.
*/
bool IsRelevantForCustomObjectEvents() const {
return relevantContext == "Any" || relevantContext == "Object";
}
/**
* Set that the instruction can be used in layouts or external events.
*/
InstructionMetadata &SetRelevantForLayoutEventsOnly() override {
relevantContext = "Layout";
return *this;
}
/**
* Set that the instruction can be used in function events.
*/
InstructionMetadata &SetRelevantForFunctionEventsOnly() override {
relevantContext = "Function";
return *this;
}
/**
* Set that the instruction can be used in asynchronous function events.
*/
InstructionMetadata &SetRelevantForAsynchronousFunctionEventsOnly() override {
relevantContext = "AsynchronousFunction";
return *this;
}
/**
* Set that the instruction can be used in custom object events.
*/
InstructionMetadata &SetRelevantForCustomObjectEventsOnly() override {
relevantContext = "Object";
return *this;
}
/**
* Check if the instruction is asynchronous - it will be running in the
* background, executing the instructions following it before the frame after
* it resolved.
*/
bool IsAsync() const {
return !codeExtraInformation.asyncFunctionCallName.empty();
}
/**
* Check if the instruction asynchronicity is optional. If it is, it can
* either be used synchronously or asynchronously, with one function for each.
*/
bool IsOptionallyAsync() const {
return IsAsync() && !codeExtraInformation.functionCallName.empty();
}
/**
* Notify that the instruction can have sub instructions.
*/
InstructionMetadata &SetCanHaveSubInstructions() {
canHaveSubInstructions = true;
return *this;
}
/**
* \brief Set the instruction to be hidden in the IDE.
*
* Used mainly when an instruction is deprecated.
*/
InstructionMetadata &SetHidden() override {
hidden = true;
return *this;
}
/**
* \brief Set the group of the instruction in the IDE.
*/
InstructionMetadata &SetGroup(const gd::String &str) {
group = str;
return *this;
}
/**
* \brief Return true if the instruction must be hidden in the IDE.
*/
bool IsHidden() const { return hidden; }
/**
* \brief Add a parameter to the instruction metadata.
*
* \param type One of the type handled by GDevelop. This
* will also determine the type of the argument used when calling the function
* in the generated code.
* \param description Description for parameter
* \param supplementaryInformation Additional information that can be used for
* rendering or logic. For example:
* - If type is "object", this argument will describe which objects are
* allowed. If this argument is empty, all objects are allowed.
* - If type is "operator", this argument will be used to display only
* pertinent operators. \param parameterIsOptional true if the parameter must
* be optional, false otherwise.
*
* \see EventsCodeGenerator::GenerateParametersCodes
*/
InstructionMetadata &AddParameter(
const gd::String &type,
const gd::String &label,
const gd::String &supplementaryInformation = "",
bool parameterIsOptional = false) override;
/**
* \brief Add a parameter not displayed in editor.
*
* \param type One of the type handled by GDevelop. This will also determine
* the type of the argument used when calling the function in the generated
* code. \param supplementaryInformation Depends on `type`. For example, when
* `type == "inlineCode"`, the content of supplementaryInformation is inserted
* in the generated code.
*
* \see EventsCodeGenerator::GenerateParametersCodes
*/
InstructionMetadata &AddCodeOnlyParameter(
const gd::String &type, const gd::String &supplementaryInformation) override;
/**
* \brief Set the default value used in editor (or if an optional parameter is
* empty during code generation) for the last added parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetDefaultValue(const gd::String &defaultValue_) override {
if (parameters.GetParametersCount() > 0) {
parameters.GetInternalVector().back()->SetDefaultValue(defaultValue_);
}
return *this;
};
/**
* \brief Set the long description shown in the editor for the last added
* parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetParameterLongDescription(
const gd::String &longDescription) override {
if (parameters.GetParametersCount() > 0) {
parameters.GetInternalVector().back()->SetLongDescription(longDescription);
}
return *this;
}
/**
* \brief Set the additional information, used for some parameters
* with special type (for example, it can contains the type of object accepted
* by the parameter), for the last added parameter.
*
* \see AddParameter
*/
InstructionMetadata &SetParameterExtraInfo(const gd::String &extraInfo) override {
if (parameters.GetParametersCount() > 0) {
parameters.GetInternalVector().back()->SetExtraInfo(extraInfo);
}
return *this;
}
/**
* \brief Add the default parameters for an instruction manipulating the
* specified type ("string", "number") with the default operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardOperatorParameters(
const gd::String &type, const ParameterOptions &options);
/**
* \brief Add the default parameters for an instruction comparing the
* specified type ("string", "number") with the default relational operators.
*
* \note The type "string" can be declined in several subtypes.
* \see ParameterMetadata
*/
InstructionMetadata &UseStandardRelationalOperatorParameters(
const gd::String &type, const ParameterOptions &options);
/**
* \brief Mark the instruction as an object instruction. Automatically called
* when using `AddAction`/`AddCondition` on an `ObjectMetadata`.
*/
InstructionMetadata &SetIsObjectInstruction() {
isObjectInstruction = true;
return *this;
}
/**
* \brief Mark the instruction as a behavior instruction. Automatically called
* when using `AddAction`/`AddCondition` on a `BehaviorMetadata`.
*/
InstructionMetadata &SetIsBehaviorInstruction() {
isBehaviorInstruction = true;
return *this;
}
/**
* \brief Check if the instruction is an object instruction.
*/
bool IsObjectInstruction() const { return isObjectInstruction; }
/**
* \brief Check if the instruction is a behavior instruction.
*/
bool IsBehaviorInstruction() const { return isBehaviorInstruction; }
/**
* \brief Mark this (object) instruction as requiring the specified
* capability, offered by the base object. This is useful for some objects
* that don't support this capability, so that the editor can hide the
* instruction as it does not apply to them.
*/
InstructionMetadata &SetRequiresBaseObjectCapability(
const gd::String &capability);
/**
* \brief Get the required specified capability for this (object) instruction,
* or an empty string if there is nothing specific required.
*/
const gd::String &GetRequiredBaseObjectCapability() const {
return requiredBaseObjectCapability;
}
/**
* \brief Consider that the instruction is easy for a user to understand.
*/
InstructionMetadata &MarkAsSimple() {
usageComplexity = 2;
return *this;
}
/**
* \brief Consider that the instruction is harder for a user to understand
* than a normal instruction.
*/
InstructionMetadata &MarkAsAdvanced() {
usageComplexity = 7;
return *this;
}
/**
* \brief Consider that the instruction is complex for a user to understand.
*/
InstructionMetadata &MarkAsComplex() {
usageComplexity = 9;
return *this;
}
/**
* \brief Return the usage complexity of this instruction for the user,
* from 0 (simple&easy to use) to 10 (complex to understand).
*/
int GetUsageComplexity() const { return usageComplexity; }
/**
* \brief Defines information about how generate the code for an instruction
*/
class ExtraInformation {
public:
enum AccessType { Reference, MutatorAndOrAccessor, Mutators };
ExtraInformation() : accessType(Reference), hasCustomCodeGenerator(false){};
virtual ~ExtraInformation(){};
// TODO Move these attributes to InstructionMetadata.
gd::String functionCallName;
gd::String asyncFunctionCallName;
gd::String type;
AccessType accessType;
gd::String optionalAssociatedInstruction;
std::map<gd::String, gd::String> optionalMutators;
bool hasCustomCodeGenerator;
std::function<gd::String(Instruction &instruction,
gd::EventsCodeGenerator &codeGenerator,
gd::EventsCodeGenerationContext &context)>
customCodeGenerator;
std::vector<gd::String> includeFiles;
};
ExtraInformation codeExtraInformation; ///< Information about how generate
///< code for the instruction
/**
* Set the name of the function which will be called in the generated code.
* \param functionName the name of the function to call.
*/
InstructionMetadata &SetFunctionName(const gd::String &functionName_) override {
codeExtraInformation.functionCallName = functionName_;
return *this;
}
/**
* Set the name of the function, doing asynchronous work, which will be
* called in the generated code. This function should return an asynchronous
* task (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*
* \param functionName the name of the function doing asynchronous work to
* call.
*/
InstructionMetadata &SetAsyncFunctionName(const gd::String &functionName_) {
codeExtraInformation.asyncFunctionCallName = functionName_;
return *this;
}
/**
* Return the name of the function which will be called in the generated code.
*/
const gd::String &GetFunctionName() {
return codeExtraInformation.functionCallName;
}
/**
* Return the name of the function, doing asynchronous work, which will be
* called in the generated code. This function should return an asynchronous
* task (i.e: `gdjs.AsyncTask` in the JavaScript runtime).
*/
const gd::String &GetAsyncFunctionName() {
return codeExtraInformation.asyncFunctionCallName;
}
/**
* \brief Declare if the instruction being declared is somewhat manipulating
* in a standard way.
*
* \param type "number" or "string"
*/
InstructionMetadata &SetManipulatedType(const gd::String &type_) {
codeExtraInformation.type = type_;
return *this;
}
/**
* \brief Return the type manipulated in a standard way by the instruction.
*
* \param type "number" or "string"
*/
const gd::String &GetManipulatedType() const {
return codeExtraInformation.type;
}
/**
* If InstructionMetadata::ExtraInformation::SetManipulatedType was called
* with "number" or "string", this function will tell the code generator the
* name of the getter function used to retrieve the data value.
*
* Usage example:
* \code
* obj.AddAction("String",
* _("Change the string"),
* _("Change the string of a text"),
* _("the string"),
* _("Text"),
* "CppPlatform/Extensions/text24.png",
* "CppPlatform/Extensions/text_black.png");
*
* .AddParameter("object", _("Object"), "Text", false)
* .AddParameter("operator", _("Modification operator"), "string")
* .AddParameter("string", _("String"))
* .SetFunctionName("SetString").SetManipulatedType("string").SetGetter("GetString");
*
* DECLARE_END_OBJECT_ACTION()
* \endcode
*/
InstructionMetadata &SetGetter(const gd::String &getter) {
codeExtraInformation.optionalAssociatedInstruction = getter;
codeExtraInformation.accessType = codeExtraInformation.MutatorAndOrAccessor;
return *this;
}
InstructionMetadata &SetMutators(
const std::map<gd::String, gd::String> &mutators) {
codeExtraInformation.optionalMutators = mutators;
codeExtraInformation.accessType = codeExtraInformation.Mutators;
return *this;
}
/**
* \brief Erase any existing include file and add the specified include.
* \deprecated Use `AddIncludeFile` instead as clearing the list is more
* error prone.
*/
InstructionMetadata &SetIncludeFile(const gd::String &includeFile) override {
codeExtraInformation.includeFiles.clear();
codeExtraInformation.includeFiles.push_back(includeFile);
return *this;
}
/**
* \brief Add a file to the already existing include files.
*/
InstructionMetadata &AddIncludeFile(const gd::String &includeFile) override {
if (std::find(codeExtraInformation.includeFiles.begin(), codeExtraInformation.includeFiles.end(), includeFile) ==
codeExtraInformation.includeFiles.end())
codeExtraInformation.includeFiles.push_back(includeFile);
return *this;
}
/**
* \brief Get the files that must be included to use the instruction.
*/
const std::vector<gd::String> &GetIncludeFiles() const override {
return codeExtraInformation.includeFiles;
};
InstructionMetadata &SetCustomCodeGenerator(
std::function<gd::String(Instruction &instruction,
gd::EventsCodeGenerator &codeGenerator,
gd::EventsCodeGenerationContext &context)>
codeGenerator) {
codeExtraInformation.hasCustomCodeGenerator = true;
codeExtraInformation.customCodeGenerator = codeGenerator;
return *this;
}
InstructionMetadata &RemoveCustomCodeGenerator() {
codeExtraInformation.hasCustomCodeGenerator = false;
std::function<gd::String(Instruction & instruction,
gd::EventsCodeGenerator & codeGenerator,
gd::EventsCodeGenerationContext & context)>
emptyFunction;
codeExtraInformation.customCodeGenerator = emptyFunction;
return *this;
}
bool HasCustomCodeGenerator() const { return codeExtraInformation.hasCustomCodeGenerator; }
/**
* \brief Return the structure containing the information about code
* generation for the instruction.
*
* \deprecated
*/
InstructionMetadata &GetCodeExtraInformation() { return *this; }
ParameterMetadataContainer parameters;
private:
gd::String fullname;
gd::String description;
gd::String helpPath;
gd::String sentence;
gd::String group;
gd::String iconFilename;
gd::String smallIconFilename;
bool canHaveSubInstructions;
gd::String extensionNamespace;
bool hidden;
int usageComplexity; ///< Evaluate the instruction from 0 (simple&easy to
///< use) to 10 (complex to understand)
bool isPrivate;
bool isObjectInstruction;
bool isBehaviorInstruction;
gd::String requiredBaseObjectCapability;
gd::String relevantContext;
};
} // namespace gd