/* * 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 #include #include #include #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 optionalMutators; bool hasCustomCodeGenerator; std::function customCodeGenerator; std::vector 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 &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 &GetIncludeFiles() const override { return codeExtraInformation.includeFiles; }; InstructionMetadata &SetCustomCodeGenerator( std::function codeGenerator) { codeExtraInformation.hasCustomCodeGenerator = true; codeExtraInformation.customCodeGenerator = codeGenerator; return *this; } InstructionMetadata &RemoveCustomCodeGenerator() { codeExtraInformation.hasCustomCodeGenerator = false; std::function 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