Compare commits

...

58 Commits

Author SHA1 Message Date
AlexandreS
c4e230d9ba Split text input padding into 2 separate properties (#7338)
Don't show in changelog
2025-01-23 16:12:18 +01:00
D8H
56ab2fdd05 Revert "Fix used extension check false positive from usused object or behavior events (#7331)" (#7340)
This reverts commit cb24f191fd.

Don't show in changelog
2025-01-23 14:11:13 +01:00
github-actions[bot]
872f4032ff Update translations [skip ci] (#7337)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-23 11:56:54 +01:00
D8H
428b4c21a8 Disable panel sprite tilling by default (#7335)
* This ensures the rendering is more performant by default
2025-01-23 09:43:40 +01:00
D8H
cb24f191fd Fix used extension check false positive from usused object or behavior events (#7331)
- Don't show in changelog
2025-01-22 17:17:39 +01:00
github-actions[bot]
994f6bf150 Update translations [skip ci] (#7334)
Co-authored-by: AlexandreSi <32449369+AlexandreSi@users.noreply.github.com>
2025-01-22 15:55:56 +01:00
AlexandreS
3e2f05460a Bump newIDE version (#7333) 2025-01-22 15:46:04 +01:00
github-actions[bot]
c775cde9df Update translations [skip ci] (#7318)
Co-authored-by: NeylMahfouf2608 <26115084+NeylMahfouf2608@users.noreply.github.com>
2025-01-22 15:37:51 +01:00
AlexandreS
6ab736a048 Return exact matches when searching for an instruction (#7330) 2025-01-22 15:35:12 +01:00
Neyl
f805182ce4 Improvement features for Input text box (#7308)
padding, max-length and text-align can now be configured on the text input object
new condition "Input is submitted" to check if the input has been submitted with the keyboard on all platforms
2025-01-22 14:20:29 +01:00
D8H
a14f1a187e Fix a few typo in Physics3D (#7332)
- Don't show in changelog
2025-01-22 11:35:52 +01:00
D8H
d08bf97471 Fix conflict between variable or property and parameter in variable setters (#7329)
- Don't show in changelog
2025-01-21 19:16:43 +01:00
D8H
093e15e105 Fix EventsFunctionsContainer copy constructor now copy the owner (#7327)
- Don't show in changelog
2025-01-20 14:35:37 +01:00
D8H
60e36f37b1 Remove EventsFunctionsExtension inheritance to EventsFunctionsContainer (#7326)
- developer changelog only
2025-01-20 11:29:55 +01:00
D8H
ad7ce3c725 [3D character] Fix the "is falling" condition from being true when the character is going up (#7325) 2025-01-19 01:00:43 +01:00
D8H
c6f83f4431 Fix behavior property generation and value. (#7324) 2025-01-19 01:00:05 +01:00
D8H
4a685db574 Fix parameters list not updating when the function configuration was changed (#7321) 2025-01-17 20:06:08 +01:00
D8H
f3dea010fb Fix syntax errors not showing up on mouse button and key parameters (#7320)
- Don't show in changelog
2025-01-17 20:05:29 +01:00
D8H
5025e03dff Fix 3D physics characters to keep rotation around X and Y intact (#7319) 2025-01-17 15:42:46 +01:00
Florian Rival
64771c6faf Give back focus to preview, when updating a preview on the web-app 2025-01-17 13:10:09 +01:00
github-actions[bot]
d4987c4739 Update translations [skip ci] (#7314)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-17 10:48:59 +01:00
Clément Pasteau
56575e7afd Introduce new subscription dialog design for creators without a plan (#7316) 2025-01-17 10:39:04 +01:00
Florian Rival
9a0df077b3 Fix a crash when opening a project missing a custom object that was loaded in the previously opened project (#7317) 2025-01-16 23:27:32 +01:00
AlexandreS
4bf3ef8ad4 Use the folder structure when choosing an object/group in an action/condition (#7297) 2025-01-16 12:03:58 +01:00
Clément Pasteau
6242e8c97a Fix not being able to drag the app from the top bar (#7315)
Do not show in changelog
2025-01-16 12:02:47 +01:00
D8H
d6c99b27af Allow to use parameters and properties in the variable action and condition (#7124) 2025-01-15 23:28:18 +01:00
AlexandreS
7844ee102e Fix issue that was not showing cubes when they have all their faces shown with no textures (#7313) 2025-01-15 18:17:38 +01:00
github-actions[bot]
7d713d9428 Update translations [skip ci] (#7311)
Co-authored-by: ClementPasteau <4895034+ClementPasteau@users.noreply.github.com>
2025-01-15 17:36:12 +01:00
Clément Pasteau
22056d3c08 Fix adding draggable space only on title bar (#7312)
Do not show in changelog
2025-01-15 17:28:43 +01:00
D8H
2ce35a1520 Allow 3D physics characters to push each other (#7292) 2025-01-15 10:52:21 +01:00
AlexandreS
b04c15f23c Add objects installed from the asset store in selected folder (#7287) 2025-01-15 10:32:15 +01:00
D8H
2ab246696b Add autocompletion for keyboard key properties in the behavior editor (#7310) 2025-01-15 09:57:45 +01:00
Florian Rival
6b4c00c987 Add missing JS files thumbnail
Don't show in changelog
2025-01-15 09:52:56 +01:00
github-actions[bot]
9c4a190a4d Update translations [skip ci] (#7300)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-15 09:50:36 +01:00
Clément Pasteau
1070a489e7 Fix dialog overflow because of cross (#7309)
Don't show in changelog
2025-01-15 09:24:37 +01:00
Clément Pasteau
d2a8cb6727 Improve title bar on multiple platforms optimizing the space at the top of the app (#7306) 2025-01-14 17:29:55 +01:00
D8H
2638c4f593 Fix getter and setter generation for animation properties (#7307)
- Don't show in changelog
2025-01-14 14:51:27 +01:00
D8H
4be2386efe Allow to toggle between drop-down list and expression for mouse button and key parameters (#7305) 2025-01-14 13:53:10 +01:00
AlexandreS
8be099d50a Add possibility to open GDevelop in the browser on the premium course (#7304) 2025-01-13 19:12:14 +01:00
Clément Pasteau
713698d3d6 Fix wrong margins for dense design of sub card (#7303)
Do not show in changelog
2025-01-13 13:32:11 +01:00
Clément Pasteau
8b36908fd8 Merge project manager & app burger menu buttons (#7289)
* The goal of this change is to simplify the top left corner of the app, by merging the App burger menu and the Project manager button into one. Having 2 buttons was causing confusion in the flow of using the app, by mistakenly pressing the wrong button.
* The app menu buttons that are useful are now displayed at the top of the project manager drawer, on the relevant platforms: Web, Windows & mobile, because other platforms have a built-in OS menu.
2025-01-13 10:11:32 +01:00
D8H
c0722dc441 [2D Physics] Reduce the CPU footprint of static objects (#7299) 2025-01-10 16:39:20 +01:00
D8H
9c6a1bed2c [3D Physics] Reduce the CPU footprint of static objects (#7298) 2025-01-10 12:58:53 +01:00
github-actions[bot]
a8a4d14ee1 Update translations [skip ci] (#7291)
Co-authored-by: 4ian <1280130+4ian@users.noreply.github.com>
2025-01-10 10:23:16 +01:00
Florian Rival
b27abfbe5d Add text vertical alignment in quick customzation
Don't show in changelog
2025-01-10 10:07:37 +01:00
Neyl
28b585aefa Allow behavior properties to be an animation name, displayed with a selector 2025-01-09 14:21:04 +01:00
AlexandreS
0623b6acd9 Fix variable list usability (#7290)
- When renaming a variable in the instance panel:
  - the caret position is kept
  - the user can erase the whole field without the name being replaced by "Unnamed" instantly
- When opening the variables editor from a variable field, the currently selected variable is selected in the list (even if child of a variable)
2025-01-08 16:44:13 +01:00
github-actions[bot]
1a833bc388 Update translations [skip ci] (#7272)
Co-authored-by: D8H <2611977+D8H@users.noreply.github.com>
2025-01-07 11:04:19 +01:00
D8H
827f187e10 Add Pixi and Three type definitions for JS events (#7266) 2025-01-07 10:34:51 +01:00
Florian Rival
0f25b80a66 Fix margins
Don't show in changelog
2025-01-06 22:06:15 +01:00
Clément Pasteau
fbf9710cc5 Use premium colors in most places where a subscription can be purchased (#7284) 2025-01-06 20:56:49 +01:00
Clément Pasteau
3d80709029 Fix correctly exporting desktop icon when project is saved in the cloud (#7282) 2025-01-06 17:54:59 +01:00
AlexandreS
5ab9c565b0 Simplify shop navigation state to avoid useless renderings (#7283)
Only show in developer changelog
2025-01-06 17:01:41 +01:00
Florian Rival
e237fc4b21 Add experimental support for importing JS source files in extensions (#7278)
* See more about using [JavaScript on this page](https://wiki.gdevelop.io/gdevelop5/events/js-code/javascript-in-extensions/#experimental-new-option-javascript-files-in-your-project).

Only show in developer changelog
2025-01-06 11:02:28 +01:00
D8H
18892f97f3 Remove unused import and fix a typo (#7277) 2025-01-04 11:19:42 +01:00
D8H
0ec0654032 Allow to make custom objects private to an extension (#7275) 2025-01-03 19:36:38 +01:00
D8H
8e29c723e8 [3D physics character] Fix a 1-frame delay on the "is falling" condition (#7276) 2025-01-03 19:33:33 +01:00
Clément Pasteau
6acde2865a Fix opening windows in foreground on windows everywhere in app (#7274)
* Ensure electron/remote is used
2025-01-02 16:36:43 +01:00
387 changed files with 13239 additions and 6411 deletions

View File

@@ -1358,12 +1358,30 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesConta
return "getProperty" + property.GetName() + "As" + type + "()";
}
gd::String EventsCodeGenerator::GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property) {
return "getProperty" + property.GetName() + "()";
}
gd::String EventsCodeGenerator::GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode) {
return "setProperty" + property.GetName() + "(" + operandCode + ")";
}
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
return "getParameter" + parameter.GetName() + "As" + type + "()";
}
gd::String EventsCodeGenerator::GenerateParameterGetterWithoutCasting(
const gd::ParameterMetadata &parameter) {
return "getParameter" + parameter.GetName() + "()";
}
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
const gd::Layout& layout,
const gd::Platform& platform_)

View File

@@ -467,7 +467,14 @@ class GD_CORE_API EventsCodeGenerator {
*/
virtual gd::String GetCodeNamespace() { return ""; };
enum VariableScope { LAYOUT_VARIABLE = 0, PROJECT_VARIABLE, OBJECT_VARIABLE, ANY_VARIABLE };
enum VariableScope {
LAYOUT_VARIABLE = 0,
PROJECT_VARIABLE,
OBJECT_VARIABLE,
ANY_VARIABLE,
VARIABLE_OR_PROPERTY,
VARIABLE_OR_PROPERTY_OR_PARAMETER
};
/**
* Generate a single unique number for the specified instruction.
@@ -510,6 +517,11 @@ class GD_CORE_API EventsCodeGenerator {
GenerateAnyOrSceneVariableGetter(const gd::Expression &variableExpression,
EventsCodeGenerationContext &context);
virtual gd::String GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode);
protected:
virtual const gd::String GenerateRelationalOperatorCodes(
const gd::String& operatorString);
@@ -565,7 +577,8 @@ protected:
const gd::String& variableName,
const VariableScope& scope,
gd::EventsCodeGenerationContext& context,
const gd::String& objectName) {
const gd::String& objectName,
bool hasChild) {
// This code is only used as a mock.
// See the real implementation in GDJS.
if (scope == LAYOUT_VARIABLE) {
@@ -573,7 +586,9 @@ protected:
} else if (scope == PROJECT_VARIABLE) {
return "getProjectVariable(" + variableName + ")";
} else if (scope == ANY_VARIABLE) {
} else if (scope == ANY_VARIABLE || scope == VARIABLE_OR_PROPERTY ||
scope == VARIABLE_OR_PROPERTY_OR_PARAMETER) {
// TODO Split the 3 cases to make tests stronger.
return "getAnyVariable(" + variableName + ")";
}
@@ -627,11 +642,18 @@ protected:
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property);
virtual gd::String GenerateParameterGetter(
const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context);
virtual gd::String
GenerateParameterGetterWithoutCasting(const gd::ParameterMetadata &parameter);
/**
* \brief Generate the code to reference an object which is
* in an empty/null state.

View File

@@ -135,18 +135,20 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
EventsCodeGenerator::VariableScope scope =
type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
: type == "variableOrProperty"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY
: type == "variableOrPropertyOrParameter"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER
: type == "globalvar" ? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar" ? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetObjectsContainersList(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.name, scope, context, objectName);
node.name, scope, context, objectName, node.child != nullptr);
if (node.child) node.child->Visit(*this);
} else {
// The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
@@ -163,7 +165,7 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
output += codeGenerator.GenerateGetVariable(
node.name, gd::EventsCodeGenerator::ANY_VARIABLE, context, "");
node.name, gd::EventsCodeGenerator::ANY_VARIABLE, context, "", node.child != nullptr);
if (node.child) node.child->Visit(*this);
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
@@ -184,8 +186,9 @@ void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
if (!objectNameToUseForVariableAccessor.empty()) {
// Use the name of the object passed by the parent, as we need both to access an object variable.
output += codeGenerator.GenerateGetVariable(node.name,
gd::EventsCodeGenerator::OBJECT_VARIABLE, context, objectNameToUseForVariableAccessor);
output += codeGenerator.GenerateGetVariable(
node.name, gd::EventsCodeGenerator::OBJECT_VARIABLE, context,
objectNameToUseForVariableAccessor, node.child != nullptr);
// We have accessed an object variable, from now we can continue accessing the child variables
// (including using the bracket notation).
@@ -222,24 +225,27 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
output +=
codeGenerator.GenerateObject(node.identifierName, type, context);
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
EventsCodeGenerator::VariableScope scope =
EventsCodeGenerator::VariableScope scope =
type == "variable"
? gd::EventsCodeGenerator::ANY_VARIABLE
: type == "globalvar"
? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar"
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
: type == "variableOrProperty"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY
: type == "variableOrPropertyOrParameter"
? gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER
: type == "globalvar" ? gd::EventsCodeGenerator::PROJECT_VARIABLE
: type == "scenevar" ? gd::EventsCodeGenerator::LAYOUT_VARIABLE
: gd::EventsCodeGenerator::OBJECT_VARIABLE;
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
codeGenerator.GetObjectsContainersList(),
rootObjectName,
node);
output += codeGenerator.GenerateGetVariable(
node.identifierName, scope, context, objectName);
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
codeGenerator.GetPlatform(), codeGenerator.GetObjectsContainersList(),
rootObjectName, node);
output += codeGenerator.GenerateGetVariable(
node.identifierName, scope, context, objectName,
!node.childIdentifierName.empty());
if (!node.childIdentifierName.empty()) {
output +=
codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}
} else {
const auto& variablesContainersList = codeGenerator.GetProjectScopedContainers().GetVariablesContainersList();
const auto& propertiesContainersList = codeGenerator.GetProjectScopedContainers().GetPropertiesContainersList();
@@ -249,12 +255,13 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
codeGenerator.GetProjectScopedContainers().MatchIdentifierWithName<void>(node.identifierName, [&]() {
// Generate the code to access the object variable.
output += codeGenerator.GenerateGetVariable(
node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE, context, node.identifierName);
node.childIdentifierName, gd::EventsCodeGenerator::OBJECT_VARIABLE,
context, node.identifierName, !node.childIdentifierName.empty());
output += codeGenerator.GenerateVariableValueAs(type);
}, [&]() {
output += codeGenerator.GenerateGetVariable(
node.identifierName, gd::EventsCodeGenerator::ANY_VARIABLE, context,
"");
node.identifierName, gd::EventsCodeGenerator::VARIABLE_OR_PROPERTY_OR_PARAMETER, context,
"", !node.childIdentifierName.empty());
if (!node.childIdentifierName.empty()) {
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
}

View File

@@ -18,8 +18,6 @@ namespace gd {
EventsList BaseEvent::badSubEvents;
VariablesContainer BaseEvent::badLocalVariables;
std::vector<gd::String> BaseEvent::emptyDependencies;
gd::String BaseEvent::emptySourceFile;
BaseEvent::BaseEvent()
: totalTimeDuringLastSession(0),

View File

@@ -175,26 +175,6 @@ class GD_CORE_API BaseEvent {
noExpr;
return noExpr;
};
/**
* \brief Returns the dependencies on source files of the project.
* \note Default implementation returns an empty list of dependencies. This is
* fine for most events that are not related to adding custom user source
* code.
*/
virtual const std::vector<gd::String>& GetSourceFileDependencies() const {
return emptyDependencies;
};
/**
* \brief Returns the name of the source file associated with the event
* \note Default implementation returns an empty string. This is fine for most
* events that are not related to adding custom user source code.
*/
virtual const gd::String& GetAssociatedGDManagedSourceFile(
gd::Project& project) const {
return emptySourceFile;
};
///@}
/** \name Code generation
@@ -327,8 +307,6 @@ class GD_CORE_API BaseEvent {
static gd::EventsList badSubEvents;
static gd::VariablesContainer badLocalVariables;
static std::vector<gd::String> emptyDependencies;
static gd::String emptySourceFile;
};
/**

View File

@@ -35,7 +35,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("key", _("Key"));
.AddParameter("key", _("Key to check"))
.SetHidden();
extension
.AddCondition("KeyReleased",
@@ -46,33 +47,32 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("key", _("Key"));
.AddParameter("key", _("Key to check"))
.SetHidden();
extension
.AddCondition("KeyFromTextPressed",
_("Key pressed (text expression)"),
_("Check if a key, retrieved from the result of the "
"expression, is pressed"),
_("Key pressed"),
_("Check if a key is pressed"),
_("_PARAM1_ key is pressed"),
"",
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
.AddParameter("keyboardKey", _("Key to check"))
.MarkAsSimple();
extension
.AddCondition("KeyFromTextReleased",
_("Key released (text expression)"),
_("Check if a key, retrieved from the result of the "
"expression, was just released"),
_("Key released"),
_("Check if a key was just released"),
_("_PARAM1_ key is released"),
"",
"res/conditions/keyboard24.png",
"res/conditions/keyboard.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("string", _("Expression generating the key to check"))
.MarkAsAdvanced();
.AddParameter("keyboardKey", _("Key to check"))
.MarkAsSimple();
extension
.AddCondition("AnyKeyPressed",

View File

@@ -252,7 +252,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("mouse", _("Button to check"))
.MarkAsSimple();
.MarkAsSimple()
.SetHidden();
// Support for deprecated names:
extension.AddDuplicatedCondition("SourisBouton", "MouseButtonPressed")
@@ -262,49 +263,41 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsMouseExtension(
.AddCondition("MouseButtonReleased",
_("Mouse button released"),
_("Check if the specified mouse button was released."),
_("_PARAM1_ mouse button was released"),
_("Touch or _PARAM1_ mouse button is released"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("mouse", _("Button to check"))
.MarkAsSimple();
.MarkAsSimple()
.SetHidden();
extension
.AddCondition(
"MouseButtonFromTextPressed",
_("Mouse button pressed or touch held (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, is pressed."),
_("_PARAM1_ mouse button is pressed"),
_("Mouse button pressed or touch held"),
_("Check if the specified mouse button is pressed or "
"if a touch is in contact with the screen."),
_("Touch or _PARAM1_ mouse button is down"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("stringWithSelector",
_("Expression generating the mouse button to check"),
"[\"Left\", \"Right\", \"Middle\"]")
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
.AddParameter("mouseButton", _("Button to check"))
.MarkAsSimple();
extension
.AddCondition(
"MouseButtonFromTextReleased",
_("Mouse button released (text expression)"),
_("Check if a mouse button, retrieved from the result of the "
"expression, was just released."),
_("_PARAM1_ mouse button is released"),
_("Mouse button released"),
_("Check if the specified mouse button was released."),
_("Touch or _PARAM1_ mouse button is released"),
"",
"res/conditions/mouse24.png",
"res/conditions/mouse.png")
.AddCodeOnlyParameter("currentScene", "")
.AddParameter("stringWithSelector",
_("Expression generating the mouse button to check"),
"[\"Left\", \"Right\", \"Middle\"]")
.SetParameterLongDescription(
_("Possible values are Left, Right and Middle."))
.MarkAsAdvanced();
.AddParameter("mouseButton", _("Button to check"))
.MarkAsSimple();
extension
.AddExpressionAndCondition("number",

View File

@@ -33,7 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", ParameterOptions::MakeNewOptions());
@@ -45,7 +45,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", ParameterOptions::MakeNewOptions());
@@ -58,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
@@ -73,7 +73,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.UseStandardOperatorParameters("number",
ParameterOptions::MakeNewOptions());
@@ -85,7 +85,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/actions/var24.png",
"res/actions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.UseStandardOperatorParameters("string",
ParameterOptions::MakeNewOptions());
@@ -98,7 +98,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsVariablesExtension(
"",
"res/conditions/var24.png",
"res/conditions/var.png")
.AddParameter("variable", _("Variable"))
.AddParameter("variableOrProperty", _("Variable"))
.AddParameter("operator", _("Value"), "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.

View File

@@ -272,7 +272,7 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its

View File

@@ -174,6 +174,7 @@ public:
virtual const gd::String &GetFullName() const = 0;
virtual const gd::String &GetDescription() const = 0;
virtual const gd::String &GetIconFilename() const = 0;
virtual bool IsPrivate() const = 0;
/**
* \brief Return a reference to a map containing the names of the actions

View File

@@ -46,30 +46,25 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24,
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
gd::LogFatalError(
"Error: Event-based objects don't have blueprint. "
"This method should never be called.");
return nullptr;
}) {}
: name(name_),
iconFilename(icon24x24),
extensionNamespace(extensionNamespace_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
}
ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24,
CreateFunPtr createFunPtrP)
: name(name_),
iconFilename(icon24x24),
createFunPtr(createFunPtrP),
extensionNamespace(extensionNamespace_) {
SetFullName(gd::String(fullname_));
SetDescription(gd::String(description_));
CreateFunPtr createFunPtr_)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24) {
createFunPtr = createFunPtr_;
}
gd::InstructionMetadata& ObjectMetadata::AddCondition(

View File

@@ -39,6 +39,8 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
/**
* \brief Construct an object metadata, using a "blueprint" object that will
* be copied when a new object is requested.
*
* \note This is used for objects declared in JavaScript extensions.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -47,9 +49,9 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
const gd::String& icon24x24_,
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_);
/**
* \brief Construct an object metadata, without "blueprint" object
* \brief Construct an object metadata.
*
* \note This is used by events based objects.
* \note This is used by events based objects ("custom objects").
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -60,14 +62,17 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
/**
* \brief Construct an object metadata, with a function that will be called
* to instantiate a new object.
*
* \note This is used for objects declared in C++ extensions.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24_,
CreateFunPtr createFunPtrP);
ObjectMetadata() : createFunPtr(NULL) {}
CreateFunPtr createFunPtr_);
ObjectMetadata() {}
virtual ~ObjectMetadata(){};
/**
@@ -300,6 +305,22 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
*/
std::map<gd::String, gd::ExpressionMetadata>& GetAllStrExpressions() override { return strExpressionsInfos; };
/**
* Check if the behavior is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const override { return isPrivate; }
/**
* Set that the behavior is private - it can't be used outside of its
* extension.
*/
ObjectMetadata &SetPrivate() {
isPrivate = true;
return *this;
}
/**
* \brief Set the object to be hidden in the IDE.
*
@@ -344,7 +365,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
std::vector<gd::String> includeFiles;
gd::String className;
CreateFunPtr createFunPtr;
CreateFunPtr createFunPtr = nullptr;
private:
gd::String extensionNamespace;
@@ -356,6 +377,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
gd::String iconFilename;
gd::String categoryFullName;
std::set<gd::String> defaultBehaviorTypes;
bool isPrivate = false;
bool hidden = false;
bool isRenderedIn3D = false;
gd::String openFullEditorLabel;

View File

@@ -33,7 +33,8 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
const auto& parameter = parameters.GetParameter(i);
if (parameter.GetName().empty()) continue;
if (gd::ParameterMetadata::IsObject(parameter.GetType())) {
auto &valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsObject()) {
const gd::String& objectName = parameter.GetName();
const gd::String& objectType = parameter.GetExtraInfo();
allObjectNames.insert(objectName);
@@ -68,7 +69,7 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
// Search "lastObjectName" in the codebase for other place where this
// convention is enforced.
lastObjectName = objectName;
} else if (gd::ParameterMetadata::IsBehavior(parameter.GetType())) {
} else if (valueTypeMetadata.IsBehavior()) {
if (!lastObjectName.empty()) {
if (outputObjectsContainer.HasObjectNamed(lastObjectName)) {
const gd::String& behaviorName = parameter.GetName();

View File

@@ -0,0 +1,56 @@
/*
* 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 "GDCore/String.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
/**
* \brief Contains information about a source file that must be included
* when an extension is used.
*/
class GD_CORE_API SourceFileMetadata {
public:
/**
* Construct a new dependency metadata, though you probably want to call
* `AddSourceFile` on gd::PlatformExtension.
*
* \see gd::PlatformExtension
*/
SourceFileMetadata() {};
SourceFileMetadata& SetResourceName(const gd::String& resourceName_) {
resourceName = resourceName_;
return *this;
};
SourceFileMetadata& SetIncludePosition(const gd::String& includePosition_) {
includePosition = includePosition_;
return *this;
};
const gd::String& GetResourceName() const { return resourceName; };
gd::String& GetResourceName() { return resourceName; };
const gd::String& GetIncludePosition() const { return includePosition; };
void SerializeTo(SerializerElement& element) const {
element.AddChild("resourceName").SetStringValue(resourceName);
element.AddChild("includePosition").SetStringValue(includePosition);
}
void UnserializeFrom(const SerializerElement& element) {
resourceName = element.GetStringAttribute("resourceName");
includePosition = element.GetStringAttribute("includePosition", "last");
}
private:
gd::String resourceName; ///< The name of the resource in the project.
gd::String includePosition = "last"; ///< "first" or "last".
};
} // namespace gd

View File

@@ -79,6 +79,8 @@ const gd::String ValueTypeMetadata::colorValueType = "color";
const gd::String ValueTypeMetadata::choiceValueType = "stringWithSelector";
const gd::String ValueTypeMetadata::behaviorValueType = "behavior";
const gd::String ValueTypeMetadata::leaderboardIdValueType = "leaderboardId";
const gd::String ValueTypeMetadata::objectAnimationNameValueType = "objectAnimationName";
const gd::String ValueTypeMetadata::keyboardKeyValueType = "keyboardKey";
const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
const gd::String &propertyType) {
@@ -94,8 +96,12 @@ const gd::String &ValueTypeMetadata::ConvertPropertyTypeToValueType(
return behaviorValueType;
} else if (propertyType == "LeaderboardId") {
return leaderboardIdValueType;
} else if (propertyType == "ObjectAnimationName") {
return objectAnimationNameValueType;
} else if (propertyType == "KeyboardKey") {
return keyboardKeyValueType;
}
// For "String" or default
// For "String", "Resource" or default
return stringValueType;
};

View File

@@ -129,7 +129,7 @@ class GD_CORE_API ValueTypeMetadata {
}
/**
* \brief Return true if the type of the parameter is a number.
* \brief Return true if the type of the parameter is a variable.
* \note If you had a new type of parameter, also add it in the IDE (
* see EventsFunctionParametersEditor, ParameterRenderingService
* and ExpressionAutocompletion) and in the EventsCodeGenerator.
@@ -148,6 +148,19 @@ class GD_CORE_API ValueTypeMetadata {
return gd::ValueTypeMetadata::GetPrimitiveValueType(type) == "variable";
}
/**
* \brief Return true if the type of the parameter is a variable and not a
* property or a parameter.
*/
bool IsVariableOnly() const {
return
// Any variable.
name == "variable" ||
// Old, "pre-scoped" variables:
name == "objectvar" || name == "globalvar" ||
name == "scenevar";
}
/**
* \brief Return true if the type is a variable but from a specific scope
* (scene, project or object). In new code, prefer to use the more generic "variable"
@@ -212,15 +225,20 @@ class GD_CORE_API ValueTypeMetadata {
parameterType == "functionParameterName" ||
parameterType == "externalLayoutName" ||
parameterType == "leaderboardId" ||
parameterType == "keyboardKey" ||
parameterType == "mouseButton" ||
parameterType == "identifier";
} else if (type == "boolean") {
return parameterType == "yesorno" || parameterType == "trueorfalse";
} else if (type == "variable") {
return
parameterType == "variable" || // Any variable.
// Old, "pre-scoped" variables:
parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
// Any variable.
parameterType == "variable" ||
parameterType == "variableOrProperty" ||
parameterType == "variableOrPropertyOrParameter" ||
// Old, "pre-scoped" variables:
parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
} else if (type == "resource") {
return parameterType == "fontResource" ||
parameterType == "audioResource" ||
@@ -316,6 +334,8 @@ class GD_CORE_API ValueTypeMetadata {
static const gd::String choiceValueType;
static const gd::String behaviorValueType;
static const gd::String leaderboardIdValueType;
static const gd::String objectAnimationNameValueType;
static const gd::String keyboardKeyValueType;
};
} // namespace gd

View File

@@ -38,12 +38,14 @@ bool Platform::AddExtension(std::shared_ptr<gd::PlatformExtension> extension) {
extensionsLoaded.push_back(extension);
// Load all creation/destruction functions for objects provided by the
// extension
// Load all creation functions for objects provided by the
// extension.
vector<gd::String> objectsTypes = extension->GetExtensionObjectsTypes();
for (std::size_t i = 0; i < objectsTypes.size(); ++i) {
creationFunctionTable[objectsTypes[i]] =
extension->GetObjectCreationFunctionPtr(objectsTypes[i]);
CreateFunPtr createFunPtr = extension->GetObjectCreationFunctionPtr(objectsTypes[i]);
if (createFunPtr != nullptr) {
creationFunctionTable[objectsTypes[i]] = createFunPtr;
}
}
for (const auto& it :
@@ -62,7 +64,9 @@ void Platform::RemoveExtension(const gd::String& name) {
if (extension->GetName() == name) {
vector<gd::String> objectsTypes = extension->GetExtensionObjectsTypes();
for (std::size_t i = 0; i < objectsTypes.size(); ++i) {
creationFunctionTable.erase(objectsTypes[i]);
if (creationFunctionTable.find(objectsTypes[i]) != creationFunctionTable.end()) {
creationFunctionTable.erase(objectsTypes[i]);
}
}
}
}

View File

@@ -215,6 +215,11 @@ gd::DependencyMetadata& PlatformExtension::AddDependency() {
return extensionDependenciesMetadata.back();
}
gd::SourceFileMetadata& PlatformExtension::AddSourceFile() {
extensionSourceFilesMetadata.push_back(SourceFileMetadata());
return extensionSourceFilesMetadata.back();
}
gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& name,
const gd::String& fullname,
@@ -463,10 +468,22 @@ PlatformExtension::GetAllStrExpressions() {
return strExpressionsInfos;
}
const std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() const {
return extensionDependenciesMetadata;
}
std::vector<gd::DependencyMetadata>& PlatformExtension::GetAllDependencies() {
return extensionDependenciesMetadata;
}
const std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() const {
return extensionSourceFilesMetadata;
}
std::vector<gd::SourceFileMetadata>& PlatformExtension::GetAllSourceFiles() {
return extensionSourceFilesMetadata;
}
std::map<gd::String, gd::EventMetadata>& PlatformExtension::GetAllEvents() {
return eventsInfos;
}

View File

@@ -13,6 +13,7 @@
#include "GDCore/CommonTools.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/EventMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionOrExpressionGroupMetadata.h"
@@ -214,6 +215,7 @@ class GD_CORE_API PlatformExtension {
const gd::String& icon);
gd::DependencyMetadata& AddDependency();
gd::SourceFileMetadata& AddSourceFile();
/**
* \brief Declare a new object as being part of the extension.
@@ -552,6 +554,24 @@ class GD_CORE_API PlatformExtension {
*/
std::vector<gd::DependencyMetadata>& GetAllDependencies();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::DependencyMetadata>& GetAllDependencies() const;
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles();
/**
* \brief Return a reference to a vector containing the metadata of all the
* dependencies of the extension.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const;
/**
* \brief Return a reference to a map containing the names of the actions,
* related to the object type, and the metadata associated with.
@@ -687,6 +707,7 @@ class GD_CORE_API PlatformExtension {
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
std::map<gd::String, gd::ExpressionMetadata> strExpressionsInfos;
std::vector<gd::DependencyMetadata> extensionDependenciesMetadata;
std::vector<gd::SourceFileMetadata> extensionSourceFilesMetadata;
std::map<gd::String, gd::EventMetadata> eventsInfos;
std::map<gd::String, gd::PropertyDescriptor> extensionPropertiesMetadata;
std::map<gd::String, InstructionOrExpressionGroupMetadata>

View File

@@ -12,7 +12,6 @@
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
const gd::Layout& layout_)
@@ -74,16 +73,6 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
}
}
// Search for source files dependencies
std::vector<gd::String> dependencies =
events[i].GetSourceFileDependencies();
sourceFilesDependencies.insert(dependencies.begin(), dependencies.end());
const gd::String& associatedSourceFile =
events[i].GetAssociatedGDManagedSourceFile(const_cast<gd::Project&>(project));
if (!associatedSourceFile.empty())
sourceFilesDependencies.insert(associatedSourceFile);
// Analyze sub events dependencies
if (events[i].CanHaveSubEvents()) {
if (!Analyze(events[i].GetSubEvents())) return false;

View File

@@ -71,14 +71,6 @@ class GD_CORE_API DependenciesAnalyzer {
return externalEventsDependencies;
};
/**
* \brief Return the source files being dependencies of the scene or external
* events passed in the constructor.
*/
const std::set<gd::String>& GetSourceFilesDependencies() const {
return sourceFilesDependencies;
};
private:
/**
* \brief Analyze the dependencies of the events.
@@ -92,7 +84,6 @@ class GD_CORE_API DependenciesAnalyzer {
std::set<gd::String> scenesDependencies;
std::set<gd::String> externalEventsDependencies;
std::set<gd::String> sourceFilesDependencies;
std::vector<gd::String>
parentScenes; ///< Used to check for circular dependencies.
std::vector<gd::String>

View File

@@ -141,7 +141,7 @@ class GD_CORE_API ExpressionParameterReplacer
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsParameterReplacer::CanContainParameter(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariable();
isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly();
parameter->Visit(*this);
}
}
@@ -197,7 +197,7 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction,
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
parameterMetadata.GetValueTypeMetadata().IsVariable(),
parameterMetadata.GetValueTypeMetadata().IsVariableOnly(),
oldToNewPropertyNames);
node->Visit(renamer);
@@ -221,7 +221,7 @@ bool EventsParameterReplacer::DoVisitEventExpression(
if (node) {
ExpressionParameterReplacer renamer(
platform, GetProjectScopedContainers(),
metadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames);
metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames);
node->Visit(renamer);
if (renamer.HasDoneRenaming()) {

View File

@@ -168,7 +168,7 @@ class GD_CORE_API ExpressionPropertyReplacer
parameterMetadata->GetValueTypeMetadata();
if (gd::EventsPropertyReplacer::CanContainProperty(
parameterTypeMetadata)) {
isParentTypeAVariable = parameterTypeMetadata.IsVariable();
isParentTypeAVariable = parameterTypeMetadata.IsVariableOnly();
parameter->Visit(*this);
}
}
@@ -231,7 +231,7 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
if (node) {
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
parameterMetadata.GetValueTypeMetadata().IsVariable(),
parameterMetadata.GetValueTypeMetadata().IsVariableOnly(),
oldToNewPropertyNames, removedPropertyNames);
node->Visit(renamer);
@@ -257,7 +257,7 @@ bool EventsPropertyReplacer::DoVisitEventExpression(
if (node) {
ExpressionPropertyReplacer renamer(
platform, GetProjectScopedContainers(), targetPropertiesContainer,
metadata.GetValueTypeMetadata().IsVariable(), oldToNewPropertyNames,
metadata.GetValueTypeMetadata().IsVariableOnly(), oldToNewPropertyNames,
removedPropertyNames);
node->Visit(renamer);

View File

@@ -71,13 +71,18 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
.GetObjectsContainersList()
.GetObjectOrGroupVariablesContainer(lastObjectName);
}
} else if (type == "variableOrProperty") {
variablesContainer =
&GetProjectScopedContainers()
.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyName(variableName);
} else {
if (GetProjectScopedContainers().GetVariablesContainersList().Has(
variableName)) {
variablesContainer =
&GetProjectScopedContainers()
.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName);
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName);
}
}

View File

@@ -122,7 +122,7 @@ class GD_CORE_API ExpressionVariableReplacer
[&]() {
// This is a variable.
if (&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(node.name) ==
.GetVariablesContainerFromVariableOrPropertyOrParameterName(node.name) ==
&targetVariablesContainer) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:
@@ -235,7 +235,7 @@ class GD_CORE_API ExpressionVariableReplacer
[&]() {
// This is a variable.
if (&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(
.GetVariablesContainerFromVariableOrPropertyOrParameterName(
node.identifierName) == &targetVariablesContainer) {
// The node represents a variable, that can come from the target
// (because the target is in the scope), replace or remove it:

View File

@@ -1034,7 +1034,7 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName)
.GetSourceType());
completions.push_back(description);
@@ -1086,7 +1086,7 @@ class GD_CORE_API ExpressionCompletionFinder
description.SetVariableType(variable.GetType());
description.SetVariableScope(
projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(variableName)
.GetVariablesContainerFromVariableOrPropertyOrParameterName(variableName)
.GetSourceType());
completions.push_back(description);

View File

@@ -460,10 +460,12 @@ const gd::String& ExpressionValidator::TypeToString(Type type) {
case Type::NumberOrString:
return numberOrStringTypeString;
case Type::Variable:
return variableTypeString;
// This function is only used to display errors.
// Users don't care if it's legacy or not or
// if it allows properties and parameters.
case Type::VariableOrProperty:
case Type::VariableOrPropertyOrParameter:
case Type::LegacyVariable:
// This function is only used to display error.
// Users don't care if it's legacy or not.
return variableTypeString;
case Type::Object:
return objectTypeString;
@@ -493,8 +495,11 @@ ExpressionValidator::Type ExpressionValidator::StringToType(
ExpressionValidator::variableTypeString, type)) {
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
return Type::LegacyVariable;
}
else {
} else if (type == "variableOrProperty") {
return Type::VariableOrProperty;
} else if (type == "variableOrPropertyOrParameter") {
return Type::VariableOrPropertyOrParameter;
} else {
return Type::Variable;
}
}

View File

@@ -203,10 +203,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitVariableNode(VariableNode& node) override {
ReportAnyError(node);
if (parentType == Type::Variable) {
if (parentType == Type::Variable ||
parentType == Type::VariableOrProperty ||
parentType == Type::VariableOrPropertyOrParameter) {
childType = parentType;
CheckVariableExistence(node.location, node.name);
CheckVariableExistence(node.location, node.name, node.child != nullptr);
if (node.child) {
node.child->Visit(*this);
}
@@ -216,7 +218,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
if (node.child) {
node.child->Visit(*this);
}
} else if (parentType == Type::String || parentType == Type::Number || parentType == Type::NumberOrString) {
} else if (parentType == Type::String || parentType == Type::Number ||
parentType == Type::NumberOrString) {
// The node represents a variable or an object variable in an expression waiting for its *value* to be returned.
childType = parentType;
@@ -336,11 +339,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
_("You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."),
node.location);
}
}
else if (parentType == Type::Variable) {
CheckVariableExistence(node.location, node.identifierName);
}
else if (parentType != Type::Object && parentType != Type::LegacyVariable) {
} else if (parentType == Type::Variable ||
parentType == Type::VariableOrProperty ||
parentType == Type::VariableOrPropertyOrParameter) {
CheckVariableExistence(node.location, node.identifierName, !node.childIdentifierName.empty());
} else if (parentType != Type::Object &&
parentType != Type::LegacyVariable) {
// It can't happen.
RaiseTypeError(
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
@@ -376,8 +380,19 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
childType = Type::Empty;
}
private:
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, LegacyVariable, Object, Empty};
private:
enum Type {
Unknown = 0,
Number,
String,
NumberOrString,
Variable,
LegacyVariable,
Object,
Empty,
VariableOrProperty,
VariableOrPropertyOrParameter
};
Type ValidateFunction(const gd::FunctionCallNode& function);
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
bool ValidateObjectVariableOrVariableOrProperty(
@@ -386,8 +401,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation);
void CheckVariableExistence(const ExpressionParserLocation &location, const gd::String& name) {
if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") {
void CheckVariableExistence(const ExpressionParserLocation &location,
const gd::String &name, bool hasChild) {
if (!currentParameterExtraInfo ||
*currentParameterExtraInfo != "AllowUndeclaredVariable") {
projectScopedContainers.MatchIdentifierWithName<void>(
name,
[&]() {
@@ -402,19 +419,28 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
},
[&]() {
// This is a property.
// This error won't happen unless the priority is changed.
RaiseVariableNameCollisionError(
_("This variable has the same name as a property. Consider "
"renaming one or the other."),
location, name);
if (parentType != Type::VariableOrProperty &&
parentType != Type::VariableOrPropertyOrParameter) {
RaiseVariableNameCollisionError(
_("This variable has the same name as a property. Consider "
"renaming one or the other."),
location, name);
} else if (hasChild) {
RaiseMalformedVariableParameter(
_("Properties can't have children."), location, name);
}
},
[&]() {
// This is a parameter.
// This error won't happen unless the priority is changed.
RaiseVariableNameCollisionError(
_("This variable has the same name as a parameter. Consider "
"renaming one or the other."),
location, name);
if (parentType != Type::VariableOrPropertyOrParameter) {
RaiseVariableNameCollisionError(
_("This variable has the same name as a parameter. Consider "
"renaming one or the other."),
location, name);
} else if (hasChild) {
RaiseMalformedVariableParameter(
_("Properties can't have children."), location, name);
}
},
[&]() {
// This is something else.
@@ -476,6 +502,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
message, location, false, variableName, objectName);
}
void RaiseMalformedVariableParameter(const gd::String &message,
const ExpressionParserLocation &location,
const gd::String &variableName) {
RaiseError(gd::ExpressionParserError::ErrorType::MalformedVariableParameter,
message, location, true, variableName, "");
}
void RaiseTypeError(const gd::String &message,
const ExpressionParserLocation &location,
bool isFatal = true) {

View File

@@ -215,7 +215,7 @@ class GD_CORE_API ExpressionVariablePathFinder
if (projectScopedContainers.GetVariablesContainersList().Has(identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName(identifier));
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
if (childIdentifier) {
childVariableNames.push_back(*childIdentifier);
@@ -223,12 +223,28 @@ class GD_CORE_API ExpressionVariablePathFinder
}
},
[&]() {
// Ignore properties here.
// There is no support for "children" of properties.
// This is a property.
if (parameterType != "objectvar" &&
projectScopedContainers.GetVariablesContainersList().Has(
identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
// There is no support for "children" of properties.
}
},
[&]() {
// Ignore parameters here.
// There is no support for "children" of parameters.
// This is a parameter.
if (parameterType != "objectvar" &&
projectScopedContainers.GetVariablesContainersList().Has(
identifier)) {
variablesContainer =
&(projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableOrPropertyOrParameterName(identifier));
variableName = identifier;
// There is no support for "children" of parameters.
}
},
[&]() {
// Ignore unrecognised identifiers here.

View File

@@ -13,6 +13,18 @@
namespace gd {
void UsedExtensionsResult::AddUsedExtension(const gd::PlatformExtension& extension) {
usedExtensions.insert(extension.GetName());
usedSourceFiles.insert(usedSourceFiles.end(),
extension.GetAllSourceFiles().begin(),
extension.GetAllSourceFiles().end());
}
void UsedExtensionsResult::AddUsedBuiltinExtension(const gd::String& extensionName) {
usedExtensions.insert(extensionName);
}
const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& project) {
UsedExtensionsFinder worker(project);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
@@ -28,9 +40,9 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
if (metadata.GetMetadata().IsRenderedIn3D()) {
result.MarkAsHaving3DObjects();
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};
@@ -39,12 +51,12 @@ void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
void UsedExtensionsFinder::DoVisitBehavior(gd::Behavior &behavior) {
auto metadata = gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
project.GetCurrentPlatform(), behavior.GetTypeName());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
for (auto &&includeFile : metadata.GetMetadata().requiredFiles) {
result.GetUsedRequiredFiles().insert(includeFile);
result.AddUsedRequiredFiles(includeFile);
}
};
@@ -57,9 +69,9 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
project.GetCurrentPlatform(), instruction.GetType())
: gd::MetadataProvider::GetExtensionAndActionMetadata(
project.GetCurrentPlatform(), instruction.GetType());
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
gd::ParameterMetadataTools::IterateOverParameters(
@@ -77,7 +89,7 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
rootType = "number";
parameterValue.GetRootNode()->Visit(*this);
} else if (gd::ParameterMetadata::IsExpression("variable", parameterType))
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
});
return false;
@@ -110,7 +122,7 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
// Add variable extension and visit sub-expressions on variable nodes
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
auto type = gd::ExpressionTypeFinder::GetType(
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
@@ -123,9 +135,9 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
// This represents an object.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.name);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}, [&]() {
// This is a variable.
@@ -143,13 +155,13 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
void UsedExtensionsFinder::OnVisitVariableAccessorNode(
VariableAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
if (node.child) node.child->Visit(*this);
};
void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
VariableBracketAccessorNode& node) {
result.GetUsedExtensions().insert("BuiltinVariables");
result.AddUsedBuiltinExtension("BuiltinVariables");
node.expression->Visit(*this);
if (node.child) node.child->Visit(*this);
};
@@ -163,9 +175,9 @@ void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
// An object or object variable is used.
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
project.GetCurrentPlatform(), node.identifierName);
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
}
};
@@ -187,9 +199,9 @@ void UsedExtensionsFinder::OnVisitFunctionCallNode(FunctionCallNode& node) {
return;
}
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
result.AddUsedExtension(metadata.GetExtension());
for (auto&& includeFile : metadata.GetMetadata().GetIncludeFiles()) {
result.GetUsedIncludeFiles().insert(includeFile);
result.AddUsedIncludeFiles(includeFile);
}
};

View File

@@ -9,6 +9,8 @@
#include <set>
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
#include "GDCore/String.h"
@@ -44,6 +46,10 @@ public:
return usedRequiredFiles;
}
const std::vector<gd::SourceFileMetadata>& GetUsedSourceFiles() const {
return usedSourceFiles;
}
/**
* \brief Return true when at least 1 object uses the 3D renderer.
*/
@@ -51,20 +57,10 @@ public:
return has3DObjects;
}
/**
* The extensions used by the project (or part of it).
*/
std::set<gd::String> &GetUsedExtensions() { return usedExtensions; }
/**
* The include files used at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedIncludeFiles() { return usedIncludeFiles; }
/**
* The additional files required at runtime by the project (or part of it).
*/
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
void AddUsedExtension(const gd::PlatformExtension& extension);
void AddUsedBuiltinExtension(const gd::String& extensionName);
void AddUsedIncludeFiles(const gd::String& includeFile) { usedIncludeFiles.insert(includeFile); }
void AddUsedRequiredFiles(const gd::String& requiredFile) { usedRequiredFiles.insert(requiredFile); }
void MarkAsHaving3DObjects() {
has3DObjects = true;
@@ -74,6 +70,7 @@ private:
std::set<gd::String> usedExtensions;
std::set<gd::String> usedIncludeFiles;
std::set<gd::String> usedRequiredFiles;
std::vector<gd::SourceFileMetadata> usedSourceFiles;
bool has3DObjects = false;
};

View File

@@ -9,7 +9,10 @@
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
//#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ParameterMetadataContainer.h"
#include "GDCore/Project/PropertiesContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/Project/EventsFunction.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
@@ -100,4 +103,72 @@ void EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
}
}
void EventsFunctionTools::ParametersToVariablesContainer(
const ParameterMetadataContainer &parameters,
gd::VariablesContainer &outputVariablesContainer) {
if (outputVariablesContainer.GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters) {
throw std::logic_error("Tried to generate a variables container from "
"parameters with the wrong source type.");
}
outputVariablesContainer.Clear();
gd::String lastObjectName;
for (std::size_t i = 0; i < parameters.GetParametersCount(); ++i) {
const auto &parameter = parameters.GetParameter(i);
if (parameter.GetName().empty())
continue;
auto &valueTypeMetadata = parameter.GetValueTypeMetadata();
if (valueTypeMetadata.IsNumber()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetValue(0);
} else if (valueTypeMetadata.IsString()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetString("");
} else if (valueTypeMetadata.IsBoolean()) {
auto &variable = outputVariablesContainer.InsertNew(
parameter.GetName(), outputVariablesContainer.Count());
variable.SetBool(false);
}
}
}
void EventsFunctionTools::PropertiesToVariablesContainer(
const PropertiesContainer &properties,
gd::VariablesContainer &outputVariablesContainer) {
if (outputVariablesContainer.GetSourceType() !=
gd::VariablesContainer::SourceType::Properties) {
throw std::logic_error("Tried to generate a variables container from "
"properties with the wrong source type.");
}
outputVariablesContainer.Clear();
gd::String lastObjectName;
for (std::size_t i = 0; i < properties.GetCount(); ++i) {
const auto &property = properties.Get(i);
if (property.GetName().empty())
continue;
auto &propertyType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
if (propertyType == "number") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetValue(0);
} else if (propertyType == "string") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetString("");
} else if (propertyType == "boolean") {
auto &variable = outputVariablesContainer.InsertNew(
property.GetName(), outputVariablesContainer.Count());
variable.SetBool(false);
}
}
}
} // namespace gd

View File

@@ -12,6 +12,9 @@ namespace gd {
class Project;
class EventsFunctionsContainer;
class ObjectsContainer;
class ParameterMetadataContainer;
class PropertiesContainer;
class VariablesContainer;
class ParameterMetadata;
class EventsFunction;
class EventsBasedBehavior;
@@ -68,5 +71,13 @@ class GD_CORE_API EventsFunctionTools {
const gd::EventsBasedObject& eventsBasedObject,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& outputObjectsContainer);
static void ParametersToVariablesContainer(
const ParameterMetadataContainer &parameters,
gd::VariablesContainer &outputVariablesContainer);
static void PropertiesToVariablesContainer(
const PropertiesContainer &properties,
gd::VariablesContainer &outputVariablesContainer);
};
} // namespace gd

View File

@@ -62,6 +62,11 @@ void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
// do.
};
void ArbitraryResourceWorker::ExposeJavaScript(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
// Nothing to do by default - each child class can define here the action to
// do.
@@ -195,6 +200,10 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
ExposeSpine(resourceName);
return;
}
if (resourceType == "javascript") {
ExposeJavaScript(resourceName);
return;
}
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
return;
}

View File

@@ -96,7 +96,7 @@ public:
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);
/**
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
*/
@@ -112,6 +112,11 @@ public:
*/
virtual void ExposeVideo(gd::String &videoName);
/**
* \brief Expose a JavaScript file, which is always a reference to a "javascript" resource.
*/
virtual void ExposeJavaScript(gd::String &javaScriptName);
/**
* \brief Expose a bitmap font, which is always a reference to a "bitmapFont" resource.
*/

View File

@@ -38,6 +38,10 @@ void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) {
ExposeResourceAsFile(videoName);
}
void AssetResourcePathCleaner::ExposeJavaScript(gd::String &javaScriptResourceName) {
ExposeResourceAsFile(javaScriptResourceName);
}
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) {
ExposeResourceAsFile(bitmapFontName);
}

View File

@@ -46,6 +46,7 @@ public:
void ExposeTileset(gd::String &tilesetName) override;
void ExposeVideo(gd::String &videoName) override;
void ExposeBitmapFont(gd::String &bitmapFontName) override;
void ExposeJavaScript(gd::String &javaScriptResourceName) override;
void ExposeFile(gd::String &resource) override;
protected:

View File

@@ -73,6 +73,9 @@ public:
virtual void ExposeVideo(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeJavaScript(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
MatchResourceName(otherResourceName);
};

View File

@@ -60,6 +60,7 @@ public:
if (resourceType == "model3D") return allModel3Ds;
if (resourceType == "atlas") return allAtlases;
if (resourceType == "spine") return allSpines;
if (resourceType == "javascript") return allJavaScripts;
return emptyResources;
};
@@ -88,6 +89,9 @@ public:
virtual void ExposeVideo(gd::String& resourceName) override {
allVideos.insert(resourceName);
};
virtual void ExposeJavaScript(gd::String& resourceName) override {
allJavaScripts.insert(resourceName);
};
virtual void ExposeBitmapFont(gd::String& resourceName) override {
allBitmapFonts.insert(resourceName);
};
@@ -114,6 +118,7 @@ public:
std::set<gd::String> allModel3Ds;
std::set<gd::String> allAtlases;
std::set<gd::String> allSpines;
std::set<gd::String> allJavaScripts;
std::set<gd::String> emptyResources;
static const std::vector<gd::String> resourceTypes;

View File

@@ -59,6 +59,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeVideo(gd::String& videoResourceName) override {
RenameIfNeeded(videoResourceName);
};
virtual void ExposeJavaScript(gd::String& javaScriptResourceName) override {
RenameIfNeeded(javaScriptResourceName);
};
virtual void ExposeBitmapFont(gd::String& bitmapFontName) override {
RenameIfNeeded(bitmapFontName);
};

View File

@@ -74,6 +74,9 @@ private:
void ExposeVideo(gd::String &videoResourceName) override {
AddUsedResource(videoResourceName);
};
void ExposeJavaScript(gd::String &javaScriptResourceName) override {
AddUsedResource(javaScriptResourceName);
};
void ExposeBitmapFont(gd::String &bitmapFontName) override {
AddUsedResource(bitmapFontName);
};

View File

@@ -26,8 +26,8 @@ namespace gd {
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorker &worker) {
// See also gd::Project::ExposeResources for a method that traverses the whole
// project (this time for resources).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources).
ExposeProjectEventsWithoutExtensions(project, worker);
@@ -106,16 +106,16 @@ void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
}
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
worker.Launch(dependencyLayout.GetEvents());
}
}
void ProjectBrowserHelper::ExposeProjectEvents(
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
// See also gd::Project::ExposeResources for a method that traverse the whole
// project (this time for resources) and ExposeProjectEffects (this time for
// effects).
// See also gd::ResourceExposer::ExposeWholeProjectResources
// for a method that traverses the whole project (this time for resources)
// and ExposeProjectEffects (this time for effects).
// Add layouts events
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
@@ -147,7 +147,8 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorker &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
worker.Launch(eventsFunction->GetEvents());
}
@@ -169,13 +170,18 @@ void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
gd::ArbitraryEventsWorkerWithContext &worker) {
// Add (free) events functions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, *eventsFunction,
parameterObjectsContainer);
parameterObjectsContainer, parameterVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -207,15 +213,32 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker) {
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension,
eventsBasedBehavior,
propertyVariablesContainer,
worker);
}
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker) {
auto &behaviorEventsFunctions = eventsBasedBehavior.GetEventsFunctions();
for (auto &&eventsFunction : behaviorEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project, eventsFunctionsExtension, eventsBasedBehavior,
*eventsFunction, parameterObjectsContainers);
*eventsFunction, parameterObjectsContainers,
parameterVariablesContainer, propertyVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -235,15 +258,31 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker) {
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject,
propertyVariablesContainer, worker);
}
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker) {
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsFunctionsExtension, eventsBasedObject,
*eventsFunction, parameterObjectsContainers);
*eventsFunction, parameterObjectsContainers,
parameterVariablesContainer, propertyVariablesContainer);
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
}
@@ -287,7 +326,7 @@ void ProjectBrowserHelper::ExposeProjectFunctions(
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
worker.Launch(eventsFunctionsExtension);
worker.Launch(eventsFunctionsExtension.GetEventsFunctions());
for (auto &&eventsBasedBehavior :
eventsFunctionsExtension.GetEventsBasedBehaviors()

View File

@@ -19,6 +19,7 @@ class ArbitraryEventsFunctionsWorker;
class ArbitraryObjectsWorker;
class ArbitraryEventBasedBehaviorsWorker;
class ArbitraryBehaviorSharedDataWorker;
class VariablesContainer;
} // namespace gd
namespace gd {
@@ -127,6 +128,20 @@ public:
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* behavior.
*
* This should be the preferred way to traverse all the events of an
* event-based behavior.
*/
static void ExposeEventsBasedBehaviorEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* object.
@@ -152,6 +167,20 @@ public:
const gd::EventsBasedObject &eventsBasedObject,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all events of the event-based
* object.
*
* This should be the preferred way to traverse all the events of an
* event-based object.
*/
static void ExposeEventsBasedObjectEvents(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
gd::VariablesContainer &propertyVariablesContainer,
gd::ArbitraryEventsWorkerWithContext &worker);
/**
* \brief Call the specified worker on all ObjectContainers of the project
* (global, layouts...)

View File

@@ -63,7 +63,7 @@ void GD_CORE_API ProjectStripper::StripProjectForExport(gd::Project &project) {
eventsBasedObject.GetPropertyDescriptors().GetInternalVector().clear();
}
extension.GetEventsBasedBehaviors().Clear();
extension.ClearEventsFunctions();
extension.GetEventsFunctions().ClearEventsFunctions();
}
}

View File

@@ -245,9 +245,11 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
bool PropertyFunctionGenerator::CanGenerateGetterAndSetter(
const gd::AbstractEventsBasedEntity &eventsBasedEntity,
const gd::NamedPropertyDescriptor &property) {
auto &type = property.GetType();
if (type != "Boolean" && type != "Number" && type != "String" &&
type != "Choice" && type != "Color" && type != "LeaderboardId") {
const auto &primitiveType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
if (primitiveType != "boolean" && primitiveType != "number" &&
primitiveType != "string") {
return false;
}

View File

@@ -5,21 +5,22 @@
*/
#include "ResourceExposer.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/IDE/ProjectBrowserHelper.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Effect.h"
#include "GDCore/String.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
namespace gd {
void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeWholeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// See also gd::ProjectBrowserHelper::ExposeProjectEvents for a method that
// traverse the whole project (this time for events) and ExposeProjectEffects
// (this time for effects).
@@ -31,13 +32,11 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeProjectEvents(
project, eventWorker);
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventWorker);
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeProjectObjects(
project, objectWorker);
gd::ProjectBrowserHelper::ExposeProjectObjects(project, objectWorker);
// Expose layer effect resources
for (std::size_t layoutIndex = 0; layoutIndex < project.GetLayoutsCount();
@@ -52,28 +51,36 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
}
// Expose loading screen background image if present
auto& loadingScreen = project.GetLoadingScreen();
auto &loadingScreen = project.GetLoadingScreen();
if (loadingScreen.GetBackgroundImageResourceName() != "")
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
// Expose extension source files
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
ExposeExtensionResources(eventsFunctionsExtension, worker);
}
}
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
void ResourceExposer::ExposeProjectResources(
gd::Project &project, gd::ArbitraryResourceWorker &worker) {
// Expose global objects configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
objectWorker.Launch(project.GetObjects());
}
void ResourceExposer::ExposeLayoutResources(
gd::Project &project, gd::Layout &layout,
gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker) {
// Expose object configuration resources
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
@@ -87,15 +94,15 @@ void ResourceExposer::ExposeLayoutResources(
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
effectIndex++) {
auto &effect = effects.GetEffect(effectIndex);
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
effect, worker);
gd::ResourceExposer::ExposeEffectResources(
project.GetCurrentPlatform(), effect, worker);
}
}
// Expose event resources
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
eventWorker);
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
project, layout, eventWorker);
// Exposed extension event resources
// Note that using resources in extensions is very unlikely and probably not
@@ -103,12 +110,14 @@ void ResourceExposer::ExposeLayoutResources(
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
e++) {
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
project, eventsFunctionsExtension, eventWorker);
}
}
void ResourceExposer::ExposeEffectResources(
gd::Platform &platform, gd::Effect &effect,
gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker) {
auto &effectMetadata =
MetadataProvider::GetEffectMetadata(platform, effect.GetEffectType());
@@ -127,11 +136,20 @@ void ResourceExposer::ExposeEffectResources(
worker.ExposeResourceWithType(resourceType,
potentiallyUpdatedResourceName);
if (potentiallyUpdatedResourceName != resourceName) {
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
effect.SetStringParameter(propertyName,
potentiallyUpdatedResourceName);
}
}
}
}
}
} // namespace gd
void ResourceExposer::ExposeExtensionResources(
gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker) {
for (auto &sourceFile : extension.GetAllSourceFiles()) {
worker.ExposeJavaScript(sourceFile.GetResourceName());
}
}
} // namespace gd

View File

@@ -9,9 +9,10 @@ namespace gd {
class Platform;
class Project;
class ArbitraryResourceWorker;
class EventsFunctionsExtension;
class Effect;
class Layout;
} // namespace gd
} // namespace gd
namespace gd {
@@ -19,7 +20,7 @@ namespace gd {
* \brief
*/
class GD_CORE_API ResourceExposer {
public:
public:
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources, sometimes update their filename or any other work or resources.
@@ -34,7 +35,7 @@ public:
/**
* @brief Expose only the resources used globally on a project.
*
*
* It doesn't include resources used in layouts.
*/
static void ExposeProjectResources(gd::Project &project,
@@ -42,17 +43,25 @@ public:
/**
* @brief Expose the resources used in a given layout.
*
*
* It doesn't include resources used globally.
*/
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
static void ExposeLayoutResources(gd::Project &project,
gd::Layout &layout,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in a given effect.
*/
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
static void ExposeEffectResources(gd::Platform &platform,
gd::Effect &effect,
gd::ArbitraryResourceWorker &worker);
/**
* @brief Expose the resources used in an extension.
*/
static void ExposeExtensionResources(gd::EventsFunctionsExtension &extension,
gd::ArbitraryResourceWorker &worker);
};
} // namespace gd
} // namespace gd

View File

@@ -600,7 +600,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
// instructions after they are renamed.
// Free expressions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
if (eventsFunction->IsExpression()) {
renameEventsFunction(*eventsFunction);
}
@@ -617,7 +618,8 @@ void WholeProjectRefactorer::RenameEventsFunctionsExtension(
}
// Free instructions
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&eventsFunction :
eventsFunctionsExtension.GetEventsFunctions().GetInternalVector()) {
if (eventsFunction->IsAction() || eventsFunction->IsCondition()) {
renameEventsFunction(*eventsFunction);
}
@@ -697,11 +699,12 @@ void WholeProjectRefactorer::RenameEventsFunction(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &oldFunctionName, const gd::String &newFunctionName) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(oldFunctionName))
const auto &eventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(oldFunctionName))
return;
const gd::EventsFunction &eventsFunction =
eventsFunctionsExtension.GetEventsFunction(oldFunctionName);
eventsFunctions.GetEventsFunction(oldFunctionName);
const WholeProjectBrowser wholeProjectExposer;
DoRenameEventsFunction(
@@ -714,7 +717,7 @@ void WholeProjectRefactorer::RenameEventsFunction(
if (eventsFunction.GetFunctionType() ==
gd::EventsFunction::ExpressionAndCondition) {
for (auto &&otherFunction : eventsFunctionsExtension.GetInternalVector()) {
for (auto &&otherFunction : eventsFunctions.GetInternalVector()) {
if (otherFunction->GetFunctionType() ==
gd::EventsFunction::ActionWithOperator &&
otherFunction->GetGetterName() == oldFunctionName) {
@@ -862,16 +865,34 @@ void WholeProjectRefactorer::RenameParameter(
}
}
void WholeProjectRefactorer::ChangeParameterType(
gd::Project &project, gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &parameterName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(parameterName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
eventsVariableInstructionTypeSwitcher.Launch(eventsFunction.GetEvents(),
projectScopedContainers);
}
void WholeProjectRefactorer::MoveEventsFunctionParameter(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::String &functionName, std::size_t oldIndex,
std::size_t newIndex) {
if (!eventsFunctionsExtension.HasEventsFunctionNamed(functionName))
const auto &eventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (!eventsFunctions.HasEventsFunctionNamed(functionName))
return;
const gd::EventsFunction &eventsFunction =
eventsFunctionsExtension.GetEventsFunction(functionName);
eventsFunctions.GetEventsFunction(functionName);
const gd::String &eventsFunctionType =
gd::PlatformExtension::GetEventsFunctionFullType(
@@ -1175,6 +1196,42 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
gd::ProjectBrowserHelper::ExposeProjectEvents(project, conditionRenamer);
}
void WholeProjectRefactorer::ChangeEventsBasedBehaviorPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &propertyName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(propertyName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
gd::ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
project, eventsFunctionsExtension, eventsBasedBehavior,
propertyVariablesContainer, eventsVariableInstructionTypeSwitcher);
}
void WholeProjectRefactorer::ChangeEventsBasedObjectPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &propertyName) {
std::unordered_set<gd::String> typeChangedPropertyNames;
typeChangedPropertyNames.insert(propertyName);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
gd::EventsVariableInstructionTypeSwitcher
eventsVariableInstructionTypeSwitcher(project.GetCurrentPlatform(),
typeChangedPropertyNames,
propertyVariablesContainer);
gd::ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
project, eventsFunctionsExtension, eventsBasedObject,
propertyVariablesContainer, eventsVariableInstructionTypeSwitcher);
}
void WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
gd::Project &project, gd::Object &object, const gd::String &behaviorType,
const gd::String &behaviorName) {

View File

@@ -191,6 +191,16 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String &oldParameterName,
const gd::String &newParameterName);
/**
* \brief Refactor the function **after** a parameter has changed of type.
*/
static void
ChangeParameterType(gd::Project &project,
gd::ProjectScopedContainers &projectScopedContainers,
gd::EventsFunction &eventsFunction,
const gd::ObjectsContainer &parameterObjectsContainer,
const gd::String &parameterName);
/**
* \brief Refactor the project **before** an events function parameter
* is moved.
@@ -283,6 +293,26 @@ class GD_CORE_API WholeProjectRefactorer {
const gd::String& oldPropertyName,
const gd::String& newPropertyName);
/**
* \brief Refactor the project **after** a property of a behavior has
* changed of type.
*/
static void ChangeEventsBasedBehaviorPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::String &propertyName);
/**
* \brief Refactor the project **after** a property of an object has
* changed of type.
*/
static void ChangeEventsBasedObjectPropertyType(
gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::String &propertyName);
/**
* \brief Add a behavior to an object and add required behaviors if necessary
* to fill every behavior properties of the added behaviors.

View File

@@ -23,6 +23,9 @@ void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
element.SetAttribute("description", description);
element.SetAttribute("name", name);
element.SetAttribute("fullName", fullName);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
gd::SerializerElement& eventsFunctionsElement =
element.AddChild("eventsFunctions");
@@ -36,6 +39,7 @@ void AbstractEventsBasedEntity::UnserializeFrom(
description = element.GetStringAttribute("description");
name = element.GetStringAttribute("name");
fullName = element.GetStringAttribute("fullName");
isPrivate = element.GetBoolAttribute("private");
const gd::SerializerElement& eventsFunctionsElement =
element.GetChild("eventsFunctions");

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#define GDCORE_ABSTRACTEVENTSBASEDENTITY_H
#pragma once
#include <vector>
#include "GDCore/Project/NamedPropertyDescriptor.h"
@@ -40,6 +39,21 @@ class GD_CORE_API AbstractEventsBasedEntity {
*/
AbstractEventsBasedEntity* Clone() const { return new AbstractEventsBasedEntity(*this); };
/**
* \brief Check if the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
AbstractEventsBasedEntity& SetPrivate(bool isPrivate_) {
isPrivate = isPrivate_;
return *this;
}
/**
* \brief Get the description of the behavior or object, that is displayed in the
* editor.
@@ -151,8 +165,7 @@ class GD_CORE_API AbstractEventsBasedEntity {
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::PropertiesContainer propertyDescriptors;
gd::String extensionName;
bool isPrivate = false;
};
} // namespace gd
#endif // GDCORE_ABSTRACTEVENTSBASEDENTITY_H

View File

@@ -22,15 +22,17 @@ void CustomConfigurationHelper::InitializeContent(
gd::SerializerElement &configurationContent) {
for (auto &&property : properties.GetInternalVector()) {
auto &element = configurationContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property->GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (primitiveType == "string" || valueType == "behavior") {
element.SetStringValue(property->GetValue());
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
element.SetDoubleValue(property->GetValue().To<double>());
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
element.SetBoolValue(property->GetValue() == "true");
}
}
@@ -43,23 +45,25 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
for (auto &property : properties.GetInternalVector()) {
const auto &propertyName = property->GetName();
const auto &propertyType = property->GetType();
// Copy the property
objectProperties[propertyName] = *property;
auto &newProperty = objectProperties[propertyName];
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property->GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (configurationContent.HasChild(propertyName)) {
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
if (primitiveType == "string" || valueType == "behavior") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetStringValue());
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
newProperty.SetValue(gd::String::From(
configurationContent.GetChild(propertyName).GetDoubleValue()));
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
newProperty.SetValue(
configurationContent.GetChild(propertyName).GetBoolValue()
? "true"
@@ -85,15 +89,16 @@ bool CustomConfigurationHelper::UpdateProperty(
const auto &property = properties.Get(propertyName);
auto &element = configurationContent.AddChild(propertyName);
const gd::String &propertyType = property.GetType();
if (propertyType == "String" || propertyType == "Choice" ||
propertyType == "Color" || propertyType == "Behavior" ||
propertyType == "Resource" || propertyType == "LeaderboardId") {
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(property.GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (primitiveType == "string" || valueType == "behavior") {
element.SetStringValue(newValue);
} else if (propertyType == "Number") {
} else if (primitiveType == "number") {
element.SetDoubleValue(newValue.To<double>());
} else if (propertyType == "Boolean") {
} else if (primitiveType == "boolean") {
element.SetBoolValue(newValue == "1");
}

View File

@@ -21,9 +21,6 @@ EventsBasedBehavior::EventsBasedBehavior()
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
AbstractEventsBasedEntity::SerializeTo(element);
element.SetAttribute("objectType", objectType);
if (isPrivate) {
element.SetBoolAttribute("private", isPrivate);
}
sharedPropertyDescriptors.SerializeElementsTo(
"propertyDescriptor", element.AddChild("sharedPropertyDescriptors"));
if (quickCustomizationVisibility != QuickCustomization::Visibility::Default) {
@@ -39,7 +36,6 @@ void EventsBasedBehavior::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
AbstractEventsBasedEntity::UnserializeFrom(project, element);
objectType = element.GetStringAttribute("objectType");
isPrivate = element.GetBoolAttribute("private");
sharedPropertyDescriptors.UnserializeElementsFrom(
"propertyDescriptor", element.GetChild("sharedPropertyDescriptors"));
if (element.HasChild("quickCustomizationVisibility")) {

View File

@@ -3,8 +3,7 @@
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_EVENTSBASEDBEHAVIOR_H
#define GDCORE_EVENTSBASEDBEHAVIOR_H
#pragma once
#include <vector>
#include "GDCore/Project/AbstractEventsBasedEntity.h"
@@ -75,17 +74,11 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
}
/**
* \brief Check if the behavior is private - it can't be used outside of its
* \brief Set that the behavior or object is private - it can't be used outside of its
* extension.
*/
bool IsPrivate() const { return isPrivate; }
/**
* \brief Set that the behavior is private - it can't be used outside of its
* extension.
*/
EventsBasedBehavior& SetPrivate(bool _isPrivate) {
isPrivate = _isPrivate;
EventsBasedBehavior& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
@@ -149,11 +142,8 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
private:
gd::String objectType;
bool isPrivate = false;
gd::PropertiesContainer sharedPropertyDescriptors;
QuickCustomization::Visibility quickCustomizationVisibility;
};
} // namespace gd
#endif // GDCORE_EVENTSBASEDBEHAVIOR_H

View File

@@ -72,6 +72,15 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity {
return *this;
}
/**
* \brief Set that the object is private - it can't be used outside of its
* extension.
*/
EventsBasedObject& SetPrivate(bool isPrivate) {
AbstractEventsBasedEntity::SetPrivate(isPrivate);
return *this;
}
/**
* \brief Declare a usage of the 3D renderer.
*/

View File

@@ -33,6 +33,20 @@ public:
EventsFunctionsContainer(FunctionOwner source_) : owner(source_) {}
EventsFunctionsContainer(const EventsFunctionsContainer &other)
: owner(other.owner) {
Init(other);
}
EventsFunctionsContainer &operator=(const EventsFunctionsContainer &other) {
if (this != &other) {
owner = other.owner;
Init(other);
}
return *this;
}
/**
* \brief Get the source of the function container.
*

View File

@@ -15,14 +15,13 @@
namespace gd {
EventsFunctionsExtension::EventsFunctionsExtension() :
gd::EventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension),
eventsFunctionsContainer(gd::EventsFunctionsContainer::FunctionOwner::Extension),
globalVariables(gd::VariablesContainer::SourceType::ExtensionGlobal),
sceneVariables(gd::VariablesContainer::SourceType::ExtensionScene) {}
EventsFunctionsExtension::EventsFunctionsExtension(
const EventsFunctionsExtension& other) :
gd::EventsFunctionsContainer(
eventsFunctionsContainer(
gd::EventsFunctionsContainer::FunctionOwner::Extension) {
Init(other);
}
@@ -48,7 +47,7 @@ void EventsFunctionsExtension::Init(const gd::EventsFunctionsExtension& other) {
previewIconUrl = other.previewIconUrl;
iconUrl = other.iconUrl;
helpPath = other.helpPath;
EventsFunctionsContainer::Init(other);
eventsFunctionsContainer = other.eventsFunctionsContainer;
eventsBasedBehaviors = other.eventsBasedBehaviors;
eventsBasedObjects = other.eventsBasedObjects;
globalVariables = other.GetGlobalVariables();
@@ -87,10 +86,18 @@ void EventsFunctionsExtension::SerializeTo(SerializerElement& element) const {
for (auto& dependency : dependencies)
SerializeDependencyTo(dependency, dependenciesElement.AddChild(""));
if (!sourceFiles.empty()) {
auto& sourceFilesElement = element.AddChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (auto& sourceFile : sourceFiles)
sourceFile.SerializeTo(sourceFilesElement.AddChild(""));
}
GetGlobalVariables().SerializeTo(element.AddChild("globalVariables"));
GetSceneVariables().SerializeTo(element.AddChild("sceneVariables"));
SerializeEventsFunctionsTo(element.AddChild("eventsFunctions"));
eventsFunctionsContainer.SerializeEventsFunctionsTo(
element.AddChild("eventsFunctions"));
eventsBasedBehaviors.SerializeElementsTo(
"eventsBasedBehavior", element.AddChild("eventsBasedBehaviors"));
eventsBasedObjects.SerializeElementsTo(
@@ -159,6 +166,17 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
dependencies.push_back(
UnserializeDependencyFrom(dependenciesElement.GetChild(i)));
sourceFiles.clear();
if (element.HasChild("sourceFiles")) {
const auto& sourceFilesElement = element.GetChild("sourceFiles");
sourceFilesElement.ConsiderAsArray();
for (size_t i = 0; i < sourceFilesElement.GetChildrenCount(); ++i) {
SourceFileMetadata sourceFile;
sourceFile.UnserializeFrom(sourceFilesElement.GetChild(i));
sourceFiles.push_back(sourceFile);
}
}
globalVariables.UnserializeFrom(element.GetChild("globalVariables"));
sceneVariables.UnserializeFrom(element.GetChild("sceneVariables"));
@@ -187,7 +205,8 @@ void EventsFunctionsExtension::UnserializeExtensionDeclarationFrom(
void EventsFunctionsExtension::UnserializeExtensionImplementationFrom(
gd::Project& project,
const SerializerElement& element) {
UnserializeEventsFunctionsFrom(project, element.GetChild("eventsFunctions"));
eventsFunctionsContainer.UnserializeEventsFunctionsFrom(
project, element.GetChild("eventsFunctions"));
eventsBasedBehaviors.UnserializeElementsFrom(
"eventsBasedBehavior", project, element.GetChild("eventsBasedBehaviors"));

View File

@@ -8,6 +8,7 @@
#include <vector>
#include "GDCore/Extensions/Metadata/DependencyMetadata.h"
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/EventsFunctionsContainer.h"
@@ -35,7 +36,7 @@ namespace gd {
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
class GD_CORE_API EventsFunctionsExtension {
public:
EventsFunctionsExtension();
EventsFunctionsExtension(const EventsFunctionsExtension&);
@@ -180,6 +181,21 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
return originIdentifier;
}
/**
* \brief Return a reference to the functions of the events based behavior or object.
*/
EventsFunctionsContainer& GetEventsFunctions() {
return eventsFunctionsContainer;
}
/**
* \brief Return a const reference to the functions of the events based
* behavior or object.
*/
const EventsFunctionsContainer& GetEventsFunctions() const {
return eventsFunctionsContainer;
}
/** \name Dependencies
*/
///@{
@@ -289,6 +305,42 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
const gd::String& eventsFunctionName);
///@}
/** \name Source files
*/
///@{
/**
* \brief Adds a new source file.
*/
gd::SourceFileMetadata& AddSourceFile() {
gd::SourceFileMetadata sourceFile;
sourceFiles.push_back(sourceFile);
return sourceFiles.back();
};
/**
* \brief Removes a source file.
*/
void RemoveSourceFileAt(size_t index) {
sourceFiles.erase(sourceFiles.begin() + index);
};
/**
* \brief Returns the list of source files.
*/
std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() {
return sourceFiles;
};
/**
* \brief Returns the list of source files.
*/
const std::vector<gd::SourceFileMetadata>& GetAllSourceFiles() const {
return sourceFiles;
};
///@}
private:
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
@@ -336,7 +388,9 @@ class GD_CORE_API EventsFunctionsExtension : public EventsFunctionsContainer {
gd::SerializableWithNameList<EventsBasedBehavior> eventsBasedBehaviors;
gd::SerializableWithNameList<EventsBasedObject> eventsBasedObjects;
std::vector<gd::DependencyMetadata> dependencies;
std::vector<gd::SourceFileMetadata> sourceFiles;
gd::EventsFunctionsContainer eventsFunctionsContainer;
gd::VariablesContainer globalVariables;
gd::VariablesContainer sceneVariables;
};

View File

@@ -137,6 +137,15 @@ void ObjectFolderOrObject::RemoveRecursivelyObjectNamed(
}
};
void ObjectFolderOrObject::Clear() {
if (IsFolder()) {
for (auto& it : children) {
it->Clear();
}
children.clear();
}
};
bool ObjectFolderOrObject::IsADescendantOf(
const ObjectFolderOrObject& otherObjectFolderOrObject) {
if (parent == nullptr) return false;

View File

@@ -134,6 +134,10 @@ class GD_CORE_API ObjectFolderOrObject {
* the instance children and recursively does it for every folder children.
*/
void RemoveRecursivelyObjectNamed(const gd::String& name);
/**
* \brief Clears all children
*/
void Clear();
/**
* \brief Inserts an instance representing the given object at the given

View File

@@ -7,12 +7,12 @@
#include <algorithm>
#include "GDCore/Tools/PolymorphicClone.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectFolderOrObject.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/PolymorphicClone.h"
namespace gd {
@@ -28,8 +28,7 @@ ObjectsContainer::ObjectsContainer(const ObjectsContainer& other) {
Init(other);
}
ObjectsContainer& ObjectsContainer::operator=(
const ObjectsContainer& other) {
ObjectsContainer& ObjectsContainer::operator=(const ObjectsContainer& other) {
if (this != &other) Init(other);
return *this;
@@ -69,7 +68,7 @@ void ObjectsContainer::AddMissingObjectsInRootFolder() {
void ObjectsContainer::UnserializeObjectsFrom(
gd::Project& project, const SerializerElement& element) {
initialObjects.clear();
Clear();
element.ConsiderAsArrayOf("object", "Objet");
for (std::size_t i = 0; i < element.GetChildrenCount(); ++i) {
const SerializerElement& objectElement = element.GetChild(i);
@@ -184,6 +183,11 @@ void ObjectsContainer::RemoveObject(const gd::String& name) {
initialObjects.erase(objectIt);
}
void ObjectsContainer::Clear() {
rootFolder->Clear();
initialObjects.clear();
}
void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
gd::ObjectFolderOrObject& objectFolderOrObject,
gd::ObjectsContainer& newContainer,

View File

@@ -166,6 +166,11 @@ class GD_CORE_API ObjectsContainer {
gd::ObjectFolderOrObject& newParentFolder,
std::size_t newPosition);
/**
* \brief Clear all groups of the container.
*/
void Clear();
/**
* Provide a raw access to the vector containing the objects
*/

View File

@@ -30,7 +30,6 @@
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/String.h"
@@ -67,7 +66,6 @@ Project::Project()
isAntialisingEnabledOnMobile(false),
projectUuid(""),
useDeprecatedZeroAsDefaultZOrder(false),
useExternalSourceFiles(false),
isPlayableWithKeyboard(false),
isPlayableWithGamepad(false),
isPlayableWithMobile(false),
@@ -742,9 +740,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
loadingScreen.UnserializeFrom(propElement.GetChild("loadingScreen"));
watermark.UnserializeFrom(propElement.GetChild("watermark"));
useExternalSourceFiles =
propElement.GetBoolAttribute("useExternalSourceFiles");
authorIds.clear();
auto& authorIdsElement = propElement.GetChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -917,19 +912,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
InsertNewExternalLayout("", GetExternalLayoutsCount());
newExternalLayout.UnserializeFrom(externalLayoutElement);
}
externalSourceFiles.clear();
const SerializerElement& externalSourceFilesElement =
element.GetChild("externalSourceFiles", 0, "ExternalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile", "SourceFile");
for (std::size_t i = 0; i < externalSourceFilesElement.GetChildrenCount();
++i) {
const SerializerElement& sourceFileElement =
externalSourceFilesElement.GetChild(i);
gd::SourceFile& newSourceFile = InsertNewSourceFile("", "");
newSourceFile.UnserializeFrom(sourceFileElement);
}
}
void Project::UnserializeAndInsertExtensionsFrom(
@@ -1109,7 +1091,6 @@ void Project::SerializeTo(SerializerElement& element) const {
propElement.AddChild("platformSpecificAssets"));
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
watermark.SerializeTo(propElement.AddChild("watermark"));
propElement.SetAttribute("useExternalSourceFiles", useExternalSourceFiles);
auto& authorIdsElement = propElement.AddChild("authorIds");
authorIdsElement.ConsiderAsArray();
@@ -1197,13 +1178,6 @@ void Project::SerializeTo(SerializerElement& element) const {
for (std::size_t i = 0; i < externalLayouts.size(); ++i)
externalLayouts[i]->SerializeTo(
externalLayoutsElement.AddChild("externalLayout"));
SerializerElement& externalSourceFilesElement =
element.AddChild("externalSourceFiles");
externalSourceFilesElement.ConsiderAsArrayOf("sourceFile");
for (std::size_t i = 0; i < externalSourceFiles.size(); ++i)
externalSourceFiles[i]->SerializeTo(
externalSourceFilesElement.AddChild("sourceFile"));
}
bool Project::IsNameSafe(const gd::String& name) {
@@ -1244,63 +1218,6 @@ gd::String Project::GetSafeName(const gd::String& name) {
return newName;
}
bool Project::HasSourceFile(gd::String name, gd::String language) const {
vector<std::unique_ptr<SourceFile> >::const_iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return false;
return language.empty() || (*sourceFile)->GetLanguage() == language;
}
gd::SourceFile& Project::GetSourceFile(const gd::String& name) {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
const gd::SourceFile& Project::GetSourceFile(const gd::String& name) const {
return *(*find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
}));
}
void Project::RemoveSourceFile(const gd::String& name) {
std::vector<std::unique_ptr<gd::SourceFile> >::iterator sourceFile =
find_if(externalSourceFiles.begin(),
externalSourceFiles.end(),
[&name](const std::unique_ptr<SourceFile>& sourceFile) {
return sourceFile->GetFileName() == name;
});
if (sourceFile == externalSourceFiles.end()) return;
externalSourceFiles.erase(sourceFile);
}
gd::SourceFile& Project::InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position) {
if (HasSourceFile(name, language)) return GetSourceFile(name);
gd::SourceFile& newlyInsertedSourceFile = *(
*(externalSourceFiles.emplace(position < externalSourceFiles.size()
? externalSourceFiles.begin() + position
: externalSourceFiles.end(),
new SourceFile())));
newlyInsertedSourceFile.SetLanguage(language);
newlyInsertedSourceFile.SetFileName(name);
return newlyInsertedSourceFile;
}
Project::Project(const Project &other)
: objectsContainer(gd::ObjectsContainer::SourceType::Global) {
Init(other);
@@ -1367,10 +1284,6 @@ void Project::Init(const gd::Project& game) {
externalLayouts = gd::Clone(game.externalLayouts);
eventsFunctionsExtensions = gd::Clone(game.eventsFunctionsExtensions);
useExternalSourceFiles = game.useExternalSourceFiles;
externalSourceFiles = gd::Clone(game.externalSourceFiles);
variables = game.GetVariables();
projectFile = game.GetProjectFile();

View File

@@ -32,7 +32,6 @@ class Object;
class ObjectConfiguration;
class VariablesContainer;
class ArbitraryResourceWorker;
class SourceFile;
class Behavior;
class BehaviorsSharedData;
class BaseEvent;
@@ -1019,56 +1018,6 @@ class GD_CORE_API Project {
static gd::String GetSafeName(const gd::String& name);
///@}
/** \name External source files
* To manage external C++ or Javascript source files used by the game
*/
///@{
/**
* \brief Return true if the game activated the use of external source files.
*/
bool UseExternalSourceFiles() const { return useExternalSourceFiles; }
/**
* \brief Return a const reference to the vector containing all the source
* files used by the game.
*/
const std::vector<std::unique_ptr<gd::SourceFile> >& GetAllSourceFiles()
const {
return externalSourceFiles;
}
/**
* \brief Return true if the source file with the specified name is used by
* the game. \param name The filename of the source file. \param language
* Optional. If specified, check that the source file that exists is in this
* language.
*/
bool HasSourceFile(gd::String name, gd::String language = "") const;
/**
* Return a reference to the external source file with the given name.
*/
SourceFile& GetSourceFile(const gd::String& name);
/**
* Return a reference to the external source file with the given name.
*/
const SourceFile& GetSourceFile(const gd::String& name) const;
/**
* Remove the specified source file.
*/
void RemoveSourceFile(const gd::String& name);
/**
* Add a new source file the specified position in the external source files
* list.
*/
gd::SourceFile& InsertNewSourceFile(const gd::String& name,
const gd::String& language,
std::size_t position = -1);
///@}
gd::WholeProjectDiagnosticReport& GetWholeProjectDiagnosticReport() {
return wholeProjectDiagnosticReport;
}
@@ -1141,10 +1090,6 @@ class GD_CORE_API Project {
std::vector<gd::Platform*>
platforms; ///< Pointers to the platforms this project supports.
gd::String firstLayout;
bool useExternalSourceFiles =
false; ///< True if game used external source files.
std::vector<std::unique_ptr<gd::SourceFile> >
externalSourceFiles; ///< List of external source files used.
gd::String author; ///< Game author name, for publishing purpose.
std::vector<gd::String>
authorIds; ///< Game author ids, from GDevelop users DB.

View File

@@ -70,24 +70,29 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsFunctionsExtensi
ProjectScopedContainers
ProjectScopedContainers::MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer) {
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
project, eventsFunctionsExtension, eventsFunction, parameterObjectsContainer);
project, eventsFunctionsExtension.GetEventsFunctions(), eventsFunction,
parameterObjectsContainer);
ProjectScopedContainers projectScopedContainers(
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
MakeNewVariablesContainersListForFreeEventsFunction(
eventsFunctionsExtension, eventsFunction,
parameterVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
projectScopedContainers.AddParameters(
eventsFunction.GetParametersForEvents(eventsFunctionsExtension));
projectScopedContainers.AddParameters(eventsFunction.GetParametersForEvents(
eventsFunctionsExtension.GetEventsFunctions()));
return projectScopedContainers;
};
@@ -97,7 +102,9 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction
const gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior& eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
project,
@@ -109,7 +116,9 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForBehaviorEventsFunction
ObjectsContainersList::MakeNewObjectsContainersListForContainer(
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(eventsFunctionsExtension),
MakeNewVariablesContainersListForBehaviorEventsFunction(
eventsFunctionsExtension, eventsBasedBehavior, eventsFunction,
parameterVariablesContainer, propertyVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
@@ -130,7 +139,9 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForObjectEventsFunction(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer) {
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
project, eventsBasedObject, eventsFunction, parameterObjectsContainer);
@@ -140,8 +151,9 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForObjectEventsFunction(
eventsBasedObject.GetObjects(),
parameterObjectsContainer),
VariablesContainersList::
MakeNewVariablesContainersListForEventsFunctionsExtension(
eventsFunctionsExtension),
MakeNewVariablesContainersListForObjectEventsFunction(
eventsFunctionsExtension, eventsBasedObject, eventsFunction,
parameterVariablesContainer, propertyVariablesContainer),
&eventsFunctionsExtension.GetGlobalVariables(),
&eventsFunctionsExtension.GetSceneVariables(),
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
@@ -166,7 +178,7 @@ ProjectScopedContainers::MakeNewProjectScopedContainersForEventsBasedObject(
// created below.
// Search for "ProjectScopedContainers wrongly containing temporary objects containers or objects"
// in the codebase.
outputObjectsContainer.GetObjects().clear();
outputObjectsContainer.Clear();
outputObjectsContainer.GetObjectGroups().Clear();
// This object named "Object" represents the parent and is used by events.

View File

@@ -69,8 +69,9 @@ class ProjectScopedContainers {
MakeNewProjectScopedContainersForFreeEventsFunction(
const gd::Project &project,
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction& eventsFunction,
gd::ObjectsContainer& parameterObjectsContainer);
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForBehaviorEventsFunction(
@@ -78,7 +79,9 @@ class ProjectScopedContainers {
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForObjectEventsFunction(
@@ -86,7 +89,9 @@ class ProjectScopedContainers {
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::ObjectsContainer &parameterObjectsContainer);
gd::ObjectsContainer &parameterObjectsContainer,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static ProjectScopedContainers
MakeNewProjectScopedContainersForEventsBasedObject(
@@ -124,9 +129,17 @@ class ProjectScopedContainers {
std::function<ReturnType()> notFoundCallback) const {
if (objectsContainersList.HasObjectOrGroupNamed(name))
return objectCallback();
else if (variablesContainersList.Has(name))
else if (variablesContainersList.Has(name)) {
const auto &variablesContainer =
variablesContainersList.GetVariablesContainerFromVariableOrPropertyOrParameterName(name);
const auto sourceType = variablesContainer.GetSourceType();
if (sourceType == gd::VariablesContainer::SourceType::Properties) {
return propertyCallback();
} else if (sourceType == gd::VariablesContainer::SourceType::Parameters) {
return parameterCallback();
}
return variableCallback();
else if (ParameterMetadataTools::Has(parametersVectorsList, name))
} else if (ParameterMetadataTools::Has(parametersVectorsList, name))
return parameterCallback();
else if (propertiesContainersList.Has(name))
return propertyCallback();

View File

@@ -97,6 +97,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<AtlasResource>();
else if (kind == "spine")
return std::make_shared<SpineResource>();
else if (kind == "javascript")
return std::make_shared<JavaScriptResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -767,6 +769,20 @@ void AtlasResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}
void JavaScriptResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void JavaScriptResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void JavaScriptResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {

View File

@@ -547,6 +547,32 @@ class GD_CORE_API AtlasResource : public Resource {
gd::String file;
};
/**
* \brief Describe a video file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API JavaScriptResource : public Resource {
public:
JavaScriptResource() : Resource() { SetKind("javascript"); };
virtual ~JavaScriptResource(){};
virtual JavaScriptResource* Clone() const override {
return new JavaScriptResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*

View File

@@ -1,36 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#if defined(GD_IDE_ONLY)
#include "GDCore/Project/SourceFile.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Serialization/SerializerElement.h"
namespace gd {
SourceFile::SourceFile() : gdManaged(false) {
// ctor
}
SourceFile::~SourceFile() {
// dtor
}
void SourceFile::SerializeTo(SerializerElement& element) const {
element.SetAttribute("filename", filename);
element.SetAttribute("language", language);
element.SetAttribute("gdManaged", gdManaged);
}
void SourceFile::UnserializeFrom(const SerializerElement& element) {
filename = element.GetStringAttribute("filename");
language = element.GetStringAttribute("language", "C++");
gdManaged = element.GetBoolAttribute("gdManaged");
}
} // namespace gd
#endif

View File

@@ -1,92 +0,0 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef SOURCEFILE_H
#define SOURCEFILE_H
#include <ctime>
#include <memory>
#include "GDCore/String.h"
namespace gd {
class SerializerElement;
}
class BaseEvent;
namespace gd {
/**
* \brief Represents a "physical" source file.
*
* Source file can be compiled (or just integrated to the exported project)
* by platforms. Most of the time, special events are provided to use functions
* created in such files.
*/
class GD_CORE_API SourceFile {
public:
SourceFile();
virtual ~SourceFile();
/**
* \brief Return a pointer to a new SourceFile constructed from this one.
*/
SourceFile* Clone() const { return new SourceFile(*this); };
/**
* \brief Get the filename
*/
gd::String GetFileName() const { return filename; };
/**
* \brief Change the filename
*/
void SetFileName(gd::String filename_) { filename = filename_; };
/**
* \brief Serialize the source file.
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the source file.
*/
void UnserializeFrom(const SerializerElement& element);
/**
* \brief Set if the file is hidden from the user point of view and is only
* managed by GDevelop
*/
void SetGDManaged(bool gdManaged_) { gdManaged = gdManaged_; };
/**
* \brief Return true if the file is hidden from the user point of view and is
* only managed by GDevelop
*/
bool IsGDManaged() const { return gdManaged; };
/**
* \brief Change the language of the source file
*/
void SetLanguage(gd::String lang) { language = lang; }
/**
* \brief Get the language of the source file
*/
const gd::String& GetLanguage() const { return language; }
private:
gd::String filename; ///< Filename
bool gdManaged; ///< True if the source file is hidden from the user point of
///< view and is managed only by GDevelop.
gd::String language; ///< String identifying the language of this source file
///< (typically "C++ or "Javascript").
std::weak_ptr<BaseEvent>
associatedGdEvent; ///< When a source file is GD-managed, it is usually
///< created for a specific event. This member is not
///< saved: It is the event responsibility to call
///< SetAssociatedEvent.
};
} // namespace gd
#endif // SOURCEFILE_H

View File

@@ -34,7 +34,9 @@ class GD_CORE_API VariablesContainer {
Object,
Local,
ExtensionGlobal,
ExtensionScene
ExtensionScene,
Parameters,
Properties,
};
VariablesContainer();

View File

@@ -6,6 +6,7 @@
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/IDE/EventsFunctionTools.h"
namespace gd {
@@ -41,6 +42,78 @@ VariablesContainersList::MakeNewVariablesContainersListForEventsFunctionsExtensi
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForFreeEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(extension.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 3;
return variablesContainersList;
}
VariablesContainersList VariablesContainersList::
MakeNewVariablesContainersListForBehaviorEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedBehavior.GetSharedPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedBehavior.GetPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(
eventsBasedBehavior.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 5;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListForObjectEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer) {
VariablesContainersList variablesContainersList;
variablesContainersList.Push(extension.GetGlobalVariables());
variablesContainersList.Push(extension.GetSceneVariables());
gd::EventsFunctionTools::PropertiesToVariablesContainer(
eventsBasedObject.GetPropertyDescriptors(), propertyVariablesContainer);
variablesContainersList.Push(propertyVariablesContainer);
gd::EventsFunctionTools::ParametersToVariablesContainer(
eventsFunction.GetParametersForEvents(
eventsBasedObject.GetEventsFunctions()),
parameterVariablesContainer);
variablesContainersList.Push(parameterVariablesContainer);
variablesContainersList.firstLocalVariableContainerIndex = 4;
return variablesContainersList;
}
VariablesContainersList
VariablesContainersList::MakeNewVariablesContainersListPushing(
const VariablesContainersList& variablesContainersList, const gd::VariablesContainer& variablesContainer) {
@@ -74,7 +147,7 @@ const Variable& VariablesContainersList::Get(const gd::String& name) const {
}
const VariablesContainer &
VariablesContainersList::GetVariablesContainerFromVariableName(
VariablesContainersList::GetVariablesContainerFromVariableOrPropertyOrParameterName(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
@@ -84,6 +157,34 @@ VariablesContainersList::GetVariablesContainerFromVariableName(
return badVariablesContainer;
}
const VariablesContainer &VariablesContainersList::
GetVariablesContainerFromVariableOrPropertyName(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
if ((*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters &&
(*it)->Has(variableName))
return **it;
}
return badVariablesContainer;
}
const VariablesContainer &VariablesContainersList::
GetVariablesContainerFromVariableNameOnly(
const gd::String &variableName) const {
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
++it) {
if ((*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Parameters &&
(*it)->GetSourceType() !=
gd::VariablesContainer::SourceType::Properties &&
(*it)->Has(variableName))
return **it;
}
return badVariablesContainer;
}
std::size_t
VariablesContainersList::GetVariablesContainerPositionFromVariableName(
const gd::String &variableName) const {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <vector>
#include <functional>
#include <vector>
namespace gd {
class String;
@@ -9,7 +9,10 @@ class Layout;
class VariablesContainer;
class Variable;
class EventsFunctionsExtension;
} // namespace gd
class EventsBasedBehavior;
class EventsBasedObject;
class EventsFunction;
} // namespace gd
namespace gd {
@@ -24,20 +27,42 @@ namespace gd {
* \ingroup PlatformDefinition
*/
class GD_CORE_API VariablesContainersList {
public:
public:
virtual ~VariablesContainersList(){};
static VariablesContainersList
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project& project,
const gd::Layout& layout);
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project &project,
const gd::Layout &layout);
static VariablesContainersList
MakeNewVariablesContainersListForProject(const gd::Project& project);
MakeNewVariablesContainersListForProject(const gd::Project &project);
static VariablesContainersList
MakeNewVariablesContainersListForEventsFunctionsExtension(
const gd::EventsFunctionsExtension &extension);
static VariablesContainersList
MakeNewVariablesContainersListForFreeEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer);
static VariablesContainersList
MakeNewVariablesContainersListForBehaviorEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedBehavior &eventsBasedBehavior,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static VariablesContainersList
MakeNewVariablesContainersListForObjectEventsFunction(
const gd::EventsFunctionsExtension &extension,
const gd::EventsBasedObject &eventsBasedObject,
const gd::EventsFunction &eventsFunction,
gd::VariablesContainer &parameterVariablesContainer,
gd::VariablesContainer &propertyVariablesContainer);
static VariablesContainersList MakeNewVariablesContainersListPushing(
const VariablesContainersList &variablesContainersList,
const gd::VariablesContainer &variablesContainer);
@@ -50,28 +75,31 @@ class GD_CORE_API VariablesContainersList {
/**
* \brief Return true if the specified variable is in one of the containers.
*/
bool Has(const gd::String& name) const;
bool Has(const gd::String &name) const;
/**
* \brief Return a reference to the variable called \a name.
*/
const Variable& Get(const gd::String& name) const;
const Variable &Get(const gd::String &name) const;
/**
* \brief Return true if the specified variable container is present.
*/
bool HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const;
bool
HasVariablesContainer(const gd::VariablesContainer &variablesContainer) const;
// TODO: Rename GetTopMostVariablesContainer and GetBottomMostVariablesContainer
// to give a clearer access to segments of the container list.
// For instance, a project tree segment and an event tree segment.
// TODO: Rename GetTopMostVariablesContainer and
// GetBottomMostVariablesContainer to give a clearer access to segments of the
// container list. For instance, a project tree segment and an event tree
// segment.
/**
* Get the variables container at the top of the scope (so the most "global" one).
* \brief Avoid using apart when a scope must be forced.
* Get the variables container at the top of the scope (so the most "global"
* one). \brief Avoid using apart when a scope must be forced.
*/
const VariablesContainer* GetTopMostVariablesContainer() const {
if (variablesContainers.empty()) return nullptr;
const VariablesContainer *GetTopMostVariablesContainer() const {
if (variablesContainers.empty())
return nullptr;
return variablesContainers.front();
};
@@ -80,16 +108,29 @@ class GD_CORE_API VariablesContainersList {
* (so the most "local" one) excluding local variables.
* \brief Avoid using apart when a scope must be forced.
*/
const VariablesContainer* GetBottomMostVariablesContainer() const {
if (variablesContainers.empty()) return nullptr;
const VariablesContainer *GetBottomMostVariablesContainer() const {
if (variablesContainers.empty())
return nullptr;
return variablesContainers.at(firstLocalVariableContainerIndex - 1);
}
/**
* Get the variables container for a given variable or property or parameter.
*/
const VariablesContainer &
GetVariablesContainerFromVariableOrPropertyOrParameterName(const gd::String &variableName) const;
/**
* Get the variables container for a given variable or property.
*/
const VariablesContainer &
GetVariablesContainerFromVariableOrPropertyName(const gd::String &variableName) const;
/**
* Get the variables container for a given variable.
*/
const VariablesContainer &
GetVariablesContainerFromVariableName(const gd::String &variableName) const;
GetVariablesContainerFromVariableNameOnly(const gd::String &variableName) const;
/**
* Get the variables container index for a given variable.
@@ -109,43 +150,47 @@ class GD_CORE_API VariablesContainersList {
* \warning Trying to access to a not existing variable container will result
* in undefined behavior.
*/
const gd::VariablesContainer& GetVariablesContainer(std::size_t index) const {
const gd::VariablesContainer &GetVariablesContainer(std::size_t index) const {
return *variablesContainers.at(index);
}
/**
* \brief Return the number variable containers.
*/
std::size_t GetVariablesContainersCount() const { return variablesContainers.size(); }
std::size_t GetVariablesContainersCount() const {
return variablesContainers.size();
}
/**
* \brief Call the callback for each variable having a name matching the specified search.
* \brief Call the callback for each variable having a name matching the
* specified search.
*/
void ForEachVariableMatchingSearch(const gd::String& search, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
void ForEachVariableMatchingSearch(
const gd::String &search,
std::function<void(const gd::String &name, const gd::Variable &variable)>
fn) const;
/**
* \brief Push a new variables container to the context.
*/
void Push(const gd::VariablesContainer& variablesContainer) {
void Push(const gd::VariablesContainer &variablesContainer) {
variablesContainers.push_back(&variablesContainer);
};
/**
* \brief Pop a variables container from the context.
*/
void Pop() {
variablesContainers.pop_back();
};
void Pop() { variablesContainers.pop_back(); };
/** Do not use - should be private but accessible to let Emscripten create a
* temporary. */
VariablesContainersList() : firstLocalVariableContainerIndex(0){};
/** Do not use - should be private but accessible to let Emscripten create a temporary. */
VariablesContainersList(): firstLocalVariableContainerIndex(0) {};
private:
std::vector<const gd::VariablesContainer*> variablesContainers;
private:
std::vector<const gd::VariablesContainer *> variablesContainers;
std::size_t firstLocalVariableContainerIndex;
static Variable badVariable;
static VariablesContainer badVariablesContainer;
};
} // namespace gd
} // namespace gd

View File

@@ -268,8 +268,9 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
auto& function = extension.InsertNewEventsFunction("MyFreeFunction", 0);
auto &function = extension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeFunction", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithResources");
@@ -777,8 +778,9 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
ArbitraryResourceWorkerTest worker(project.GetResourcesManager());
auto& extension = project.InsertNewEventsFunctionsExtension("MyEventExtension", 0);
auto& function = extension.InsertNewEventsFunction("MyFreeFunction", 0);
auto &function = extension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeFunction", 0);
gd::StandardEvent standardEvent;
gd::Instruction instruction;
instruction.SetType("MyExtension::DoSomethingWithResources");

View File

@@ -32,7 +32,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetScenesDependencies().find("Layout2") !=
analyzer.GetScenesDependencies().end());
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 0);
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a simple external events dependency") {
@@ -55,7 +54,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a transitive scene and external events dependency") {
@@ -87,7 +85,6 @@ TEST_CASE("DependenciesAnalyzer", "[common]") {
REQUIRE(analyzer.GetExternalEventsDependencies().size() == 1);
REQUIRE(analyzer.GetExternalEventsDependencies().find("ExternalEvents1") !=
analyzer.GetExternalEventsDependencies().end());
REQUIRE(analyzer.GetSourceFilesDependencies().size() == 0);
}
SECTION("Can detect a (nested) circular dependency with scenes") {

View File

@@ -263,6 +263,46 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
extension->SetExtensionInformation(
"BuiltinVariables", "My testing extension for variables", "", "", "");
extension
->AddCondition("NumberVariable",
"Variable value",
"Compare the number value of a variable.",
"The variable _PARAM0_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"number", gd::ParameterOptions::MakeNewOptions());
extension
->AddCondition("StringVariable",
"Variable value",
"Compare the text (string) of a variable.",
"The variable _PARAM0_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.UseStandardRelationalOperatorParameters(
"string", gd::ParameterOptions::MakeNewOptions());
extension
->AddCondition(
"BooleanVariable",
"Variable value",
"Compare the boolean value of a variable.",
"The variable _PARAM0_ is _PARAM1_",
"",
"",
"")
.AddParameter("variableOrPropertyOrParameter", _("Variable"))
.AddParameter("trueorfalse", _("Check if the value is"))
.SetDefaultValue("true")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.
.AddCodeOnlyParameter("trueorfalse", "");
extension
->AddAction("SetNumberVariable",
"Change variable value",
@@ -271,7 +311,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "number")
.AddParameter("number", "Value")
.SetFunctionName("setNumberVariable");
@@ -284,7 +324,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "string")
.AddParameter("string", "Value")
.SetFunctionName("setStringVariable");
@@ -297,7 +337,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
"",
"",
"")
.AddParameter("variable", "Variable")
.AddParameter("variableOrProperty", "Variable")
.AddParameter("operator", "Operator", "boolean")
// This parameter allows to keep the operand expression
// when the editor switch between variable instructions.

View File

@@ -38,6 +38,8 @@ TEST_CASE("EventsFunctionsContainer", "[common]") {
"Function2.x");
REQUIRE(eventsFunctionContainer.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionContainer.GetOwner() ==
gd::EventsFunctionsContainer::FunctionOwner::Extension);
REQUIRE(eventsFunctionContainer2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionContainer2.GetEventsFunction(0).GetName() ==
"Function1.y");

View File

@@ -11,9 +11,10 @@
TEST_CASE("EventsFunctionsExtension", "[common]") {
SECTION("Sanity checks") {
gd::EventsFunctionsExtension eventsFunctionExtension;
eventsFunctionExtension.InsertNewEventsFunction("Function1", 0);
eventsFunctionExtension.InsertNewEventsFunction("Function2", 1);
eventsFunctionExtension.InsertNewEventsFunction("Function3", 2);
auto &freeEventsFunctions = eventsFunctionExtension.GetEventsFunctions();
freeEventsFunctions.InsertNewEventsFunction("Function1", 0);
freeEventsFunctions.InsertNewEventsFunction("Function2", 1);
freeEventsFunctions.InsertNewEventsFunction("Function3", 2);
eventsFunctionExtension.GetEventsBasedBehaviors().InsertNew("MyBehavior",
0);
eventsFunctionExtension.GetEventsBasedBehaviors().InsertNew("MyBehavior2",
@@ -22,12 +23,13 @@ TEST_CASE("EventsFunctionsExtension", "[common]") {
// Check that copy operator is working
gd::EventsFunctionsExtension eventsFunctionExtension2 =
eventsFunctionExtension;
REQUIRE(eventsFunctionExtension2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension2.GetEventsFunction(0).GetName() ==
auto &freeEventsFunctions2 = eventsFunctionExtension2.GetEventsFunctions();
REQUIRE(freeEventsFunctions2.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions2.GetEventsFunction(0).GetName() ==
"Function1");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(1).GetName() ==
"Function2");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionExtension2.GetEventsBasedBehaviors().GetCount() == 2);
REQUIRE(
@@ -39,21 +41,21 @@ TEST_CASE("EventsFunctionsExtension", "[common]") {
// Check that the copy has not somehow shared the same pointers
// to the events functions.
eventsFunctionExtension.GetEventsFunction(1).SetName("Function2.x");
eventsFunctionExtension2.GetEventsFunction(0).SetName("Function1.y");
REQUIRE(eventsFunctionExtension.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension.GetEventsFunction(0).GetName() ==
freeEventsFunctions.GetEventsFunction(1).SetName("Function2.x");
freeEventsFunctions2.GetEventsFunction(0).SetName("Function1.y");
REQUIRE(freeEventsFunctions.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions.GetEventsFunction(0).GetName() ==
"Function1");
REQUIRE(eventsFunctionExtension.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions.GetEventsFunction(1).GetName() ==
"Function2.x");
REQUIRE(eventsFunctionExtension.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions.GetEventsFunction(2).GetName() ==
"Function3");
REQUIRE(eventsFunctionExtension2.GetEventsFunctionsCount() == 3);
REQUIRE(eventsFunctionExtension2.GetEventsFunction(0).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunctionsCount() == 3);
REQUIRE(freeEventsFunctions2.GetEventsFunction(0).GetName() ==
"Function1.y");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(1).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(1).GetName() ==
"Function2");
REQUIRE(eventsFunctionExtension2.GetEventsFunction(2).GetName() ==
REQUIRE(freeEventsFunctions2.GetEventsFunction(2).GetName() ==
"Function3");
}
}

View File

@@ -2035,6 +2035,319 @@ TEST_CASE("ExpressionParser2", "[common][events]") {
}
}
SECTION("Valid property (in variableOrPropertyOrParameter parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Valid property (in variableOrProperty parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Invalid property (in variable parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
auto node = parser.ParseExpression("MyProperty");
gd::ExpressionValidator validator(
platform, projectScopedContainersWithProperties, "variable");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a property. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid property (property with child in variableOrProperty parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
{
auto node = parser.ParseExpression("MyProperty.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Invalid property (property with child in variableOrPropertyOrParameter parameter)") {
{
gd::PropertiesContainer propertiesContainer(
gd::EventsFunctionsContainer::Extension);
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithProperties.AddPropertiesContainer(
propertiesContainer);
propertiesContainer.InsertNew("MyProperty");
{
auto node = parser.ParseExpression("MyProperty.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyProperty[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithProperties,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Valid parameter (in variableOrPropertyOrParameter parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 0);
}
}
SECTION("Invalid parameter (in variableOrProperty parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrProperty");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a parameter. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid parameter (in variable parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
auto node = parser.ParseExpression("MyParameter");
gd::ExpressionValidator validator(
platform, projectScopedContainersWithParameters, "variable");
node->Visit(validator);
REQUIRE(validator.GetAllErrors().size() == 1);
REQUIRE(validator.GetAllErrors()[0]->GetMessage() ==
"This variable has the same name as a parameter. Consider "
"renaming one or the other.");
}
}
SECTION("Invalid parameter (parameter with child in variableOrPropertyOrParameter parameter)") {
{
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter", 0).SetType("number");
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
projectScopedContainersWithParameters.AddParameters(parameters);
{
auto node = parser.ParseExpression("MyParameter.MyChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter.MyChild.MyGrandChild");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter[\"MyChild\"]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
{
auto node = parser.ParseExpression("MyParameter[0]");
gd::ExpressionValidator validator(platform,
projectScopedContainersWithParameters,
"variableOrPropertyOrParameter");
node->Visit(validator);
REQUIRE(validator.GetFatalErrors().size() == 1);
REQUIRE(validator.GetFatalErrors()[0]->GetMessage() ==
"Properties can't have children.");
}
}
}
SECTION("Valid parameter") {
gd::ParameterMetadataContainer parameters;
parameters.InsertNewParameter("MyParameter1", 0).SetType("number");

View File

@@ -1,30 +0,0 @@
/*
* 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/CommonTools.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "catch.hpp"
TEST_CASE("SourceFile", "[common]") {
SECTION("Basics") {
gd::Project project;
project.InsertNewSourceFile("test.cpp", "C++");
project.InsertNewSourceFile("test.js", "Javascript");
REQUIRE(project.HasSourceFile("test.cpp", "C++") == true);
REQUIRE(project.HasSourceFile("test.cpp", "JS") == false);
REQUIRE(project.HasSourceFile("test.cpp") == true);
gd::SourceFile& cppSourceFile = project.GetSourceFile("test.cpp");
REQUIRE(cppSourceFile.GetFileName() == "test.cpp");
REQUIRE(cppSourceFile.GetLanguage() == "C++");
project.RemoveSourceFile("test.cpp");
REQUIRE(project.HasSourceFile("test.cpp") == false);
REQUIRE(project.HasSourceFile("test.js") == true);
}
}

View File

@@ -1540,7 +1540,7 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
auto projectScopedContainers =
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, scene);
REQUIRE(&projectScopedContainers.GetVariablesContainersList()
.GetVariablesContainerFromVariableName("MyVariable") == &scene.GetVariables());
.GetVariablesContainerFromVariableOrPropertyOrParameterName("MyVariable") == &scene.GetVariables());
// Do the changes and launch the refactoring.
scene.GetVariables().ResetPersistentUuid();
@@ -1722,7 +1722,8 @@ TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
auto &extension = project.InsertNewEventsFunctionsExtension("Extension", 0);
extension.GetSceneVariables().InsertNew("MySceneVariable").SetValue(123);
auto &function = extension.InsertNewEventsFunction("MyFunction", 0);
auto &function =
extension.GetEventsFunctions().InsertNewEventsFunction("MyFunction", 0);
gd::StandardEvent &event =
dynamic_cast<gd::StandardEvent &>(function.GetEvents().InsertNewEvent(
project, "BuiltinCommonInstructions::Standard"));

View File

@@ -136,6 +136,74 @@ CreateInstructionWithVariableParameter(gd::Project &project,
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateNumberVariableSetterAction(gd::Project &project,
gd::EventsList &events,
const gd::String &variableName,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("SetNumberVariable");
instruction.SetParametersCount(3);
instruction.SetParameter(0, variableName);
instruction.SetParameter(1, "=");
instruction.SetParameter(2, expression);
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateNumberVariableGetterCondition(gd::Project &project,
gd::EventsList &events,
const gd::String &variableName,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("NumberVariable");
instruction.SetParametersCount(3);
instruction.SetParameter(0, variableName);
instruction.SetParameter(1, "=");
instruction.SetParameter(2, expression);
return event.GetConditions().Insert(instruction);
}
const gd::Instruction &
CreateStringVariableSetterAction(gd::Project &project,
gd::EventsList &events,
const gd::String &variableName,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("SetStringVariable");
instruction.SetParametersCount(3);
instruction.SetParameter(0, variableName);
instruction.SetParameter(1, "=");
instruction.SetParameter(2, expression);
return event.GetActions().Insert(instruction);
}
const gd::Instruction &
CreateStringVariableGetterCondition(gd::Project &project,
gd::EventsList &events,
const gd::String &variableName,
const gd::String &expression) {
gd::StandardEvent &event = dynamic_cast<gd::StandardEvent &>(
events.InsertNewEvent(project, "BuiltinCommonInstructions::Standard"));
gd::Instruction instruction;
instruction.SetType("StringVariable");
instruction.SetParametersCount(3);
instruction.SetParameter(0, variableName);
instruction.SetParameter(1, "=");
instruction.SetParameter(2, expression);
return event.GetConditions().Insert(instruction);
}
enum TestEvent {
FreeFunctionAction,
FreeFunctionWithExpression,
@@ -201,8 +269,9 @@ const std::vector<const gd::EventsList *> GetEventsListsNotAssociatedToScene(gd:
.GetEventsFunctions()
.GetEventsFunction("MyBehaviorEventsFunction")
.GetEvents();
auto &freeFunctionEvents =
eventsExtension.GetEventsFunction("MyOtherEventsFunction").GetEvents();
auto &freeFunctionEvents = eventsExtension.GetEventsFunctions()
.GetEventsFunction("MyOtherEventsFunction")
.GetEvents();
eventLists.push_back(&objectFunctionEvents);
eventLists.push_back(&behaviorFunctionEvents);
eventLists.push_back(&freeFunctionEvents);
@@ -1129,8 +1198,9 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
// Add (free) functions and a (free) expression
{
auto &freeEventsFunctions = eventsExtension.GetEventsFunctions();
auto &action =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
freeEventsFunctions.InsertNewEventsFunction("MyEventsFunction", 0);
action.GetParameters()
.InsertNewParameter("currentScene", 0)
.SetType("")
@@ -1145,21 +1215,21 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior");
auto &expression =
eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpression", 1)
freeEventsFunctions.InsertNewEventsFunction("MyEventsFunctionExpression", 1)
.SetFunctionType(gd::EventsFunction::Expression);
expression.GetParameters()
.InsertNewParameter("currentScene", 0)
.SetType("")
.SetCodeOnly(true);
auto &freeExpressionAndCondition = eventsExtension.InsertNewEventsFunction("MyEventsFunctionExpressionAndCondition", 2)
auto &freeExpressionAndCondition = freeEventsFunctions.InsertNewEventsFunction("MyEventsFunctionExpressionAndCondition", 2)
.SetFunctionType(gd::EventsFunction::ExpressionAndCondition);
freeExpressionAndCondition.GetParameters().InsertNewParameter("Value1", 0)
.SetType("expression");
freeExpressionAndCondition.GetParameters().InsertNewParameter("Value2", 1)
.SetType("expression");
eventsExtension.InsertNewEventsFunction("MyEventsFunctionActionWithOperator", 2)
freeEventsFunctions.InsertNewEventsFunction("MyEventsFunctionActionWithOperator", 2)
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
.SetGetterName("MyEventsFunctionExpressionAndCondition");
}
@@ -1168,8 +1238,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
// object and behavior.
{
// Add functions, and parameters that should be there by convention.
auto &action =
eventsExtension.InsertNewEventsFunction("MyOtherEventsFunction", 0);
auto &action = eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyOtherEventsFunction", 0);
// Define the same objects as in the layout to be consistent with events.
action.GetParameters()
.InsertNewParameter("ObjectWithMyBehavior", 0)
@@ -1231,7 +1301,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.GetEventsFunctions()
.GetEventsFunction("MyBehaviorEventsFunction")
.GetEvents());
SetupEvents(eventsExtension.GetEventsFunction("MyOtherEventsFunction")
SetupEvents(eventsExtension.GetEventsFunctions()
.GetEventsFunction("MyOtherEventsFunction")
.GetEvents());
}
@@ -1583,7 +1654,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
@@ -1594,10 +1666,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
// Trigger the refactoring before the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
@@ -1618,15 +1692,18 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunction("MyOtherEventsFunction");
eventsExtension.GetEventsFunctions().GetEventsFunction(
"MyOtherEventsFunction");
// Create the objects container for the events function
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
// Simulate a variable in ObjectWithMyBehavior, even if this is not
// supported by the editor.
@@ -1868,7 +1945,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
@@ -2066,7 +2144,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Add the function used by the instruction that is checked in this test.
// When the function doesn't exist the destination extension, the
// instruction keeps pointing to the old extension.
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
destinationExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyEventsFunction", 0);
auto &duplicatedBehavior =
destinationExtension.GetEventsBasedBehaviors().InsertNew(
@@ -2108,7 +2187,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Add the function used by the instruction that is checked in this test.
// When the function doesn't exist the destination extension, the
// instruction keeps pointing to the old extension.
destinationExtension.InsertNewEventsFunction("MyEventsFunction", 0);
destinationExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyEventsFunction", 0);
auto &duplicatedObject =
destinationExtension.GetEventsBasedObjects().InsertNew(
@@ -2217,6 +2297,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Free function
auto &myEventsFunction =
project.GetEventsFunctionsExtension("MyEventsExtension")
.GetEventsFunctions()
.GetEventsFunction("MyEventsFunction");
REQUIRE(myEventsFunction.GetParameters().GetParameter(1).GetExtraInfo() ==
"MyRenamedExtension::MyEventsBasedObject");
@@ -2333,8 +2414,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyEventsExtension::MyRenamedFunctionExpressionAndCondition");
// Check that the action still refer to the right ExpressionAndCondition.
REQUIRE(eventsExtension.GetEventsFunction("MyEventsFunctionActionWithOperator")
.GetGetterName() == "MyRenamedFunctionExpressionAndCondition");
REQUIRE(eventsExtension.GetEventsFunctions()
.GetEventsFunction("MyEventsFunctionActionWithOperator")
.GetGetterName() ==
"MyRenamedFunctionExpressionAndCondition");
}
}
@@ -2345,7 +2428,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
@@ -2358,10 +2442,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
@@ -2372,6 +2458,134 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedParameter])");
}
SECTION("(Free function) number parameter renamed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
auto &instruction = CreateNumberVariableSetterAction(
project, eventsFunction.GetEvents(), "MyParameter", "123");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedParameter");
}
SECTION("(Free function) number parameter renamed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
auto &instruction = CreateNumberVariableGetterCondition(
project, eventsFunction.GetEvents(), "MyParameter", "123");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedParameter");
}
SECTION("(Free function) parameter type changed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
// The property was of type "string".
auto &instruction = CreateStringVariableSetterAction(
project, eventsFunction.GetEvents(), "MyParameter", "123");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::ChangeParameterType(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter");
REQUIRE(instruction.GetType() == "SetNumberVariable");
}
SECTION("(Free function) parameter type changed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
.SetName("number");
// The property was of type "string".
auto &instruction = CreateStringVariableGetterCondition(
project, eventsFunction.GetEvents(), "MyParameter", "123");
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::ChangeParameterType(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter");
REQUIRE(instruction.GetType() == "NumberVariable");
}
SECTION("(Free function) number parameter not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
@@ -2379,7 +2593,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyParameter")
.GetValueTypeMetadata()
@@ -2393,10 +2608,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyParameter", "MyRenamedParameter");
@@ -2415,7 +2632,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
@@ -2431,10 +2649,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyObject", "MyRenamedObject");
@@ -2454,7 +2674,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
@@ -2469,10 +2690,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyObject", "MyRenamedObject");
@@ -2491,7 +2714,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
@@ -2512,10 +2736,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior");
@@ -2535,7 +2761,8 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyFreeEventsFunction", 0);
eventsExtension.GetEventsFunctions().InsertNewEventsFunction(
"MyFreeEventsFunction", 0);
eventsFunction.GetParameters()
.AddNewParameter("MyObject")
.GetValueTypeMetadata()
@@ -2555,10 +2782,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
gd::ObjectsContainer parametersObjectsContainer(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsExtension, eventsFunction,
parametersObjectsContainer);
parametersObjectsContainer, parameterVariablesContainer);
gd::WholeProjectRefactorer::RenameParameter(
project, projectScopedContainers, eventsFunction,
parametersObjectsContainer, "MyBehavior", "MyRenamedBehavior");
@@ -2727,6 +2956,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Free function
auto &myEventsFunction =
project.GetEventsFunctionsExtension("MyEventsExtension")
.GetEventsFunctions()
.GetEventsFunction("MyEventsFunction");
REQUIRE(myEventsFunction.GetParameters().GetParameter(2).GetExtraInfo() ==
"MyEventsExtension::MyRenamedEventsBasedBehavior");
@@ -2837,6 +3067,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Free function
auto &myEventsFunction =
project.GetEventsFunctionsExtension("MyEventsExtension")
.GetEventsFunctions()
.GetEventsFunction("MyEventsFunction");
REQUIRE(myEventsFunction.GetParameters().GetParameter(1).GetExtraInfo() ==
"MyEventsExtension::MyRenamedEventsBasedObject");
@@ -3366,6 +3597,54 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedProperty])");
}
SECTION("(Events based behavior) property renamed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
auto &instruction = CreateNumberVariableSetterAction(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
project, eventsExtension, eventsBasedBehavior, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
}
SECTION("(Events based behavior) property renamed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
auto &instruction = CreateNumberVariableGetterCondition(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
project, eventsExtension, eventsBasedBehavior, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
}
SECTION("(Events based behavior) property not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
@@ -3397,6 +3676,52 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyExtension::GetVariableAsNumber(MyProperty)");
}
SECTION("(Events based behavior) property type changed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
// The property was of type "string".
auto &instruction = CreateStringVariableSetterAction(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::ChangeEventsBasedBehaviorPropertyType(
project, eventsExtension, eventsBasedBehavior, "MyProperty");
REQUIRE(instruction.GetType() == "SetNumberVariable");
}
SECTION("(Events based behavior) property type changed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
auto &behaviorAction =
eventsBasedBehavior.GetEventsFunctions().InsertNewEventsFunction(
"MyBehaviorEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureBehaviorEventsFunctionsProperParameters(
eventsExtension, eventsBasedBehavior);
// The property was of type "string".
auto &instruction = CreateStringVariableGetterCondition(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::ChangeEventsBasedBehaviorPropertyType(
project, eventsExtension, eventsBasedBehavior, "MyProperty");
REQUIRE(instruction.GetType() == "NumberVariable");
}
SECTION("(Events based behavior) shared property renamed") {
gd::Project project;
gd::Platform platform;
@@ -3536,6 +3861,54 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
"MyExtension::GetVariableAsNumber(MyVariable.MyChild[MyRenamedProperty])");
}
SECTION("(Events based object) property renamed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
auto &instruction = CreateNumberVariableSetterAction(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::RenameEventsBasedObjectProperty(
project, eventsExtension, eventsBasedObject, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
}
SECTION("(Events based object) property renamed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
auto &instruction = CreateNumberVariableGetterCondition(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::RenameEventsBasedObjectProperty(
project, eventsExtension, eventsBasedObject, "MyProperty",
"MyRenamedProperty");
REQUIRE(instruction.GetParameter(0).GetPlainString() ==
"MyRenamedProperty");
}
SECTION("(Events based object) property not renamed (in variable parameter)") {
gd::Project project;
gd::Platform platform;
@@ -3566,6 +3939,52 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(instruction2.GetParameter(0).GetPlainString() ==
"MyExtension::GetVariableAsNumber(MyProperty)");
}
SECTION("(Events based object) property type changed (in variable setter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
// The property was of type "string".
auto &instruction = CreateStringVariableSetterAction(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::ChangeEventsBasedObjectPropertyType(
project, eventsExtension, eventsBasedObject, "MyProperty");
REQUIRE(instruction.GetType() == "SetNumberVariable");
}
SECTION("(Events based object) property type changed (in variable getter)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
eventsExtension.GetEventsBasedObjects().Get("MyEventsBasedObject");
auto &behaviorAction =
eventsBasedObject.GetEventsFunctions().InsertNewEventsFunction(
"MyObjectEventsFunction", 0);
gd::WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
eventsExtension, eventsBasedObject);
// The property was of type "string".
auto &instruction = CreateStringVariableGetterCondition(
project, behaviorAction.GetEvents(), "MyProperty", "123");
gd::WholeProjectRefactorer::ChangeEventsBasedObjectPropertyType(
project, eventsExtension, eventsBasedObject, "MyProperty");
REQUIRE(instruction.GetType() == "NumberVariable");
}
}
// TODO: Check that this works when behaviors are attached to a child-object.
TEST_CASE("WholeProjectRefactorer (FindInvalidRequiredBehaviorProperties)",

View File

@@ -2332,9 +2332,9 @@ module.exports = {
this._pixiObject = new PIXI.Graphics();
this._pixiContainer.addChild(this._pixiObject);
this._faceResourceNames = ['', '', '', '', '', ''];
this._faceVisibilities = [true, true, true, true, true, true];
this._shouldRepeatTextureOnFace = [true, true, true, true, true, true];
this._faceResourceNames = new Array(6).fill(null);
this._faceVisibilities = new Array(6).fill(null);
this._shouldRepeatTextureOnFace = new Array(6).fill(null);
this._facesOrientation = 'Y';
this._backFaceUpThroughWhichAxisRotation = 'X';
this._shouldUseTransparentTexture = false;

View File

@@ -26,7 +26,8 @@ PanelSpriteObject::PanelSpriteObject()
leftMargin(0),
topMargin(0),
rightMargin(0),
bottomMargin(0) {}
bottomMargin(0),
tiled(false) {}
PanelSpriteObject::~PanelSpriteObject() {}

View File

@@ -964,7 +964,7 @@ namespace gdjs {
// It would be useless to try to recreate it as updateBodyFromObject already does it.
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (this._body !== null) {
if (this._body !== null && !this.isStatic() && this._body.IsAwake()) {
this.owner.setX(
this._body.GetPosition().get_x() * this._sharedData.worldScale -
this.owner.getWidth() / 2 +

View File

@@ -20,7 +20,7 @@ module.exports = {
extension
.setExtensionInformation(
'Physics3D',
_('3D Physics Engine'),
_('3D physics engine'),
"The physics engine simulates realistic object physics, with gravity, forces, joints, etc. It's perfect for games that need to have realistic behaving objects and a gameplay centered around it.",
'Florian Rival',
'MIT'
@@ -29,7 +29,7 @@ module.exports = {
.setCategory('Movement')
.setTags('physics, gravity, obstacle, collision');
extension
.addInstructionOrExpressionGroupMetadata(_('3D Physics Engine'))
.addInstructionOrExpressionGroupMetadata(_('3D physics engine'))
.setIcon('JsPlatform/Extensions/physics3d.svg');
{
const behavior = new gd.BehaviorJsImplementation();
@@ -998,8 +998,8 @@ module.exports = {
'number',
'AngularVelocityX',
_('Angular velocity X'),
_('the object angular velocity on X.'),
_('the angular velocity on X'),
_('the object angular velocity around X.'),
_('the angular velocity around X'),
_('Velocity'),
'JsPlatform/Extensions/physics3d.svg'
)
@@ -1019,8 +1019,8 @@ module.exports = {
'number',
'AngularVelocityY',
_('Angular velocity Y'),
_('the object angular velocity on Y.'),
_('the angular velocity on Y'),
_('the object angular velocity around Y.'),
_('the angular velocity around Y'),
_('Velocity'),
'JsPlatform/Extensions/physics3d.svg'
)
@@ -1040,8 +1040,8 @@ module.exports = {
'number',
'AngularVelocityZ',
_('Angular velocity Z'),
_('the object angular velocity on Z.'),
_('the angular velocity on Z'),
_('the object angular velocity around Z.'),
_('the angular velocity around Z'),
_('Velocity'),
'JsPlatform/Extensions/physics3d.svg'
)
@@ -1551,6 +1551,13 @@ module.exports = {
return true;
}
if (propertyName === 'canBePushed') {
behaviorContent
.getChild('canBePushed')
.setBoolValue(newValue === '1');
return true;
}
return false;
};
behavior.getProperties = function (behaviorContent) {
@@ -1745,6 +1752,22 @@ module.exports = {
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
if (!behaviorContent.hasChild('canBePushed')) {
behaviorContent.addChild('canBePushed').setBoolValue(true);
}
behaviorProperties
.getOrCreate('canBePushed')
.setLabel('Can be pushed by other characters')
.setGroup(_('Walk'))
.setType('Boolean')
.setValue(
behaviorContent.getChild('canBePushed').getBoolValue()
? 'true'
: 'false'
)
.setAdvanced(true)
.setQuickCustomizationVisibility(gd.QuickCustomization.Hidden);
return behaviorProperties;
};
@@ -1765,6 +1788,7 @@ module.exports = {
behaviorContent
.addChild('shouldBindObjectAndForwardAngle')
.setBoolValue(true);
behaviorContent.addChild('canBePushed').setBoolValue(true);
};
const aut = extension

View File

@@ -807,17 +807,25 @@ namespace gdjs {
*/
getBodyLayer(): number {
return Jolt.ObjectLayerPairFilterMask.prototype.sGetObjectLayer(
// Make sure objects don't register in the wrong layer group.
this.bodyType === 'Static'
? this.layers & gdjs.Physics3DSharedData.staticLayersMask
: this.layers & gdjs.Physics3DSharedData.dynamicLayersMask,
// Static objects accept all collisions as it's the mask of dynamic objects that matters.
this.bodyType === 'Static'
? gdjs.Physics3DSharedData.allLayersMask
: this.masks
this.getLayersAccordingToBodyType(),
this.getMasksAccordingToBodyType()
);
}
private getLayersAccordingToBodyType(): integer {
// Make sure objects don't register in the wrong layer group.
return this.isStatic()
? this.layers & gdjs.Physics3DSharedData.staticLayersMask
: this.layers & gdjs.Physics3DSharedData.dynamicLayersMask;
}
private getMasksAccordingToBodyType(): integer {
// Static objects accept all collisions as it's the mask of dynamic objects that matters.
return this.isStatic()
? gdjs.Physics3DSharedData.allLayersMask
: this.masks;
}
doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// Step the world if not done this frame yet.
// Don't step at the first frame to allow events to handle overlapping objects.
@@ -1691,6 +1699,14 @@ namespace gdjs {
}
}
canCollideAgainst(otherBehavior: gdjs.Physics3DRuntimeBehavior): boolean {
return (
(this.getMasksAccordingToBodyType() &
otherBehavior.getLayersAccordingToBodyType()) !==
0
);
}
static areObjectsColliding(
object1: gdjs.RuntimeObject,
object2: gdjs.RuntimeObject,
@@ -1805,7 +1821,7 @@ namespace gdjs {
// It would be useless to try to recreate it as updateBodyFromObject already does it.
// If the body is null, we just don't do anything
// (but still run the physics simulation - this is independent).
if (_body !== null) {
if (_body !== null && _body.IsActive()) {
behavior.moveObjectToPhysicsPosition(_body.GetPosition());
behavior.moveObjectToPhysicsRotation(_body.GetRotation());
}
@@ -1819,8 +1835,6 @@ namespace gdjs {
}
const body = behavior._body!;
// TODO the `if` is probably unnecessary because `SetPositionAndRotationWhenChanged` already check this.
// The object object transform has changed, update body transform:
if (
this.behavior._objectOldX !== owner3D.getX() ||
this.behavior._objectOldY !== owner3D.getY() ||

View File

@@ -72,6 +72,7 @@ namespace gdjs {
private _jumpSpeed: float;
private _jumpSustainTime: float;
private _stairHeightMax: float;
_canBePushed: boolean;
private _hasPressedForwardKey: boolean = false;
private _hasPressedBackwardKey: boolean = false;
@@ -145,6 +146,10 @@ namespace gdjs {
behaviorData.stairHeightMax === undefined
? 20
: behaviorData.stairHeightMax;
this._canBePushed =
behaviorData.canBePushed === undefined
? true
: behaviorData.canBePushed;
}
private getVec3(x: float, y: float, z: float): Jolt.Vec3 {
@@ -327,6 +332,21 @@ namespace gdjs {
return result;
}
getPhysicsRotation(result: Jolt.Quat): Jolt.Quat {
// Characters body should not rotate around X and Y.
const rotation = result.sEulerAngles(
this.getVec3(0, 0, gdjs.toRad(this.owner3D.getAngle()))
);
result.Set(
rotation.GetX(),
rotation.GetY(),
rotation.GetZ(),
rotation.GetW()
);
Jolt.destroy(rotation);
return result;
}
moveObjectToPhysicsPosition(physicsPosition: Jolt.RVec3): void {
const { behavior } = this.getPhysics3D();
this.owner3D.setCenterXInScene(
@@ -341,6 +361,19 @@ namespace gdjs {
);
}
moveObjectToPhysicsRotation(physicsRotation: Jolt.Quat): void {
const threeObject = this.owner3D.get3DRendererObject();
threeObject.quaternion.x = physicsRotation.GetX();
threeObject.quaternion.y = physicsRotation.GetY();
threeObject.quaternion.z = physicsRotation.GetZ();
threeObject.quaternion.w = physicsRotation.GetW();
// TODO Avoid this instantiation
const euler = new THREE.Euler(0, 0, 0, 'ZYX');
euler.setFromQuaternion(threeObject.quaternion);
// No need to update the rotation for X and Y as CharacterVirtual doesn't change it.
this.owner3D.setAngle(gdjs.toDegrees(euler.z));
}
onDeActivate() {
this.collisionChecker.clearContacts();
}
@@ -1224,7 +1257,7 @@ namespace gdjs {
* @returns Returns true if it is falling and false if not.
*/
isFallingWithoutJumping(): boolean {
return !this.isOnFloor() && this._currentJumpSpeed === 0;
return !this.isOnFloor() && !this.isJumping();
}
/**
@@ -1241,7 +1274,8 @@ namespace gdjs {
*/
isFalling(): boolean {
return (
!this.isOnFloor() && this._currentJumpSpeed < this._currentFallSpeed
this.isFallingWithoutJumping() ||
(this.isJumping() && this._currentFallSpeed > this._currentJumpSpeed)
);
}
@@ -1323,6 +1357,8 @@ namespace gdjs {
export namespace PhysicsCharacter3DRuntimeBehavior {
export class CharacterBodyUpdater {
characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior;
/** Handle collisions between characters that can push each other. */
static characterVsCharacterCollision: Jolt.CharacterVsCharacterCollisionSimple | null = null;
constructor(characterBehavior: gdjs.PhysicsCharacter3DRuntimeBehavior) {
this.characterBehavior = characterBehavior;
@@ -1335,7 +1371,13 @@ namespace gdjs {
const shape = behavior.createShape();
const settings = new Jolt.CharacterVirtualSettings();
settings.mInnerBodyLayer = behavior.getBodyLayer();
// Characters innerBody are Kinematic body, they don't allow other
// characters to push them.
// The layer 0 doesn't allow any collision as masking them always result to 0.
// This allows CharacterVsCharacterCollisionSimple to handle the collisions.
settings.mInnerBodyLayer = this.characterBehavior._canBePushed
? 0
: behavior.getBodyLayer();
settings.mInnerBodyShape = shape;
settings.mMass = shape.GetMassProperties().get_mMass();
settings.mMaxSlopeAngle = gdjs.toRad(_slopeMaxAngle);
@@ -1375,13 +1417,112 @@ namespace gdjs {
.GetBodyLockInterface()
.TryGetBody(character.GetInnerBodyID());
this.characterBehavior.character = character;
if (this.characterBehavior._canBePushed) {
// CharacterVsCharacterCollisionSimple handle characters pushing each other.
let characterVsCharacterCollision =
CharacterBodyUpdater.characterVsCharacterCollision;
if (!characterVsCharacterCollision) {
characterVsCharacterCollision = new Jolt.CharacterVsCharacterCollisionSimple();
CharacterBodyUpdater.characterVsCharacterCollision = characterVsCharacterCollision;
}
characterVsCharacterCollision.Add(character);
character.SetCharacterVsCharacterCollision(
characterVsCharacterCollision
);
const characterContactListener = new Jolt.CharacterContactListenerJS();
characterContactListener.OnAdjustBodyVelocity = (
character,
body2Ptr,
linearVelocityPtr,
angularVelocity
) => {};
characterContactListener.OnContactValidate = (
character,
bodyID2,
subShapeID2
) => {
return true;
};
characterContactListener.OnCharacterContactValidate = (
characterPtr,
otherCharacterPtr,
subShapeID2
) => {
// CharacterVsCharacterCollisionSimple doesn't handle collision layers.
// We have to filter characters ourself.
const character = Jolt.wrapPointer(
characterPtr,
Jolt.CharacterVirtual
);
const otherCharacter = Jolt.wrapPointer(
otherCharacterPtr,
Jolt.CharacterVirtual
);
const body = _sharedData.physicsSystem
.GetBodyLockInterface()
.TryGetBody(character.GetInnerBodyID());
const otherBody = _sharedData.physicsSystem
.GetBodyLockInterface()
.TryGetBody(otherCharacter.GetInnerBodyID());
const physicsBehavior = body.gdjsAssociatedBehavior;
const otherPhysicsBehavior = otherBody.gdjsAssociatedBehavior;
if (!physicsBehavior || !otherPhysicsBehavior) {
return true;
}
return physicsBehavior.canCollideAgainst(otherPhysicsBehavior);
};
characterContactListener.OnContactAdded = (
character,
bodyID2,
subShapeID2,
contactPosition,
contactNormal,
settings
) => {};
characterContactListener.OnCharacterContactAdded = (
character,
otherCharacter,
subShapeID2,
contactPosition,
contactNormal,
settings
) => {};
characterContactListener.OnContactSolve = (
character,
bodyID2,
subShapeID2,
contactPosition,
contactNormal,
contactVelocity,
contactMaterial,
characterVelocity,
newCharacterVelocity
) => {};
characterContactListener.OnCharacterContactSolve = (
character,
otherCharacter,
subShapeID2,
contactPosition,
contactNormal,
contactVelocity,
contactMaterial,
characterVelocityPtr,
newCharacterVelocityPtr
) => {};
character.SetListener(characterContactListener);
}
// TODO This is not really reliable. We could choose to disable it and force user to use the "is on platform" condition.
//body.SetCollideKinematicVsNonDynamic(true);
return body;
}
updateObjectFromBody() {
const { behavior } = this.characterBehavior.getPhysics3D();
const { character } = this.characterBehavior;
if (!character) {
return;
@@ -1390,8 +1531,9 @@ namespace gdjs {
this.characterBehavior.moveObjectToPhysicsPosition(
character.GetPosition()
);
// TODO No need to update the rotation for X and Y as CharacterVirtual doesn't change it.
behavior.moveObjectToPhysicsRotation(character.GetRotation());
this.characterBehavior.moveObjectToPhysicsRotation(
character.GetRotation()
);
}
updateBodyFromObject() {
@@ -1412,9 +1554,10 @@ namespace gdjs {
behavior._objectOldRotationY !== owner3D.getRotationY() ||
behavior._objectOldRotationZ !== owner3D.getAngle()
) {
// TODO No need to update the rotation for X and Y as CharacterVirtual doesn't change it.
character.SetRotation(
behavior.getPhysicsRotation(_sharedData.getQuat(0, 0, 0, 1))
this.characterBehavior.getPhysicsRotation(
_sharedData.getQuat(0, 0, 0, 1)
)
);
}
}

View File

@@ -78,6 +78,18 @@ module.exports = {
} else if (propertyName === 'disabled') {
objectContent.disabled = newValue === '1';
return true;
} else if (propertyName === 'maxLength') {
objectContent.maxLength = newValue;
return true;
} else if (propertyName === 'paddingX') {
objectContent.paddingX = Math.max(0, parseFloat(newValue));
return true;
} else if (propertyName === 'paddingY') {
objectContent.paddingY = Math.max(0, parseFloat(newValue));
return true;
} else if (propertyName === 'textAlign') {
objectContent.textAlign = newValue;
return true;
}
return false;
@@ -200,6 +212,50 @@ module.exports = {
.setLabel(_('Width'))
.setGroup(_('Border appearance'));
objectProperties
.getOrCreate('paddingX')
.setValue(
(objectContent.paddingX !== undefined
? objectContent.paddingX
: 2
).toString()
)
.setType('number')
.setLabel(_('Padding (horizontal)'))
.setGroup(_('Field appearance'));
objectProperties
.getOrCreate('paddingY')
.setValue(
(objectContent.paddingY !== undefined
? objectContent.paddingY
: 1
).toString()
)
.setType('number')
.setLabel(_('Padding (vertical)'))
.setGroup(_('Field appearance'));
objectProperties
.getOrCreate('maxLength')
.setValue((objectContent.maxLength || 0).toString())
.setType('number')
.setLabel(_('Max length'))
.setDescription(
_(
'The maximum length of the input value (this property will be ignored if the input type is a number).'
)
)
.setAdvanced(true);
objectProperties
.getOrCreate('textAlign')
.setValue(objectContent.textAlign || 'left')
.setType('choice')
.addExtraInfo('left')
.addExtraInfo('center')
.addExtraInfo('right')
.setLabel(_('Text alignment'))
.setGroup(_('Field appearance'));
return objectProperties;
};
textInputObject.content = {
@@ -216,6 +272,10 @@ module.exports = {
borderWidth: 1,
readOnly: false,
disabled: false,
paddingX: 2,
paddingY: 1,
textAlign: 'left',
maxLength: 0,
};
textInputObject.updateInitialInstanceProperty = function (
@@ -572,6 +632,22 @@ module.exports = {
.getCodeExtraInformation()
.setFunctionName('isFocused');
object
.addScopedCondition(
'IsInputSubmitted',
_('Input is submitted'),
_(
'Check if the input is submitted, which usually happens when the Enter key is pressed on a keyboard, or a specific button on mobile virtual keyboards.'
),
_('_PARAM0_ value was submitted'),
'',
'res/conditions/surObject24.png',
'res/conditions/surObject.png'
)
.addParameter('object', _('Text input'), 'TextInputObject', false)
.getCodeExtraInformation()
.setFunctionName('isSubmitted');
object
.addScopedAction(
'Focus',
@@ -627,7 +703,6 @@ module.exports = {
const DEFAULT_WIDTH = 300;
const DEFAULT_HEIGHT = 30;
const TEXT_MASK_PADDING = 2;
class RenderedTextInputObjectInstance extends RenderedInstance {
constructor(
@@ -741,24 +816,51 @@ module.exports = {
);
const borderWidth = object.content.borderWidth || 0;
const paddingX =
object.content.paddingX !== undefined
? Math.min(
parseFloat(object.content.paddingX),
width / 2 - borderWidth
)
: 2;
const paddingY =
object.content.paddingY !== undefined
? Math.min(
parseFloat(object.content.paddingY),
height / 2 - borderWidth
)
: 1;
// Draw the mask for the text.
const textOffset = borderWidth + TEXT_MASK_PADDING;
const textOffsetX = borderWidth + paddingX;
// textOffsetY does not include the paddingY because browsers display the text, even if it is supposed to be cut off by vertical padding.
const textOffsetY = borderWidth;
this._pixiTextMask.clear();
this._pixiTextMask.beginFill(0xdddddd, 1);
this._pixiTextMask.drawRect(
textOffset,
textOffset,
width - 2 * textOffset,
height - 2 * textOffset
textOffsetX,
textOffsetY,
width - 2 * textOffsetX,
height - 2 * textOffsetY
);
this._pixiTextMask.endFill();
const isTextArea = object.content.inputType === 'text area';
const textAlign = object.content.textAlign
? object.content.textAlign
: 'left';
if (textAlign === 'left') this._pixiText.position.x = textOffsetX;
else if (textAlign === 'right')
this._pixiText.position.x =
0 + width - this._pixiText.width - textOffsetX;
else if (textAlign === 'center') {
this._pixiText.align = 'center';
this._pixiText.position.x = 0 + width / 2 - this._pixiText.width / 2;
}
this._pixiText.position.x = textOffset;
this._pixiText.position.y = isTextArea
? textOffset
? textOffsetY + paddingY
: height / 2 - this._pixiText.height / 2;
// Draw the background and border.

View File

@@ -25,6 +25,10 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
borderWidth: 2,
disabled: false,
readOnly: false,
paddingX: 2,
paddingY: 1,
textAlign: 'left',
maxLength: 20,
},
});
@@ -166,33 +170,33 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
object,
} = await setupObjectAndGetDomElementContainer();
const inputElement = gameDomElementContainer.querySelector('input');
if (!inputElement) throw new Error('Expected input element to be found');
const formElement = gameDomElementContainer.querySelector('form');
if (!formElement) throw new Error('Expected form element to be found');
// Check visibility of the DOM element is visible by default, if it should be visible
// on the screen.
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
// Check visibility of the DOM element is updated at each frame,
// according to the object visibility.
object.hide(true);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('none');
expect(formElement.style.display).to.be('none');
object.hide(false);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
// Check visibility of the DOM element is updated at each frame,
// according to the layer visibility.
runtimeScene.getLayer('').show(false);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('none');
expect(formElement.style.display).to.be('none');
runtimeScene.getLayer('').show(true);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
// Clean up - not mandatory but to avoid overloading the testing browser.
runtimeScene.unloadScene();
@@ -205,31 +209,31 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
object,
} = await setupObjectAndGetDomElementContainer();
const inputElement = gameDomElementContainer.querySelector('input');
if (!inputElement) throw new Error('Expected input element to be found');
const formElement = gameDomElementContainer.querySelector('form');
if (!formElement) throw new Error('Expected input element to be found');
// Check visibility of the DOM element is visible by default, if it should be visible
// on the screen.
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
// Check visibility of the DOM element is updated at each frame,
// according to the object position of screen.
object.setX(-500); // -500 + 300 (object default width) = -200, still outside the camera.
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('none');
expect(formElement.style.display).to.be('none');
object.setWidth(600); // -500 + 600 = 100, inside the camera
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
runtimeScene.getLayer('').setCameraX(900);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('none');
expect(formElement.style.display).to.be('none');
runtimeScene.getLayer('').setCameraX(400);
runtimeScene.renderAndStep(1000 / 60);
expect(inputElement.style.display).to.be('initial');
expect(formElement.style.display).to.be('initial');
// Clean up - not mandatory but to avoid overloading the testing browser.
runtimeScene.unloadScene();

View File

@@ -31,6 +31,7 @@ namespace gdjs {
private _input: HTMLInputElement | HTMLTextAreaElement | null = null;
private _instanceContainer: gdjs.RuntimeInstanceContainer;
private _runtimeGame: gdjs.RuntimeGame;
private _form: HTMLFormElement | null = null;
constructor(
runtimeObject: gdjs.TextInputRuntimeObject,
@@ -47,18 +48,34 @@ namespace gdjs {
if (!!this._input)
throw new Error('Tried to recreate an input while it already exists.');
this._form = document.createElement('form');
const isTextArea = this._object.getInputType() === 'text area';
this._input = document.createElement(isTextArea ? 'textarea' : 'input');
this._input.style.border = '1px solid black';
this._form.style.border = '0px';
this._form.style.borderRadius = '0px';
this._form.style.backgroundColor = 'transparent';
this._form.style.position = 'absolute';
this._form.style.pointerEvents = 'auto'; // Element can be clicked/touched.
this._form.style.display = 'none'; // Hide while object is being set up.
this._form.style.boxSizing = 'border-box';
this._form.style.textAlign = this._object.getTextAlign();
this._input.autocomplete = 'off';
this._input.style.borderRadius = '0px';
this._input.style.backgroundColor = 'white';
this._input.style.position = 'absolute';
this._input.style.resize = 'none';
this._input.style.outline = 'none';
this._input.style.pointerEvents = 'auto'; // Element can be clicked/touched.
this._input.style.display = 'none'; // Hide while object is being set up.
this._input.style.boxSizing = 'border-box'; // Important for iOS, because border is added to width/height.
this._input.style.outline = 'none'; // Remove any style added by the browser to highlight the focused field in a form (:focus & :focus-visible modifiers).
this._input.style.resize = 'none'; // Prevent user from resizing the input when it's a text area.
this._input.style.border = '1px solid black';
this._input.style.boxSizing = 'border-box';
this._input.style.width = '100%';
this._input.style.height = '100%';
this._input.maxLength = this._object.getMaxLength();
this._input.style.padding = `${this._object
.getPaddingY()
.toFixed(2)}px ${this._object.getPaddingX().toFixed(2)}px`;
this._form.appendChild(this._input);
this._input.addEventListener('input', () => {
if (!this._input) return;
@@ -72,6 +89,11 @@ namespace gdjs {
if (document.activeElement !== this._input) this._input.focus();
});
this._form.addEventListener('submit', (event) => {
event.preventDefault();
this._object.onRendererFormSubmitted();
});
this.updateString();
this.updateFont();
this.updatePlaceholder();
@@ -83,11 +105,14 @@ namespace gdjs {
this.updateBorderWidth();
this.updateDisabled();
this.updateReadOnly();
this.updateTextAlign();
this.updateMaxLength();
this.updatePadding();
this._runtimeGame
.getRenderer()
.getDomElementContainer()!
.appendChild(this._input);
.appendChild(this._form);
}
_destroyElement() {
@@ -116,13 +141,12 @@ namespace gdjs {
}
updatePreRender() {
if (!this._input) return;
if (!this._input || !this._form) return;
// Hide the input entirely if the object is hidden.
// Because this object is rendered as a DOM element (and not part of the PixiJS
// scene graph), we have to do this manually.
if (this._object.isHidden()) {
this._input.style.display = 'none';
this._form.style.display = 'none';
return;
}
@@ -136,7 +160,7 @@ namespace gdjs {
do {
const layer = instanceContainer.getLayer(object.getLayer());
if (!layer.isVisible() || !object.isVisible()) {
this._input.style.display = 'none';
this._form.style.display = 'none';
return;
}
// TODO Declare an interface to move up in the object tree.
@@ -184,7 +208,7 @@ namespace gdjs {
canvasLeft > runtimeGame.getGameResolutionWidth() ||
canvasTop > runtimeGame.getGameResolutionHeight();
if (isOutsideCanvas) {
this._input.style.display = 'none';
this._form.style.display = 'none';
return;
}
@@ -210,12 +234,17 @@ namespace gdjs {
const widthInContainer = pageRight - pageLeft;
const heightInContainer = pageBottom - pageTop;
this._input.style.left = pageLeft + 'px';
this._input.style.top = pageTop + 'px';
this._input.style.width = widthInContainer + 'px';
this._input.style.height = heightInContainer + 'px';
this._input.style.transform =
this._form.style.left = pageLeft + 'px';
this._form.style.top = pageTop + 'px';
this._form.style.width = widthInContainer + 'px';
this._form.style.height = heightInContainer + 'px';
this._form.style.transform =
'rotate3d(0,0,1,' + (this._object.getAngle() % 360) + 'deg)';
this._form.style.textAlign = this._object.getTextAlign();
this._input.style.padding = `${this._object
.getPaddingY()
.toFixed(2)}px ${this._object.getPaddingX().toFixed(2)}px`;
// Automatically adjust the font size to follow the game scale.
this._input.style.fontSize =
@@ -224,7 +253,7 @@ namespace gdjs {
'px';
// Display after the object is positioned.
this._input.style.display = 'initial';
this._form.style.display = 'initial';
}
updateString() {
@@ -246,8 +275,8 @@ namespace gdjs {
}
updateOpacity() {
if (!this._input) return;
this._input.style.opacity = '' + this._object.getOpacity() / 255;
if (!this._form) return;
this._form.style.opacity = (this._object.getOpacity() / 255).toFixed(3);
}
updateInputType() {
@@ -297,14 +326,39 @@ namespace gdjs {
this._input.style.borderWidth = this._object.getBorderWidth() + 'px';
}
updateDisabled() {
if (!this._input) return;
if (!this._form) return;
this._input.disabled = this._object.isDisabled();
this._form.disabled = this._object.isDisabled();
}
updateReadOnly() {
if (!this._form) return;
this._form.readOnly = this._object.isReadOnly();
}
updateMaxLength() {
const input = this._input;
if (!input) return;
if (this._object.getMaxLength() <= 0) {
input.removeAttribute('maxLength');
return;
}
input.maxLength = this._object.getMaxLength();
}
updatePadding() {
if (!this._input) return;
this._input.readOnly = this._object.isReadOnly();
this._input.style.padding = `${this._object
.getPaddingY()
.toFixed(2)}px ${this._object.getPaddingX().toFixed(2)}px`;
}
updateTextAlign() {
if (!this._input) return;
const newTextAlign = this._object.getTextAlign();
this._input.style.textAlign = newTextAlign;
}
isFocused() {
@@ -317,7 +371,6 @@ namespace gdjs {
this._input.focus();
}
}
export const TextInputRuntimeObjectRenderer = TextInputRuntimeObjectPixiRenderer;
export type TextInputRuntimeObjectRenderer = TextInputRuntimeObjectPixiRenderer;
}

View File

@@ -9,9 +9,10 @@ namespace gdjs {
'search',
'text area',
] as const;
const supportedTextAlign = ['left', 'center', 'right'] as const;
type SupportedInputType = typeof supportedInputTypes[number];
type SupportedTextAlign = typeof supportedTextAlign[number];
const parseInputType = (potentialInputType: string): SupportedInputType => {
const lowercasedNewInputType = potentialInputType.toLowerCase();
@@ -22,6 +23,19 @@ namespace gdjs {
return 'text';
};
const parseTextAlign = (
potentialTextAlign: string | undefined
): SupportedTextAlign => {
if (!potentialTextAlign) return 'left';
const lowercasedNewTextAlign = potentialTextAlign.toLowerCase();
// @ts-ignore - we're actually checking that this value is correct.
if (supportedTextAlign.includes(lowercasedNewTextAlign))
return potentialTextAlign as SupportedTextAlign;
return 'left';
};
/** Base parameters for {@link gdjs.TextInputRuntimeObject} */
export interface TextInputObjectData extends ObjectData {
/** The base parameters of the TextInput */
@@ -39,6 +53,12 @@ namespace gdjs {
borderWidth: float;
disabled: boolean;
readOnly: boolean;
// ---- Values can be undefined because of support for these feature was added in v5.5.222.
paddingX?: float;
paddingY?: float;
textAlign?: SupportedTextAlign;
maxLength?: integer;
// ----
};
}
@@ -67,6 +87,10 @@ namespace gdjs {
const DEFAULT_WIDTH = 300;
const DEFAULT_HEIGHT = 30;
const clampPadding = (value: float, dimension: float, borderWidth: float) => {
return Math.max(0, Math.min(dimension / 2 - borderWidth, value));
};
/**
* Shows a text input on the screen the player can type text into.
*/
@@ -84,12 +108,16 @@ namespace gdjs {
private _textColor: [float, float, float];
private _fillColor: [float, float, float];
private _fillOpacity: float;
private _paddingX: integer;
private _paddingY: integer;
private _textAlign: SupportedTextAlign;
private _maxLength: integer;
private _borderColor: [float, float, float];
private _borderOpacity: float;
private _borderWidth: float;
private _disabled: boolean;
private _readOnly: boolean;
private _isSubmitted: boolean;
_renderer: TextInputRuntimeObjectRenderer;
constructor(
@@ -113,7 +141,17 @@ namespace gdjs {
this._borderWidth = objectData.content.borderWidth;
this._disabled = objectData.content.disabled;
this._readOnly = objectData.content.readOnly;
this._textAlign = parseTextAlign(objectData.content.textAlign);
this._maxLength = objectData.content.maxLength || 0;
this._paddingX =
objectData.content.paddingX !== undefined
? objectData.content.paddingX
: 2;
this._paddingY =
objectData.content.paddingY !== undefined
? objectData.content.paddingY
: 1;
this._isSubmitted = false;
this._renderer = new gdjs.TextInputRuntimeObjectRenderer(
this,
instanceContainer
@@ -189,6 +227,31 @@ namespace gdjs {
if (oldObjectData.content.readOnly !== newObjectData.content.readOnly) {
this.setReadOnly(newObjectData.content.readOnly);
}
if (
newObjectData.content.maxLength !== undefined &&
oldObjectData.content.maxLength !== newObjectData.content.maxLength
) {
this.setMaxLength(newObjectData.content.maxLength);
}
if (
newObjectData.content.textAlign &&
oldObjectData.content.textAlign !== newObjectData.content.textAlign
) {
this._textAlign = newObjectData.content.textAlign;
}
if (
newObjectData.content.paddingX !== undefined &&
oldObjectData.content.paddingX !== newObjectData.content.paddingX
) {
this.setPaddingX(newObjectData.content.paddingX);
}
if (
newObjectData.content.paddingY !== undefined &&
oldObjectData.content.paddingY !== newObjectData.content.paddingY
) {
this.setPaddingY(newObjectData.content.paddingY);
}
return true;
}
@@ -236,6 +299,7 @@ namespace gdjs {
}
updatePreRender(instanceContainer: RuntimeInstanceContainer): void {
this._isSubmitted = false;
this._renderer.updatePreRender();
}
@@ -253,6 +317,7 @@ namespace gdjs {
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
this._renderer.updatePadding();
}
if (initialInstanceData.opacity !== undefined) {
this.setOpacity(initialInstanceData.opacity);
@@ -288,10 +353,12 @@ namespace gdjs {
setWidth(width: float): void {
this._width = width;
this._renderer.updatePadding();
}
setHeight(height: float): void {
this._height = height;
this._renderer.updatePadding();
}
/**
@@ -349,6 +416,10 @@ namespace gdjs {
this._string = inputValue;
}
onRendererFormSubmitted() {
this._isSubmitted = true;
}
getFontResourceName() {
return this._fontResourceName;
}
@@ -500,6 +571,58 @@ namespace gdjs {
isFocused(): boolean {
return this._renderer.isFocused();
}
isSubmitted(): boolean {
return this._isSubmitted;
}
getMaxLength(): integer {
return this._maxLength;
}
setMaxLength(value: integer) {
if (this._maxLength === value) return;
this._maxLength = value;
this._renderer.updateMaxLength();
}
getPaddingX(): integer {
return clampPadding(this._paddingX, this._width, this._borderWidth);
}
setPaddingX(value: integer) {
if (this._paddingX === value) return;
if (value < 0) {
this._paddingX = 0;
return;
}
this._paddingX = value;
this._renderer.updatePadding();
}
getPaddingY(): integer {
return clampPadding(this._paddingY, this._height, this._borderWidth);
}
setPaddingY(value: integer) {
if (this._paddingY === value) return;
if (value < 0) {
this._paddingY = 0;
return;
}
this._paddingY = value;
this._renderer.updatePadding();
}
getTextAlign(): SupportedTextAlign {
return this._textAlign;
}
setTextAlign(newTextAlign: string) {
const parsedTextAlign = parseTextAlign(newTextAlign);
if (parsedTextAlign === this._textAlign) return;
this._textAlign = parsedTextAlign;
this._renderer.updateTextAlign();
}
focus(): void {
if (!this.isFocused()) {

View File

@@ -175,7 +175,8 @@ std::map<gd::String, gd::PropertyDescriptor> TextObject::GetProperties() const {
.AddExtraInfo("center")
.AddExtraInfo("bottom")
.SetLabel(_("Vertical alignment"))
.SetGroup(_("Font"));
.SetGroup(_("Font"))
.SetQuickCustomizationVisibility(gd::QuickCustomization::Hidden);
objectProperties["isOutlineEnabled"]
.SetValue(isOutlineEnabled ? "true" : "false")

View File

@@ -10,7 +10,6 @@
#include "GDCore/Events/Tools/EventsCodeNameMangler.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/SourceFile.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Localization.h"
#include "GDCore/Tools/Log.h"

View File

@@ -420,15 +420,18 @@ gd::String BehaviorCodeGenerator::GenerateUpdatePropertyFromNetworkSyncDataCode(
gd::String BehaviorCodeGenerator::GeneratePropertyValueCode(
const gd::PropertyDescriptor& property) {
if (property.GetType() == "String" || property.GetType() == "Choice" ||
property.GetType() == "Color" || property.GetType() == "Behavior" ||
property.GetType() == "Resource") {
const auto &valueType =
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(property.GetType());
const auto &primitiveType =
gd::ValueTypeMetadata::GetPrimitiveValueType(valueType);
if (primitiveType == "string" || valueType == "behavior") {
return EventsCodeGenerator::ConvertToStringExplicit(property.GetValue());
} else if (property.GetType() == "Number") {
} else if (primitiveType == "number") {
return "Number(" +
EventsCodeGenerator::ConvertToStringExplicit(property.GetValue()) +
") || 0";
} else if (property.GetType() == "Boolean") { // TODO: Check if working
} else if (primitiveType == "boolean") { // TODO: Check if working
return property.GetValue() == "true" ? "true" : "false";
}

View File

@@ -131,10 +131,12 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsAndGroups(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForFreeEventsFunction(
project, eventsFunctionsExtension, eventsFunction,
parameterObjectsAndGroups);
parameterObjectsAndGroups, parameterVariablesContainer);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
@@ -144,18 +146,15 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
codeGenerator.SetDiagnosticReport(&diagnosticReport);
gd::String output = GenerateEventsListCompleteFunctionCode(
codeGenerator,
codeGenerator.GetCodeNamespaceAccessor() + "func",
codeGenerator, codeGenerator.GetCodeNamespaceAccessor() + "func",
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
eventsFunction.GetParametersForEvents(eventsFunctionsExtension),
0,
true),
eventsFunction.GetParametersForEvents(
eventsFunctionsExtension.GetEventsFunctions()),
0, true),
codeGenerator.GenerateFreeEventsFunctionContext(
eventsFunctionsExtension,
eventsFunction,
eventsFunctionsExtension, eventsFunction,
"runtimeScene.getOnceTriggers()"),
eventsFunction.GetEvents(),
"",
eventsFunction.GetEvents(), "",
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
// TODO: the editor should pass the diagnostic report and display it to the
@@ -185,10 +184,15 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForBehaviorEventsFunction(
project, eventsFunctionsExtension, eventsBasedBehavior,
eventsFunction, parameterObjectsContainers);
eventsFunction, parameterObjectsContainers,
parameterVariablesContainer, propertyVariablesContainer);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
@@ -265,10 +269,15 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
bool compilationForRuntime) {
gd::ObjectsContainer parameterObjectsContainers(
gd::ObjectsContainer::SourceType::Function);
gd::VariablesContainer parameterVariablesContainer(
gd::VariablesContainer::SourceType::Parameters);
gd::VariablesContainer propertyVariablesContainer(
gd::VariablesContainer::SourceType::Properties);
auto projectScopedContainers = gd::ProjectScopedContainers::
MakeNewProjectScopedContainersForObjectEventsFunction(
project, eventsFunctionsExtension, eventsBasedObject, eventsFunction,
parameterObjectsContainers);
parameterObjectsContainers, parameterVariablesContainer,
propertyVariablesContainer);
EventsCodeGenerator codeGenerator(projectScopedContainers);
codeGenerator.SetCodeNamespace(codeNamespace);
@@ -375,7 +384,7 @@ gd::String EventsCodeGenerator::GenerateFreeEventsFunctionContext(
gd::String objectArraysMap;
gd::String behaviorNamesMap;
return GenerateEventsFunctionContext(eventsFunctionsExtension,
eventsFunctionsExtension,
eventsFunctionsExtension.GetEventsFunctions(),
eventsFunction,
onceTriggersVariable,
objectsGettersMap,
@@ -1350,15 +1359,25 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
const gd::String& variableName,
const VariableScope& scope,
gd::EventsCodeGenerationContext& context,
const gd::String& objectName) {
const gd::String& objectName,
bool hasChild) {
gd::String output;
const gd::VariablesContainer* variables = NULL;
if (scope == ANY_VARIABLE) {
if (scope == ANY_VARIABLE || scope == VARIABLE_OR_PROPERTY ||
scope == VARIABLE_OR_PROPERTY_OR_PARAMETER) {
const auto variablesContainersList =
GetProjectScopedContainers().GetVariablesContainersList();
const auto& variablesContainer =
variablesContainersList.GetVariablesContainerFromVariableName(
variableName);
const auto &variablesContainer =
scope == VARIABLE_OR_PROPERTY_OR_PARAMETER
? variablesContainersList.GetVariablesContainerFromVariableOrPropertyOrParameterName(
variableName)
: scope == VARIABLE_OR_PROPERTY
? variablesContainersList
.GetVariablesContainerFromVariableOrPropertyName(
variableName)
: variablesContainersList
.GetVariablesContainerFromVariableNameOnly(
variableName);
const auto sourceType = variablesContainer.GetSourceType();
if (sourceType == gd::VariablesContainer::SourceType::Scene) {
variables = &variablesContainer;
@@ -1381,6 +1400,30 @@ gd::String EventsCodeGenerator::GenerateGetVariable(
gd::VariablesContainer::SourceType::ExtensionScene) {
variables = &variablesContainer;
output = "eventsFunctionContext.sceneVariablesForExtension";
} else if (sourceType ==
gd::VariablesContainer::SourceType::Properties) {
if (hasChild) {
// Properties with children are not supported.
return "gdjs.VariablesContainer.badVariablesContainer";
}
const auto &propertiesContainersList =
GetProjectScopedContainers().GetPropertiesContainersList();
const auto &propertiesContainerAndProperty =
propertiesContainersList.Get(variableName);
return GeneratePropertyGetterWithoutCasting(
propertiesContainerAndProperty.first,
propertiesContainerAndProperty.second);
} else if (sourceType ==
gd::VariablesContainer::SourceType::Parameters) {
if (hasChild) {
// Parameters with children are not supported.
return "gdjs.VariablesContainer.badVariablesContainer";
}
const auto &parametersVectorsList =
GetProjectScopedContainers().GetParametersVectorsList();
const auto &parameter =
gd::ParameterMetadataTools::Get(parametersVectorsList, variableName);
return GenerateParameterGetterWithoutCasting(parameter);
}
} else if (scope == LAYOUT_VARIABLE) {
output = "runtimeScene.getScene().getVariables()";
@@ -1489,11 +1532,10 @@ gd::String EventsCodeGenerator::GenerateProfilerSectionEnd(
ConvertToStringExplicit(section) + "); }";
}
gd::String EventsCodeGenerator::GeneratePropertyGetter(
const gd::PropertiesContainer& propertiesContainer,
const gd::NamedPropertyDescriptor& property,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
gd::String EventsCodeGenerator::GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode) {
bool isLocalProperty =
projectScopedContainers.GetPropertiesContainersList()
.GetBottomMostPropertiesContainer() == &propertiesContainer;
@@ -1506,6 +1548,34 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(
gd::EventsFunctionsContainer::Object
? "eventsFunctionContext.getObjects(\"Object\")[0]"
: "eventsFunctionContext.getProperties()");
gd::String propertySetterCode =
propertyHolderCode + "." +
(isLocalProperty
? BehaviorCodeGenerator::GetBehaviorPropertySetterName(
property.GetName())
: BehaviorCodeGenerator::GetBehaviorSharedPropertySetterName(
property.GetName())) +
"(" + operandCode + ")\n";
return propertySetterCode;
}
gd::String EventsCodeGenerator::GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property) {
bool isLocalProperty =
projectScopedContainers.GetPropertiesContainersList()
.GetBottomMostPropertiesContainer() == &propertiesContainer;
gd::String propertyHolderCode =
propertiesContainer.GetOwner() == gd::EventsFunctionsContainer::Behavior
? "eventsFunctionContext.getObjects(\"Object\")[0].getBehavior(" +
GenerateGetBehaviorNameCode("Behavior") + ")"
: (propertiesContainer.GetOwner() ==
gd::EventsFunctionsContainer::Object
? "eventsFunctionContext.getObjects(\"Object\")[0]"
: "eventsFunctionContext.getProperties()");
gd::String propertyGetterCode =
propertyHolderCode + "." +
(isLocalProperty
@@ -1514,6 +1584,16 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(
: BehaviorCodeGenerator::GetBehaviorSharedPropertyGetterName(
property.GetName())) +
"()";
return propertyGetterCode;
}
gd::String EventsCodeGenerator::GeneratePropertyGetter(
const gd::PropertiesContainer& propertiesContainer,
const gd::NamedPropertyDescriptor& property,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
gd::String propertyGetterCode =
GeneratePropertyGetterWithoutCasting(propertiesContainer, property);
if (type == "number|string") {
if (property.GetType() == "Number") {
@@ -1548,13 +1628,18 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(
}
}
gd::String EventsCodeGenerator::GenerateParameterGetterWithoutCasting(
const gd::ParameterMetadata &parameter) {
return "eventsFunctionContext.getArgument(" +
ConvertToStringExplicit(parameter.GetName()) + ")";
}
gd::String EventsCodeGenerator::GenerateParameterGetter(
const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context) {
gd::String parameterGetterCode =
"eventsFunctionContext.getArgument(" +
ConvertToStringExplicit(parameter.GetName()) + ")";
GenerateParameterGetterWithoutCasting(parameter);
if (type == "number|string") {
if (parameter.GetValueTypeMetadata().IsNumber()) {

View File

@@ -225,6 +225,11 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
codeNamespace = codeNamespace_;
};
virtual gd::String GeneratePropertySetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property,
const gd::String &operandCode) override;
protected:
virtual gd::String GenerateParameterCodes(
const gd::Expression& parameter,
@@ -303,7 +308,8 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::String& variableName,
const VariableScope& scope,
gd::EventsCodeGenerationContext& context,
const gd::String& objectName) override;
const gd::String& objectName,
bool hasChild) override;
virtual gd::String GenerateVariableAccessor(gd::String childName) override {
// This could be probably optimised by using `getChildNamed`.
@@ -329,10 +335,17 @@ class EventsCodeGenerator : public gd::EventsCodeGenerator {
const gd::String& type,
gd::EventsCodeGenerationContext& context) override;
virtual gd::String GeneratePropertyGetterWithoutCasting(
const gd::PropertiesContainer &propertiesContainer,
const gd::NamedPropertyDescriptor &property) override;
virtual gd::String GenerateParameterGetter(const gd::ParameterMetadata& parameter,
const gd::String& type,
gd::EventsCodeGenerationContext& context) override;
virtual gd::String GenerateParameterGetterWithoutCasting(
const gd::ParameterMetadata &parameter) override;
virtual gd::String GenerateBadObject() override { return "null"; }
virtual gd::String GenerateObject(const gd::String& objectName,

View File

@@ -53,6 +53,10 @@ void MetadataDeclarationHelper::DeclareExtension(
extension.SetCategory(eventsFunctionsExtension.GetCategory());
DeclareExtensionDependencies(extension, eventsFunctionsExtension);
for (const auto &sourceFile : eventsFunctionsExtension.GetAllSourceFiles()) {
extension.AddSourceFile() = sourceFile;
}
}
/**
@@ -154,6 +158,9 @@ gd::ObjectMetadata &MetadataDeclarationHelper::DeclareObjectMetadata(
.AddDefaultBehavior("TextContainerCapability::TextContainerBehavior");
}
if (eventsBasedObject.IsPrivate())
objectMetadata.SetPrivate();
// TODO EBO Use full type to identify object to avoid collision.
// Objects are identified by their name alone.
const gd::String &objectType = eventsBasedObject.GetName();
@@ -510,6 +517,7 @@ MetadataDeclarationHelper::DeclareExpressionMetadata(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction) {
auto functionType = eventsFunction.GetFunctionType();
auto &freeEventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (functionType == gd::EventsFunction::ExpressionAndCondition) {
auto expressionAndCondition = extension.AddExpressionAndCondition(
gd::ValueTypeMetadata::GetPrimitiveValueType(
@@ -523,7 +531,7 @@ MetadataDeclarationHelper::DeclareExpressionMetadata(
eventsFunction.GetGroup(), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
expressionAndCondition.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
expressionAndCondition, 0);
expressionAndConditions.push_back(expressionAndCondition);
return expressionAndConditions.back();
@@ -544,7 +552,7 @@ MetadataDeclarationHelper::DeclareExpressionMetadata(
eventsFunction.GetGroup(), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
expression.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
expression, 0);
return expression;
}
@@ -559,6 +567,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
const gd::EventsFunction &eventsFunction) {
auto functionType = eventsFunction.GetFunctionType();
auto &freeEventsFunctions = eventsFunctionsExtension.GetEventsFunctions();
if (functionType == gd::EventsFunction::Condition) {
auto &condition = extension.AddCondition(
eventsFunction.GetName(),
@@ -568,14 +577,14 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
GetExtensionIconUrl(extension), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
condition.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
condition, 0);
return condition;
} else if (functionType == gd::EventsFunction::ActionWithOperator) {
if (eventsFunctionsExtension.HasEventsFunctionNamed(
if (freeEventsFunctions.HasEventsFunctionNamed(
eventsFunction.GetGetterName())) {
auto &getterFunction = eventsFunctionsExtension.GetEventsFunction(
eventsFunction.GetGetterName());
auto &getterFunction =
freeEventsFunctions.GetEventsFunction(eventsFunction.GetGetterName());
auto &action = extension.AddAction(
eventsFunction.GetName(),
@@ -594,7 +603,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
getterFunction));
// By convention, first parameter is always the Runtime Scene.
action.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
action, 0);
return action;
} else {
@@ -608,7 +617,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
action.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
action, 0);
return action;
}
@@ -621,7 +630,7 @@ gd::InstructionMetadata &MetadataDeclarationHelper::DeclareInstructionMetadata(
GetExtensionIconUrl(extension), GetExtensionIconUrl(extension));
// By convention, first parameter is always the Runtime Scene.
action.AddCodeOnlyParameter("currentScene", "");
DeclareEventsFunctionParameters(eventsFunctionsExtension, eventsFunction,
DeclareEventsFunctionParameters(freeEventsFunctions, eventsFunction,
action, 0);
return action;
}

View File

@@ -327,14 +327,16 @@ gd::String ObjectCodeGenerator::GenerateUpdatePropertyFromObjectDataCode(
gd::String ObjectCodeGenerator::GeneratePropertyValueCode(
const gd::PropertyDescriptor& property) {
if (property.GetType() == "String" || property.GetType() == "Choice" ||
property.GetType() == "Color" || property.GetType() == "Resource") {
const auto &primitiveType = gd::ValueTypeMetadata::GetPrimitiveValueType(
gd::ValueTypeMetadata::ConvertPropertyTypeToValueType(
property.GetType()));
if (primitiveType == "string") {
return EventsCodeGenerator::ConvertToStringExplicit(property.GetValue());
} else if (property.GetType() == "Number") {
} else if (primitiveType == "number") {
return "Number(" +
EventsCodeGenerator::ConvertToStringExplicit(property.GetValue()) +
") || 0";
} else if (property.GetType() == "Boolean") { // TODO: Check if working
} else if (primitiveType == "boolean") { // TODO: Check if working
return property.GetValue() == "true" ? "true" : "false";
}

View File

@@ -36,7 +36,6 @@ FileExtension::FileExtension() {
GetAllActions()["DeleteGroupFichier"].SetFunctionName(
"gdjs.evtTools.storage.deleteElementFromJSONFile");
GetAllActions()["DeleteFichier"]
.SetGroup(_("Storage"))
.SetFunctionName("gdjs.evtTools.storage.clearJSONFile");
StripUnimplementedInstructionsAndExpressions(); // Unimplemented things are

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