mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
74 Commits
experiment
...
v5.2.174
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a7ef02f1c1 | ||
![]() |
2cc7506ad7 | ||
![]() |
608b460da7 | ||
![]() |
6b082731c2 | ||
![]() |
e040dd5f28 | ||
![]() |
ae64cfb3bb | ||
![]() |
02b96eb298 | ||
![]() |
6f59a3ba03 | ||
![]() |
6180016b51 | ||
![]() |
b15e72410e | ||
![]() |
d6fbb0e78b | ||
![]() |
8eb9136847 | ||
![]() |
46c02ad9b7 | ||
![]() |
9846a9b45e | ||
![]() |
3d86820581 | ||
![]() |
b636eeb859 | ||
![]() |
189bcc4ceb | ||
![]() |
3fe031063c | ||
![]() |
d10712d584 | ||
![]() |
1721fb6211 | ||
![]() |
e31ea07902 | ||
![]() |
85929b6e76 | ||
![]() |
0662607409 | ||
![]() |
52f10d73ab | ||
![]() |
d4cb4fafd3 | ||
![]() |
9f4b12ce8a | ||
![]() |
b5b7edafb1 | ||
![]() |
1335819ea7 | ||
![]() |
aa7ae758f7 | ||
![]() |
7da88662fb | ||
![]() |
dbc1385787 | ||
![]() |
8944df1831 | ||
![]() |
a0925c79c3 | ||
![]() |
73642092bb | ||
![]() |
dfe84fbe2b | ||
![]() |
cd896c399d | ||
![]() |
d8126ead34 | ||
![]() |
bdbbe1f409 | ||
![]() |
3a241d03f1 | ||
![]() |
e4cf92e949 | ||
![]() |
0dc513317c | ||
![]() |
3e85242165 | ||
![]() |
ca9fe51d99 | ||
![]() |
2e9a5ee287 | ||
![]() |
b57e944da5 | ||
![]() |
06cdeb763d | ||
![]() |
5809a02a26 | ||
![]() |
3a2878c737 | ||
![]() |
2cbfb806e2 | ||
![]() |
18211d197a | ||
![]() |
128657c876 | ||
![]() |
930d4d394f | ||
![]() |
cc460657e5 | ||
![]() |
d9f2cb51a3 | ||
![]() |
d736afe4a0 | ||
![]() |
10825c0a8c | ||
![]() |
a75a8e1bfe | ||
![]() |
9a02905fb6 | ||
![]() |
414292812f | ||
![]() |
4d9035ad07 | ||
![]() |
bcf0184fdd | ||
![]() |
817f2cf758 | ||
![]() |
3c5e10f5c3 | ||
![]() |
7d19b811e4 | ||
![]() |
17b3579e5b | ||
![]() |
afa8d4f7f6 | ||
![]() |
b262cb4c3e | ||
![]() |
1c69302c02 | ||
![]() |
5ba36916c6 | ||
![]() |
7f3e2b210a | ||
![]() |
f83df15817 | ||
![]() |
40639f5578 | ||
![]() |
f07e3bfe19 | ||
![]() |
ef9a82ee33 |
@@ -62,9 +62,10 @@ jobs:
|
||||
|
||||
# Build GDevelop IDE (seems like we need to allow Node.js to use more space than usual)
|
||||
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
|
||||
# To test signing the code in the CI, add "export CSC_FOR_PULL_REQUEST=true && " before the command.
|
||||
- run:
|
||||
name: Build GDevelop IDE
|
||||
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && CI=false npm run build -- --mac --publish=never
|
||||
command: export CSC_FOR_PULL_REQUEST=true && export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && CI=false npm run build -- --mac --publish=never
|
||||
|
||||
- run:
|
||||
name: Clean dist folder to keep only installers/binaries.
|
||||
|
2
.github/workflows/extract-translations.yml
vendored
2
.github/workflows/extract-translations.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
run: sudo apt update && sudo apt install gettext -y
|
||||
|
||||
- name: Install newIDE dependencies
|
||||
run: npm install
|
||||
run: npm ci
|
||||
working-directory: newIDE/app
|
||||
|
||||
- name: Extract translations
|
||||
|
@@ -60,7 +60,7 @@ if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT WIN32 AND CMAKE_COMPILER_IS_
|
||||
endif()
|
||||
|
||||
#Activate C++11
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 11) # Upgrading to C++17 would need to remove usage of bind2nd (should be easy).
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Mark some warnings as errors
|
||||
@@ -77,7 +77,8 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
-Wno-unused-private-field
|
||||
|
||||
# Make as much warnings considered as errors as possible (only one for now).
|
||||
-Werror=return-stack-address)
|
||||
-Werror=return-stack-address
|
||||
-Werror=return-type)
|
||||
endif()
|
||||
|
||||
# Define common directories:
|
||||
|
@@ -45,10 +45,39 @@ ForEachChildVariableEvent::GetAllActionsVectors() const {
|
||||
return allActions;
|
||||
}
|
||||
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() {
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("scenevar");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&iterableVariableName, metadata));
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&valueIteratorVariableName, metadata));
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&keyIteratorVariableName, metadata));
|
||||
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
ForEachChildVariableEvent::GetAllExpressionsWithMetadata() const {
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("scenevar");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&iterableVariableName, metadata));
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&valueIteratorVariableName, metadata));
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&keyIteratorVariableName, metadata));
|
||||
|
||||
return allExpressionsWithMetadata;
|
||||
}
|
||||
|
||||
void ForEachChildVariableEvent::SerializeTo(SerializerElement& element) const {
|
||||
element.AddChild("iterableVariableName").SetValue(iterableVariableName);
|
||||
element.AddChild("valueIteratorVariableName").SetValue(valueIteratorVariableName);
|
||||
element.AddChild("keyIteratorVariableName").SetValue(keyIteratorVariableName);
|
||||
element.AddChild("iterableVariableName").SetValue(iterableVariableName.GetPlainString());
|
||||
element.AddChild("valueIteratorVariableName").SetValue(valueIteratorVariableName.GetPlainString());
|
||||
element.AddChild("keyIteratorVariableName").SetValue(keyIteratorVariableName.GetPlainString());
|
||||
gd::EventsListSerialization::SerializeInstructionsTo(
|
||||
conditions, element.AddChild("conditions"));
|
||||
gd::EventsListSerialization::SerializeInstructionsTo(
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define FOREACHCHILDVARIABLEEVENT_H
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
class Project;
|
||||
@@ -44,7 +45,7 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
*
|
||||
* It is the structure variable that will be iterated on.
|
||||
*/
|
||||
const gd::String& GetIterableVariableName() const { return iterableVariableName; };
|
||||
const gd::String& GetIterableVariableName() const { return iterableVariableName.GetPlainString(); };
|
||||
|
||||
/**
|
||||
* \brief Set the iterable variable name attached to the event.
|
||||
@@ -56,15 +57,15 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
/**
|
||||
* \brief Get the value iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the value of the
|
||||
* It is the variable that will contain the value of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
const gd::String& GetValueIteratorVariableName() const { return valueIteratorVariableName; };
|
||||
const gd::String& GetValueIteratorVariableName() const { return valueIteratorVariableName.GetPlainString(); };
|
||||
|
||||
/**
|
||||
* \brief Set the value iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the value of the
|
||||
* It is the variable that will contain the value of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
void SetValueIteratorVariableName(gd::String newName) { valueIteratorVariableName = newName; };
|
||||
@@ -72,15 +73,15 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
/**
|
||||
* \brief Get the key iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the name of the
|
||||
* It is the variable that will contain the name of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
const gd::String& GetKeyIteratorVariableName() const { return keyIteratorVariableName; };
|
||||
const gd::String& GetKeyIteratorVariableName() const { return keyIteratorVariableName.GetPlainString(); };
|
||||
|
||||
/**
|
||||
* \brief Set the key iterator variable attached to the event.
|
||||
*
|
||||
* It is the variable that will contain the name of the
|
||||
* It is the variable that will contain the name of the
|
||||
* iterable's child being iterated on.
|
||||
*/
|
||||
void SetKeyIteratorVariableName(gd::String newName) { keyIteratorVariableName = newName; };
|
||||
@@ -92,14 +93,19 @@ class GD_CORE_API ForEachChildVariableEvent : public gd::BaseEvent {
|
||||
virtual std::vector<gd::InstructionsList*> GetAllConditionsVectors();
|
||||
virtual std::vector<gd::InstructionsList*> GetAllActionsVectors();
|
||||
|
||||
virtual std::vector<std::pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata() const;
|
||||
virtual std::vector<std::pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
GetAllExpressionsWithMetadata();
|
||||
|
||||
virtual void SerializeTo(SerializerElement& element) const;
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
||||
private:
|
||||
gd::String valueIteratorVariableName;
|
||||
gd::String keyIteratorVariableName;
|
||||
gd::String iterableVariableName;
|
||||
gd::Expression valueIteratorVariableName;
|
||||
gd::Expression keyIteratorVariableName;
|
||||
gd::Expression iterableVariableName;
|
||||
gd::InstructionsList conditions;
|
||||
gd::InstructionsList actions;
|
||||
gd::EventsList events;
|
||||
|
@@ -64,12 +64,6 @@ class GD_CORE_API ForEachEvent : public gd::BaseEvent {
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
|
||||
std::vector<gd::Expression*> GetAllObjectExpressions() {
|
||||
std::vector<gd::Expression*> allObjectExpressions;
|
||||
allObjectExpressions.push_back(&objectsToPick);
|
||||
return allObjectExpressions;
|
||||
}
|
||||
|
||||
private:
|
||||
gd::Expression objectsToPick;
|
||||
gd::InstructionsList conditions;
|
||||
|
@@ -131,30 +131,36 @@ void LinkEvent::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
void LinkEvent::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
SerializerElement& includeElement = element.GetChild("include", 0, "Limites");
|
||||
|
||||
SetTarget(element.GetChild("target", 0, "Scene").GetValue().GetString());
|
||||
|
||||
if (includeElement.HasAttribute("includeAll")) {
|
||||
// Compatibility with GDevelop <= 4.0.92
|
||||
if (includeElement.GetBoolAttribute("includeAll", true)) {
|
||||
SetIncludeAllEvents();
|
||||
// Compatibility with GD <= 5
|
||||
if (element.HasChild("include", "Limites")) {
|
||||
SerializerElement& includeElement = element.GetChild("include", 0, "Limites");
|
||||
if (includeElement.HasAttribute("includeAll")) {
|
||||
// Compatibility with GDevelop <= 4.0.92
|
||||
if (includeElement.GetBoolAttribute("includeAll", true)) {
|
||||
SetIncludeAllEvents();
|
||||
} else {
|
||||
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
|
||||
includeElement.GetIntAttribute("end"));
|
||||
}
|
||||
} else {
|
||||
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
|
||||
includeElement.GetIntAttribute("end"));
|
||||
// GDevelop > 4.0.92
|
||||
IncludeConfig config = static_cast<IncludeConfig>(
|
||||
includeElement.GetIntAttribute("includeConfig", 0));
|
||||
if (config == INCLUDE_ALL)
|
||||
SetIncludeAllEvents();
|
||||
else if (config == INCLUDE_EVENTS_GROUP)
|
||||
SetIncludeEventsGroup(includeElement.GetStringAttribute("eventsGroup"));
|
||||
else if (config == INCLUDE_BY_INDEX)
|
||||
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
|
||||
includeElement.GetIntAttribute("end"));
|
||||
}
|
||||
} else {
|
||||
// GDevelop > 4.0.92
|
||||
IncludeConfig config = static_cast<IncludeConfig>(
|
||||
includeElement.GetIntAttribute("includeConfig", 0));
|
||||
if (config == INCLUDE_ALL)
|
||||
SetIncludeAllEvents();
|
||||
else if (config == INCLUDE_EVENTS_GROUP)
|
||||
SetIncludeEventsGroup(includeElement.GetStringAttribute("eventsGroup"));
|
||||
else if (config == INCLUDE_BY_INDEX)
|
||||
SetIncludeStartAndEnd(includeElement.GetIntAttribute("start"),
|
||||
includeElement.GetIntAttribute("end"));
|
||||
// Since GDevelop 5, links always include all events.
|
||||
SetIncludeAllEvents();
|
||||
}
|
||||
// end of compatibility code
|
||||
}
|
||||
|
||||
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
|
||||
|
@@ -35,7 +35,7 @@ vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
RepeatEvent::GetAllExpressionsWithMetadata() {
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("expression");
|
||||
auto metadata = gd::ParameterMetadata().SetType("number");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&repeatNumberExpression, metadata));
|
||||
|
||||
@@ -61,7 +61,7 @@ vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
RepeatEvent::GetAllExpressionsWithMetadata() const {
|
||||
vector<pair<const gd::Expression*, const gd::ParameterMetadata> >
|
||||
allExpressionsWithMetadata;
|
||||
auto metadata = gd::ParameterMetadata().SetType("expression");
|
||||
auto metadata = gd::ParameterMetadata().SetType("number");
|
||||
allExpressionsWithMetadata.push_back(
|
||||
std::make_pair(&repeatNumberExpression, metadata));
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -295,16 +296,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
gd::String objectInParameter =
|
||||
condition.GetParameter(pNb).GetPlainString();
|
||||
|
||||
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
|
||||
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
|
||||
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
|
||||
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
|
||||
objectInParameter)) {
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) {
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
|
||||
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
|
||||
GetObjectsAndGroups(),
|
||||
objectInParameter) !=
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter) !=
|
||||
instrInfos.parameters[pNb].GetExtraInfo()) {
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
@@ -315,11 +310,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
if (!objectName.empty() && !instrInfos.parameters.empty()) {
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Set up the context
|
||||
gd::String objectType = gd::GetTypeOfObject(
|
||||
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), realObjects[i]);
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
@@ -343,13 +337,10 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
gd::String behaviorType =
|
||||
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
|
||||
GetObjectsAndGroups(),
|
||||
condition.GetParameter(1).GetPlainString());
|
||||
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(condition.GetParameter(1).GetPlainString());
|
||||
if (instrInfos.parameters.size() >= 2) {
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Setup context
|
||||
const BehaviorMetadata& autoInfo =
|
||||
@@ -479,16 +470,10 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
|
||||
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
|
||||
if (!GetObjectsAndGroups().HasObjectNamed(objectInParameter) &&
|
||||
!GetGlobalObjectsAndGroups().HasObjectNamed(objectInParameter) &&
|
||||
!GetObjectsAndGroups().GetObjectGroups().Has(objectInParameter) &&
|
||||
!GetGlobalObjectsAndGroups().GetObjectGroups().Has(
|
||||
objectInParameter)) {
|
||||
if (!GetObjectsContainersList().HasObjectOrGroupNamed(objectInParameter)) {
|
||||
return "/* Unknown object - skipped. */";
|
||||
} else if (!instrInfos.parameters[pNb].GetExtraInfo().empty() &&
|
||||
gd::GetTypeOfObject(GetGlobalObjectsAndGroups(),
|
||||
GetObjectsAndGroups(),
|
||||
objectInParameter) !=
|
||||
GetObjectsContainersList().GetTypeOfObject(objectInParameter) !=
|
||||
instrInfos.parameters[pNb].GetExtraInfo()) {
|
||||
return "/* Mismatched object type - skipped. */";
|
||||
}
|
||||
@@ -501,11 +486,10 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
|
||||
if (!instrInfos.parameters.empty()) {
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Setup context
|
||||
gd::String objectType = gd::GetTypeOfObject(
|
||||
GetGlobalObjectsAndGroups(), GetObjectsAndGroups(), realObjects[i]);
|
||||
gd::String objectType = GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
|
||||
@@ -529,14 +513,11 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
}
|
||||
} else if (instrInfos.IsBehaviorInstruction()) {
|
||||
gd::String objectName = action.GetParameter(0).GetPlainString();
|
||||
gd::String behaviorType =
|
||||
gd::GetTypeOfBehavior(GetGlobalObjectsAndGroups(),
|
||||
GetObjectsAndGroups(),
|
||||
action.GetParameter(1).GetPlainString());
|
||||
gd::String behaviorType = GetObjectsContainersList().GetTypeOfBehavior(action.GetParameter(1).GetPlainString());
|
||||
|
||||
if (instrInfos.parameters.size() >= 2) {
|
||||
std::vector<gd::String> realObjects =
|
||||
ExpandObjectsName(objectName, context);
|
||||
GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
// Setup context
|
||||
const BehaviorMetadata& autoInfo =
|
||||
@@ -912,41 +893,6 @@ gd::String EventsCodeGenerator::ConvertToStringExplicit(
|
||||
return "\"" + ConvertToString(plainString) + "\"";
|
||||
}
|
||||
|
||||
std::vector<gd::String> EventsCodeGenerator::ExpandObjectsName(
|
||||
const gd::String& objectName,
|
||||
const EventsCodeGenerationContext& context) const {
|
||||
// Note: this logic is duplicated in EventsContextAnalyzer::ExpandObjectsName
|
||||
std::vector<gd::String> realObjects;
|
||||
if (globalObjectsAndGroups.GetObjectGroups().Has(objectName))
|
||||
realObjects = globalObjectsAndGroups.GetObjectGroups()
|
||||
.Get(objectName)
|
||||
.GetAllObjectsNames();
|
||||
else if (objectsAndGroups.GetObjectGroups().Has(objectName))
|
||||
realObjects =
|
||||
objectsAndGroups.GetObjectGroups().Get(objectName).GetAllObjectsNames();
|
||||
else
|
||||
realObjects.push_back(objectName);
|
||||
|
||||
// If current object is present, use it and only it.
|
||||
if (find(realObjects.begin(),
|
||||
realObjects.end(),
|
||||
context.GetCurrentObject()) != realObjects.end()) {
|
||||
realObjects.clear();
|
||||
realObjects.push_back(context.GetCurrentObject());
|
||||
}
|
||||
|
||||
// Ensure that all returned objects actually exists.
|
||||
for (std::size_t i = 0; i < realObjects.size();) {
|
||||
if (!objectsAndGroups.HasObjectNamed(realObjects[i]) &&
|
||||
!globalObjectsAndGroups.HasObjectNamed(realObjects[i]))
|
||||
realObjects.erase(realObjects.begin() + i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
|
||||
return realObjects;
|
||||
}
|
||||
|
||||
void EventsCodeGenerator::DeleteUselessEvents(gd::EventsList& events) {
|
||||
for (std::size_t eId = events.size() - 1; eId < events.size(); --eId) {
|
||||
if (events[eId].CanHaveSubEvents()) // Process sub events, if any
|
||||
@@ -963,6 +909,8 @@ void EventsCodeGenerator::DeleteUselessEvents(gd::EventsList& events) {
|
||||
*/
|
||||
void EventsCodeGenerator::PreprocessEventList(gd::EventsList& listEvent) {
|
||||
for (std::size_t i = 0; i < listEvent.GetEventsCount(); ++i) {
|
||||
if (listEvent[i].IsDisabled()) continue;
|
||||
|
||||
listEvent[i].Preprocess(*this, listEvent, i);
|
||||
if (i <
|
||||
listEvent.GetEventsCount()) { // Be sure that that there is still an
|
||||
@@ -1260,12 +1208,24 @@ gd::String EventsCodeGenerator::GenerateArgumentsList(
|
||||
return argumentsStr;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
const gd::Layout& layout,
|
||||
const gd::Platform& platform_)
|
||||
: platform(platform_),
|
||||
globalObjectsAndGroups(project_),
|
||||
objectsAndGroups(layout),
|
||||
projectScopedContainers(gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project_, layout)),
|
||||
hasProjectAndLayout(true),
|
||||
project(&project_),
|
||||
scene(&layout),
|
||||
@@ -1277,11 +1237,9 @@ EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(
|
||||
const gd::Platform& platform_,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& objectsAndGroups_)
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_)
|
||||
: platform(platform_),
|
||||
globalObjectsAndGroups(globalObjectsAndGroups_),
|
||||
objectsAndGroups(objectsAndGroups_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
hasProjectAndLayout(false),
|
||||
project(nullptr),
|
||||
scene(nullptr),
|
||||
|
@@ -13,12 +13,14 @@
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
class Expression;
|
||||
class Project;
|
||||
class Layout;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class ExternalEvents;
|
||||
class ParameterMetadata;
|
||||
class ObjectMetadata;
|
||||
@@ -57,8 +59,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* objects/groups and platform
|
||||
*/
|
||||
EventsCodeGenerator(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups_,
|
||||
const gd::ObjectsContainer& objectsAndGroups_);
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
|
||||
/**
|
||||
@@ -326,18 +327,12 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*/
|
||||
bool ErrorOccurred() const { return errorOccurred; };
|
||||
|
||||
/**
|
||||
* \brief Get the global objects/groups used for code generation.
|
||||
*/
|
||||
const gd::ObjectsContainer& GetGlobalObjectsAndGroups() const {
|
||||
return globalObjectsAndGroups;
|
||||
}
|
||||
const gd::ObjectsContainersList& GetObjectsContainersList() const {
|
||||
return projectScopedContainers.GetObjectsContainersList();
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the objects/groups used for code generation.
|
||||
*/
|
||||
const gd::ObjectsContainer& GetObjectsAndGroups() const {
|
||||
return objectsAndGroups;
|
||||
const gd::ProjectScopedContainers& GetProjectScopedContainers() const {
|
||||
return projectScopedContainers;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -363,22 +358,6 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*/
|
||||
const gd::Platform& GetPlatform() const { return platform; }
|
||||
|
||||
/**
|
||||
* \brief Convert a group name to the full list of objects contained in the
|
||||
* group.
|
||||
*
|
||||
* Get a list containing the "real" objects name when the events refers to \a
|
||||
* objectName :<br> If \a objectName is really an object, the list will only
|
||||
* contains \a objectName unchanged.<br> If \a objectName is a group, the list
|
||||
* will contains all the objects of the group.<br> If \a objectName is the
|
||||
* "current" object in the context ( i.e: The object being used for launching
|
||||
* an action... ), none of the two rules below apply, and the list will only
|
||||
* contains the context "current" object name.
|
||||
*/
|
||||
std::vector<gd::String> ExpandObjectsName(
|
||||
const gd::String& objectName,
|
||||
const EventsCodeGenerationContext& context) const;
|
||||
|
||||
/**
|
||||
* \brief Get the maximum depth of custom conditions reached during code
|
||||
* generation.
|
||||
@@ -566,6 +545,10 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
return ".getChild(" + ConvertToStringExplicit(childName) + ")";
|
||||
};
|
||||
|
||||
virtual gd::String GenerateVariableValueAs(const gd::String& type) {
|
||||
return type == "string" ? ".getAsString()" : ".getAsNumber()";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Generate the code to get the child of a variable,
|
||||
* using generated the expression.
|
||||
@@ -594,6 +577,15 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
return "fakeObjectListOf_" + objectName;
|
||||
}
|
||||
|
||||
virtual gd::String GeneratePropertyGetter(const gd::PropertiesContainer& propertiesContainer,
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
virtual gd::String GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context);
|
||||
|
||||
/**
|
||||
* \brief Generate the code to reference an object which is
|
||||
* in an empty/null state.
|
||||
@@ -777,8 +769,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
|
||||
const gd::Platform& platform; ///< The platform being used.
|
||||
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups;
|
||||
const gd::ObjectsContainer& objectsAndGroups;
|
||||
gd::ProjectScopedContainers projectScopedContainers;
|
||||
|
||||
bool hasProjectAndLayout; ///< true only if project and layout are valid
|
||||
///< references. If false, they should not be used.
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/CodeGeneration/EventsCodeGenerationContext.h"
|
||||
@@ -25,6 +26,9 @@
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/VariablesContainersList.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
|
||||
|
||||
@@ -47,8 +51,7 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
}
|
||||
|
||||
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType);
|
||||
node->Visit(validator);
|
||||
if (!validator.GetFatalErrors().empty()) {
|
||||
@@ -100,34 +103,94 @@ void ExpressionCodeGenerator::OnVisitVariableNode(VariableNode& node) {
|
||||
// This "translation" from the type to an enum could be avoided
|
||||
// if all types were moved to an enum.
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootType,
|
||||
node);
|
||||
EventsCodeGenerator::VariableScope scope =
|
||||
type == "globalvar"
|
||||
? gd::EventsCodeGenerator::PROJECT_VARIABLE
|
||||
: ((type == "scenevar")
|
||||
? gd::EventsCodeGenerator::LAYOUT_VARIABLE
|
||||
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
rootObjectName,
|
||||
node);
|
||||
output += codeGenerator.GenerateGetVariable(
|
||||
node.name, scope, context, objectName);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// The node is a variable inside an expression waiting for a *variable* to be returned, not its value.
|
||||
EventsCodeGenerator::VariableScope scope =
|
||||
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);
|
||||
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.
|
||||
|
||||
codeGenerator.GetProjectScopedContainers().MatchIdentifierWithName<void>(node.name, [&](){
|
||||
// Generate the code to access the object variables.
|
||||
|
||||
// Defer generation of the access to the object and variable to the child,
|
||||
// once we know the name of the variable.
|
||||
objectNameToUseForVariableAccessor = node.name;
|
||||
if (node.child) node.child->Visit(*this);
|
||||
objectNameToUseForVariableAccessor = "";
|
||||
|
||||
output += codeGenerator.GenerateVariableValueAs(type);
|
||||
}, [&]() {
|
||||
if (!codeGenerator.HasProjectAndLayout()) {
|
||||
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now.");
|
||||
output += GenerateDefaultValue(type);
|
||||
return;
|
||||
}
|
||||
|
||||
// This could be adapted in the future if more scopes are supported.
|
||||
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
|
||||
if (codeGenerator.GetProjectScopedContainers().GetVariablesContainersList().GetBottomMostVariablesContainer()->Has(node.name)) {
|
||||
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
|
||||
}
|
||||
|
||||
output += codeGenerator.GenerateGetVariable(node.name, scope, context, "");
|
||||
if (node.child) node.child->Visit(*this);
|
||||
output += codeGenerator.GenerateVariableValueAs(type);
|
||||
}, [&]() {
|
||||
// Properties are not supported.
|
||||
output += GenerateDefaultValue(type);
|
||||
}, [&]() {
|
||||
// Parameters are not supported.
|
||||
output += GenerateDefaultValue(type);
|
||||
}, [&]() {
|
||||
// The identifier does not represents a variable (or a child variable), or not at least an existing
|
||||
// one, nor an object variable. It's invalid.
|
||||
output += GenerateDefaultValue(type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitVariableAccessorNode(
|
||||
VariableAccessorNode& node) {
|
||||
output += codeGenerator.GenerateVariableAccessor(node.name);
|
||||
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);
|
||||
|
||||
// We have accessed an object variable, from now we can continue accessing the child variables
|
||||
// (including using the bracket notation).
|
||||
objectNameToUseForVariableAccessor = "";
|
||||
} else {
|
||||
output += codeGenerator.GenerateVariableAccessor(node.name);
|
||||
}
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) {
|
||||
if (!objectNameToUseForVariableAccessor.empty()) {
|
||||
// Bracket notation can't be used to directly access a variable of an object (`MyObject["MyVariable"]`).
|
||||
// This would be rejected by the ExpressionValidator.
|
||||
output += codeGenerator.GenerateBadVariable();
|
||||
return;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
|
||||
node.expression->Visit(generator);
|
||||
output +=
|
||||
@@ -137,10 +200,10 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
output +=
|
||||
codeGenerator.GenerateObject(node.identifierName, type, context);
|
||||
@@ -153,8 +216,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
: gd::EventsCodeGenerator::OBJECT_VARIABLE);
|
||||
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootObjectName,
|
||||
node);
|
||||
output += codeGenerator.GenerateGetVariable(
|
||||
@@ -162,30 +224,60 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
if (!node.childIdentifierName.empty()) {
|
||||
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
|
||||
}
|
||||
} else if (node.childIdentifierName.empty()) {
|
||||
output += "/* Error during generation, unrecognized identifier type: " +
|
||||
codeGenerator.ConvertToString(type) + " with value " +
|
||||
codeGenerator.ConvertToString(node.identifierName) + " */ " +
|
||||
codeGenerator.ConvertToStringExplicit(node.identifierName);
|
||||
}
|
||||
else {
|
||||
// This is for function names that are put in IdentifierNode
|
||||
// because the type is needed to tell them apart from variables.
|
||||
output += GenerateDefaultValue(type);
|
||||
} else {
|
||||
const auto& variablesContainersList = codeGenerator.GetProjectScopedContainers().GetVariablesContainersList();
|
||||
const auto& propertiesContainersList = codeGenerator.GetProjectScopedContainers().GetPropertiesContainersList();
|
||||
const auto& parametersVectorsList = codeGenerator.GetProjectScopedContainers().GetParametersVectorsList();
|
||||
|
||||
// The node represents a variable, property, parameter or an object.
|
||||
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);
|
||||
output += codeGenerator.GenerateVariableValueAs(type);
|
||||
}, [&]() {
|
||||
if (!codeGenerator.HasProjectAndLayout()) {
|
||||
gd::LogWarning("Tried to generate access to a variable without a project/scene - the code generator only works for global and scene variables for now.");
|
||||
output += GenerateDefaultValue(type);
|
||||
return;
|
||||
}
|
||||
|
||||
// This could be adapted in the future if more scopes are supported at runtime.
|
||||
EventsCodeGenerator::VariableScope scope = gd::EventsCodeGenerator::PROJECT_VARIABLE;
|
||||
if (variablesContainersList.GetBottomMostVariablesContainer()->Has(node.identifierName)) {
|
||||
scope = gd::EventsCodeGenerator::LAYOUT_VARIABLE;
|
||||
}
|
||||
|
||||
output += codeGenerator.GenerateGetVariable(node.identifierName, scope, context, "");
|
||||
if (!node.childIdentifierName.empty()) {
|
||||
output += codeGenerator.GenerateVariableAccessor(node.childIdentifierName);
|
||||
}
|
||||
output += codeGenerator.GenerateVariableValueAs(type);
|
||||
}, [&]() {
|
||||
const auto& propertiesContainerAndProperty = propertiesContainersList.Get(node.identifierName);
|
||||
|
||||
output += codeGenerator.GeneratePropertyGetter(
|
||||
propertiesContainerAndProperty.first, propertiesContainerAndProperty.second, type, context);
|
||||
}, [&]() {
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
|
||||
output += codeGenerator.GenerateParameterGetter(parameter, type, context);
|
||||
}, [&]() {
|
||||
// The identifier does not represents a variable (or a child variable), or not at least an existing
|
||||
// one, nor an object variable. It's invalid.
|
||||
output += GenerateDefaultValue(type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
node);
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
@@ -236,11 +328,6 @@ gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
|
||||
const gd::String& objectName,
|
||||
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
|
||||
const ExpressionMetadata& expressionMetadata) {
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups =
|
||||
codeGenerator.GetGlobalObjectsAndGroups();
|
||||
const gd::ObjectsContainer& objectsAndGroups =
|
||||
codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionMetadata.GetIncludeFiles());
|
||||
|
||||
@@ -261,12 +348,11 @@ gd::String ExpressionCodeGenerator::GenerateObjectFunctionCode(
|
||||
|
||||
// Get object(s) concerned by function call
|
||||
std::vector<gd::String> realObjects =
|
||||
codeGenerator.ExpandObjectsName(objectName, context);
|
||||
codeGenerator.GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
for (std::size_t i = 0; i < realObjects.size(); ++i) {
|
||||
context.ObjectsListNeeded(realObjects[i]);
|
||||
|
||||
gd::String objectType = gd::GetTypeOfObject(
|
||||
globalObjectsAndGroups, objectsAndGroups, realObjects[i]);
|
||||
gd::String objectType = codeGenerator.GetObjectsContainersList().GetTypeOfObject(realObjects[i]);
|
||||
const ObjectMetadata& objInfo = MetadataProvider::GetObjectMetadata(
|
||||
codeGenerator.GetPlatform(), objectType);
|
||||
|
||||
@@ -288,11 +374,6 @@ gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
|
||||
const gd::String& behaviorName,
|
||||
const std::vector<std::unique_ptr<ExpressionNode>>& parameters,
|
||||
const ExpressionMetadata& expressionMetadata) {
|
||||
const gd::ObjectsContainer& globalObjectsAndGroups =
|
||||
codeGenerator.GetGlobalObjectsAndGroups();
|
||||
const gd::ObjectsContainer& objectsAndGroups =
|
||||
codeGenerator.GetObjectsAndGroups();
|
||||
|
||||
codeGenerator.AddIncludeFiles(
|
||||
expressionMetadata.GetIncludeFiles());
|
||||
|
||||
@@ -311,12 +392,11 @@ gd::String ExpressionCodeGenerator::GenerateBehaviorFunctionCode(
|
||||
|
||||
// Get object(s) concerned by function call
|
||||
std::vector<gd::String> realObjects =
|
||||
codeGenerator.ExpandObjectsName(objectName, context);
|
||||
codeGenerator.GetObjectsContainersList().ExpandObjectName(objectName, context.GetCurrentObject());
|
||||
|
||||
gd::String functionOutput = GenerateDefaultValue(type);
|
||||
|
||||
gd::String behaviorType = gd::GetTypeOfBehavior(
|
||||
globalObjectsAndGroups, objectsAndGroups, behaviorName);
|
||||
gd::String behaviorType = codeGenerator.GetObjectsContainersList().GetTypeOfBehavior(behaviorName);
|
||||
const BehaviorMetadata& autoInfo = MetadataProvider::GetBehaviorMetadata(
|
||||
codeGenerator.GetPlatform(), behaviorType);
|
||||
|
||||
@@ -352,8 +432,7 @@ gd::String ExpressionCodeGenerator::GenerateParametersCodes(
|
||||
if (!parameterMetadata.IsCodeOnly()) {
|
||||
if (nonCodeOnlyParameterIndex < parameters.size()) {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootObjectName,
|
||||
*parameters[nonCodeOnlyParameterIndex].get());
|
||||
ExpressionCodeGenerator generator(parameterMetadata.GetType(), objectName, codeGenerator, context);
|
||||
@@ -423,8 +502,7 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
@@ -433,8 +511,7 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
|
||||
ObjectFunctionNameNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetGlobalObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsAndGroups(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
|
@@ -102,6 +102,7 @@ class GD_CORE_API ExpressionCodeGenerator : public ExpressionParser2NodeWorker {
|
||||
const std::vector<std::unique_ptr<ExpressionNode>>& parameters);
|
||||
|
||||
gd::String output;
|
||||
gd::String objectNameToUseForVariableAccessor;
|
||||
EventsCodeGenerator& codeGenerator;
|
||||
EventsCodeGenerationContext& context;
|
||||
const gd::String rootType;
|
||||
|
@@ -240,7 +240,7 @@ class GD_CORE_API BaseEvent {
|
||||
*/
|
||||
virtual void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element){};
|
||||
|
||||
|
||||
virtual bool AcceptVisitor(gd::EventVisitor& eventVisitor);
|
||||
virtual void AcceptVisitor(gd::ReadOnlyEventVisitor& eventVisitor) const;
|
||||
///@}
|
||||
@@ -281,15 +281,6 @@ class GD_CORE_API BaseEvent {
|
||||
* \brief True if the event should be folded in the events editor.
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
/**
|
||||
* \brief Return a list of all objects linked to the event.
|
||||
*/
|
||||
virtual std::vector<gd::Expression*> GetAllObjectExpressions() {
|
||||
std::vector<gd::Expression*> allObjectExpressions;
|
||||
return allObjectExpressions;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
std::weak_ptr<gd::BaseEvent>
|
||||
|
@@ -47,14 +47,9 @@ class GD_CORE_API ExpressionParser2 {
|
||||
virtual ~ExpressionParser2(){};
|
||||
|
||||
/**
|
||||
* Parse the given expression with the specified type.
|
||||
* Parse the given expression into a tree of nodes.
|
||||
*
|
||||
* \param type Type of the expression: "string", "number",
|
||||
* type supported by gd::ParameterMetadata::IsObject, types supported by
|
||||
* gd::ParameterMetadata::IsExpression or "unknown".
|
||||
* \param expression The expression to parse
|
||||
* \param objectName Specify the object name, only for the
|
||||
* case of "objectvar" type.
|
||||
* \param expression The expression to parse.
|
||||
*
|
||||
* \return The node representing the expression as a parsed tree.
|
||||
*/
|
||||
@@ -267,12 +262,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
} else if (CheckIfChar(IsDot)) {
|
||||
ExpressionParserLocation dotLocation = SkipChar();
|
||||
SkipAllWhitespaces();
|
||||
return ObjectFunctionOrBehaviorFunction(
|
||||
return ObjectFunctionOrBehaviorFunctionOrVariable(
|
||||
name, nameLocation, dotLocation);
|
||||
} else if (CheckIfChar(IsOpeningSquareBracket)) {
|
||||
return Variable(name, nameLocation);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
auto identifier = gd::make_unique<IdentifierNode>(name);
|
||||
identifier->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -318,7 +312,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
auto dotLocation = SkipChar();
|
||||
SkipAllWhitespaces();
|
||||
|
||||
auto identifierAndLocation = ReadIdentifierName();
|
||||
auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
@@ -358,11 +352,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
}
|
||||
|
||||
std::unique_ptr<IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode>
|
||||
ObjectFunctionOrBehaviorFunction(
|
||||
ObjectFunctionOrBehaviorFunctionOrVariable(
|
||||
const gd::String &parentIdentifier,
|
||||
const ExpressionParserLocation &parentIdentifierLocation,
|
||||
const ExpressionParserLocation &parentIdentifierDotLocation) {
|
||||
auto childIdentifierAndLocation = ReadIdentifierName();
|
||||
auto childIdentifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
|
||||
const gd::String &childIdentifierName = childIdentifierAndLocation.name;
|
||||
const auto &childIdentifierNameLocation =
|
||||
childIdentifierAndLocation.location;
|
||||
@@ -420,12 +414,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
auto node = gd::make_unique<IdentifierNode>(
|
||||
parentIdentifier, childIdentifierName);
|
||||
if (!CheckIfChar(IsParameterSeparator) && !CheckIfChar(IsClosingParenthesis) && !IsEndReached()) {
|
||||
node->diagnostic = RaiseSyntaxError(
|
||||
_("An opening parenthesis (for an object expression), a double colon "
|
||||
"(:: for a behavior expression), a dot or an opening bracket (for "
|
||||
"a child variable) where expected."));
|
||||
}
|
||||
node->location = ExpressionParserLocation(
|
||||
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
node->identifierNameLocation = parentIdentifierLocation;
|
||||
@@ -625,13 +613,13 @@ class GD_CORE_API ExpressionParser2 {
|
||||
ExpressionParserLocation location;
|
||||
};
|
||||
|
||||
IdentifierAndLocation ReadIdentifierName() {
|
||||
IdentifierAndLocation ReadIdentifierName(bool allowDeprecatedSpacesInName = true) {
|
||||
gd::String name;
|
||||
size_t startPosition = currentPosition;
|
||||
while (currentPosition < expression.size() &&
|
||||
(CheckIfChar(IsAllowedInIdentifier)
|
||||
// Allow whitespace in identifier name for compatibility
|
||||
|| expression[currentPosition] == ' ')) {
|
||||
|| (allowDeprecatedSpacesInName && expression[currentPosition] == ' '))) {
|
||||
name += expression[currentPosition];
|
||||
currentPosition++;
|
||||
}
|
||||
|
@@ -189,7 +189,7 @@ struct GD_CORE_API IdentifierOrFunctionCallOrObjectFunctionNameOrEmptyNode
|
||||
*
|
||||
* The name of a function to call on an object or the behavior,
|
||||
* for example: "MyObject.Function" or "MyObject.Physics".
|
||||
*
|
||||
*
|
||||
* A variable, potentially with accessor to its child,
|
||||
* for example: MyVariable or MyVariable.MyChild
|
||||
*/
|
||||
@@ -239,13 +239,14 @@ struct GD_CORE_API VariableAccessorOrVariableBracketAccessorNode : public Expres
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A variable with bracket accessor or at least 2 "dot" accessors.
|
||||
* \brief A variable, or object variable, with bracket accessor or at least 2 "dot" accessors.
|
||||
*
|
||||
* Example: `MyVariable["MyChildren"]` or `MyVariable.MyChildren.MyGrandChildren`.
|
||||
* Example: `MyObject["MyVariable"]` or `MyObject.MyVariable.MyChildren`.
|
||||
*
|
||||
* Example: MyVariable[MyChildren] or MyVariable.MyChildren.MyGranChildren.
|
||||
*
|
||||
* Other cases like "MyVariable" or "MyVariable.MyChildren" are IdentifierNode
|
||||
* to allow handling ambiguities.
|
||||
*
|
||||
*
|
||||
* \see gd::IdentifierNode
|
||||
* \see gd::VariableAccessorNode
|
||||
* \see gd::VariableBracketAccessorNode
|
||||
@@ -267,7 +268,7 @@ struct GD_CORE_API VariableNode : public FunctionCallOrObjectFunctionNameOrEmpty
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A bracket accessor of a variable. Example: MyChild
|
||||
* \brief A direct accessor to a child variable. Example: MyChild
|
||||
* in MyVariable.MyChild
|
||||
*/
|
||||
struct GD_CORE_API VariableAccessorNode
|
||||
@@ -285,7 +286,7 @@ struct GD_CORE_API VariableAccessorNode
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A bracket accessor of a variable. Example: ["MyChild"]
|
||||
* \brief A bracket accessor to a child variable. Example: ["MyChild"]
|
||||
* (in MyVariable["MyChild"]).
|
||||
*/
|
||||
struct GD_CORE_API VariableBracketAccessorNode
|
||||
@@ -303,10 +304,10 @@ struct GD_CORE_API VariableBracketAccessorNode
|
||||
/**
|
||||
* \brief The name of a function to call on an object or the behavior
|
||||
* For example: "MyObject.Physics::LinearVelocity".
|
||||
*
|
||||
*
|
||||
* Other cases like "MyObject.Function" or "MyObject.Physics" are IdentifierNode
|
||||
* to allow handling ambiguities.
|
||||
*
|
||||
*
|
||||
* \see gd::IdentifierNode
|
||||
*/
|
||||
struct GD_CORE_API ObjectFunctionNameNode
|
||||
|
@@ -75,10 +75,21 @@ inline bool IsZeroDigit(gd::String::value_type character) {
|
||||
return character == '0';
|
||||
}
|
||||
|
||||
inline bool IsAdditionalReservedCharacter(gd::String::value_type character) {
|
||||
// These characters are not part of the grammar - but are often used in programming language
|
||||
// and could become operators or part of the grammar one day.
|
||||
return character == '~' || character == '\'' || character == '%' ||
|
||||
character == '#' || character == '@' || character == '|' ||
|
||||
character == '&' || character == '`' || character == '$' ||
|
||||
character == ';';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given character can be used in an identifier. This is
|
||||
* any unicode character, except for:
|
||||
* `, . " () [] {} + - < > ? ^ = \ : ! / *` and whitespaces (space, line break, carriage return).
|
||||
* `, . " () [] {} + - < > ? ^ = \ : ! / * ~ ' % # @ | & $ ;`
|
||||
* and backtick and whitespaces (space, line break, carriage return).
|
||||
*
|
||||
* This is loosely based on what is allowed in languages like JavaScript
|
||||
* (see https://mathiasbynens.be/notes/javascript-properties), without support
|
||||
@@ -96,7 +107,7 @@ inline bool IsAllowedInIdentifier(gd::String::value_type character) {
|
||||
if (!IsParameterSeparator(character) && !IsDot(character) &&
|
||||
!IsQuote(character) && !IsBracket(character) &&
|
||||
!IsExpressionOperator(character) && !IsTermOperator(character) &&
|
||||
!IsWhitespace(character)) {
|
||||
!IsWhitespace(character) && !IsAdditionalReservedCharacter(character)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -1277,9 +1277,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
// Deprecated
|
||||
obj.AddAction("SetEffectDoubleParameter",
|
||||
_("Effect parameter (number)"),
|
||||
_("Change the value of a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (number)"),
|
||||
_("Change the value of a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
@@ -1287,17 +1287,17 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"res/actions/effect.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("expression", _("New value"))
|
||||
.MarkAsSimple()
|
||||
.SetHidden();
|
||||
|
||||
// Deprecated
|
||||
obj.AddAction("SetEffectStringParameter",
|
||||
_("Effect parameter (string)"),
|
||||
_("Change the value (string) of a parameter of an effect.") +
|
||||
_("Effect property (string)"),
|
||||
_("Change the value (string) of a property of an effect.") +
|
||||
"\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
@@ -1305,16 +1305,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"res/actions/effect.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("string", _("New value"))
|
||||
.MarkAsSimple()
|
||||
.SetHidden();
|
||||
|
||||
// Deprecated
|
||||
obj.AddAction("SetEffectBooleanParameter",
|
||||
_("Effect parameter (enable or disable)"),
|
||||
_("Enable or disable a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (enable or disable)"),
|
||||
_("Enable or disable a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"),
|
||||
_("Effects"),
|
||||
@@ -1322,8 +1322,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"res/actions/effect.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("yesorno", _("Enable?"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("yesorno", _("Enable this property"))
|
||||
.MarkAsSimple()
|
||||
.SetHidden();
|
||||
|
||||
|
@@ -444,9 +444,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerEffectParameter",
|
||||
_("Effect parameter (number)"),
|
||||
_("Change the value of a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (number)"),
|
||||
_("Change the value of a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
|
||||
_("Effects"),
|
||||
@@ -456,16 +456,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("layerEffectName", _("Effect name"))
|
||||
.AddParameter("layerEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("layerEffectParameterName", _("Property name"))
|
||||
.AddParameter("expression", _("New value"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerEffectStringParameter",
|
||||
_("Effect parameter (string)"),
|
||||
_("Change the value (string) of a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (string)"),
|
||||
_("Change the value (string) of a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
|
||||
_("Effects"),
|
||||
@@ -475,16 +475,16 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("layerEffectName", _("Effect name"))
|
||||
.AddParameter("layerEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("layerEffectParameterName", _("Property name"))
|
||||
.AddParameter("string", _("New value"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerEffectBooleanParameter",
|
||||
_("Effect parameter (enable or disable)"),
|
||||
_("Enable or disable a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (enable or disable)"),
|
||||
_("Enable or disable a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM3_ for effect _PARAM2_ of layer _PARAM1_: _PARAM4_"),
|
||||
_("Effects"),
|
||||
@@ -494,8 +494,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("layerEffectName", _("Effect name"))
|
||||
.AddParameter("layerEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("yesorno", _("Enable this parameter"))
|
||||
.AddParameter("layerEffectParameterName", _("Property name"))
|
||||
.AddParameter("yesorno", _("Enable this property"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
|
@@ -59,7 +59,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"Name",
|
||||
_("Animation (by name)"),
|
||||
_("the animation played by the object using the name of the "
|
||||
"animation."),
|
||||
"animation"),
|
||||
_("the animation"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
@@ -72,7 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
|
||||
aut.AddScopedAction("PauseAnimation",
|
||||
_("Pause the animation"),
|
||||
_("Pause the animation of the object"),
|
||||
_("Pause the animation of the object."),
|
||||
_("Pause the animation of _PARAM0_"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png",
|
||||
@@ -83,7 +83,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
|
||||
aut.AddScopedAction("PlayAnimation",
|
||||
_("Resume the animation"),
|
||||
_("Resume the animation of the object"),
|
||||
_("Resume the animation of the object."),
|
||||
_("Resume the animation of _PARAM0_"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png",
|
||||
|
@@ -52,9 +52,9 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddScopedAction("SetEffectDoubleParameter",
|
||||
_("Effect parameter (number)"),
|
||||
_("Change the value of a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (number)"),
|
||||
_("Change the value of a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
@@ -63,15 +63,15 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("expression", _("New value"))
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddScopedAction("SetEffectStringParameter",
|
||||
_("Effect parameter (string)"),
|
||||
_("Change the value (string) of a parameter of an effect.") +
|
||||
_("Effect property (string)"),
|
||||
_("Change the value (string) of a property of an effect.") +
|
||||
"\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
@@ -80,14 +80,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("string", _("New value"))
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddScopedAction("SetEffectBooleanParameter",
|
||||
_("Effect parameter (enable or disable)"),
|
||||
_("Enable or disable a parameter of an effect.") + "\n" +
|
||||
_("You can find the parameter names (and change the effect "
|
||||
_("Effect property (enable or disable)"),
|
||||
_("Enable or disable a property of an effect.") + "\n" +
|
||||
_("You can find the property names (and change the effect "
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM3_ for effect _PARAM2_ of _PARAM0_: _PARAM4_"),
|
||||
_("Effects"),
|
||||
@@ -96,8 +96,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Parameter name"))
|
||||
.AddParameter("yesorno", _("Enable?"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
.AddParameter("yesorno", _("Enable this property"))
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddScopedCondition("IsEffectEnabled",
|
||||
|
@@ -14,7 +14,7 @@ using namespace std;
|
||||
namespace gd {
|
||||
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
gd::PlatformExtension &extension) {
|
||||
extension
|
||||
.SetExtensionInformation("ResizableCapability",
|
||||
_("Resizable capability"),
|
||||
@@ -22,84 +22,86 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsResizableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"ResizableBehavior",
|
||||
_("Resizable capability"),
|
||||
"Resizable",
|
||||
_("Change the object dimensions."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
gd::BehaviorMetadata &aut =
|
||||
extension
|
||||
.AddBehavior("ResizableBehavior",
|
||||
_("Resizable capability"),
|
||||
"Resizable",
|
||||
_("Change the object dimensions."),
|
||||
"",
|
||||
"res/actions/scale24_black.png",
|
||||
"ResizableBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddScopedAction("SetWidth",
|
||||
_("Width"),
|
||||
_("Change the width of the object."),
|
||||
_("the width"),
|
||||
_("Size"),
|
||||
"res/actions/scaleWidth24_black.png",
|
||||
"res/actions/scaleWidth_black.png")
|
||||
_("Width"),
|
||||
_("Change the width of the object."),
|
||||
_("the width"),
|
||||
_("Size"),
|
||||
"res/actions/scaleWidth24_black.png",
|
||||
"res/actions/scaleWidth_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
|
||||
.UseStandardOperatorParameters("number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Width")))
|
||||
.UseStandardOperatorParameters(
|
||||
"number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(_("Width")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
aut.AddScopedCondition("Width",
|
||||
_("Width"),
|
||||
_("Compare the width of the object."),
|
||||
_("the width"),
|
||||
_("Size"),
|
||||
"res/conditions/scaleWidth24_black.png",
|
||||
"res/conditions/scaleWidth_black.png")
|
||||
_("Width"),
|
||||
_("Compare the width of the object."),
|
||||
_("the width"),
|
||||
_("Size"),
|
||||
"res/conditions/scaleWidth24_black.png",
|
||||
"res/conditions/scaleWidth_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Width")))
|
||||
"number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(_("Width")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
aut.AddScopedAction("SetHeight",
|
||||
_("Height"),
|
||||
_("Change the height of the object."),
|
||||
_("the height"),
|
||||
_("Size"),
|
||||
"res/actions/scaleHeight24_black.png",
|
||||
"res/actions/scaleHeight_black.png")
|
||||
_("Height"),
|
||||
_("Change the height of the object."),
|
||||
_("the height"),
|
||||
_("Size"),
|
||||
"res/actions/scaleHeight24_black.png",
|
||||
"res/actions/scaleHeight_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
|
||||
.UseStandardOperatorParameters("number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Height")))
|
||||
.UseStandardOperatorParameters(
|
||||
"number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(_("Height")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
aut.AddScopedCondition("Height",
|
||||
_("Height"),
|
||||
_("Compare the height of the object."),
|
||||
_("the height"),
|
||||
_("Size"),
|
||||
"res/conditions/scaleHeight24_black.png",
|
||||
"res/conditions/scaleHeight_black.png")
|
||||
_("Height"),
|
||||
_("Compare the height of the object."),
|
||||
_("the height"),
|
||||
_("Size"),
|
||||
"res/conditions/scaleHeight24_black.png",
|
||||
"res/conditions/scaleHeight_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Height")))
|
||||
"number",
|
||||
ParameterOptions::MakeNewOptions().SetDescription(_("Height")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
aut.AddScopedAction("SetSize",
|
||||
_("Size"),
|
||||
_("Change the size of an object."),
|
||||
_("Change the size of _PARAM0_: set to _PARAM1_ x _PARAM2_"),
|
||||
_("Size"),
|
||||
"res/actions/scale24_black.png",
|
||||
"res/actions/scale_black.png")
|
||||
aut.AddScopedAction(
|
||||
"SetSize",
|
||||
_("Size"),
|
||||
_("Change the size of an object."),
|
||||
_("Change the size of _PARAM0_: set to _PARAM2_ x _PARAM3_"),
|
||||
_("Size"),
|
||||
"res/actions/scale24_black.png",
|
||||
"res/actions/scale_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ResizableBehavior")
|
||||
.AddParameter("expression", _("Width"))
|
||||
|
@@ -61,7 +61,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("the scale on X axis of the object (default scale is 1)"),
|
||||
_("the scale on X axis"),
|
||||
_("Scale"),
|
||||
"res/actions/scale24_black.png")
|
||||
"res/actions/scaleWidth24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
.UseStandardParameters(
|
||||
@@ -77,7 +77,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("the scale on Y axis of the object (default scale is 1)"),
|
||||
_("the scale on Y axis"),
|
||||
_("Scale"),
|
||||
"res/actions/scale24_black.png")
|
||||
"res/actions/scaleHeight24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
.UseStandardParameters(
|
||||
|
@@ -35,6 +35,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Events and control flow"))
|
||||
.SetIcon("res/conditions/toujours24_black.png");
|
||||
|
||||
// This condition is deprecated as this does not bring anything new
|
||||
// and can be confusing or misleading for beginners.
|
||||
extension
|
||||
.AddCondition("Always",
|
||||
_("Always"),
|
||||
@@ -46,7 +48,8 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
"res/conditions/toujours_black.png")
|
||||
.SetHelpPath("/all-features/advanced-conditions")
|
||||
.AddCodeOnlyParameter("conditionInverted", "")
|
||||
.MarkAsAdvanced();
|
||||
.MarkAsAdvanced()
|
||||
.SetHidden();
|
||||
|
||||
// Compatibility with GD <= 5.0.127
|
||||
extension
|
||||
@@ -114,8 +117,9 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
|
||||
// Compatibility with GD <= 5.0.127
|
||||
extension
|
||||
.AddDuplicatedCondition(
|
||||
"Egal", "BuiltinCommonInstructions::CompareNumbers", {.unscoped = true})
|
||||
.AddDuplicatedCondition("Egal",
|
||||
"BuiltinCommonInstructions::CompareNumbers",
|
||||
{.unscoped = true})
|
||||
.SetHidden();
|
||||
// end of compatibility code
|
||||
|
||||
@@ -135,8 +139,9 @@ BuiltinExtensionsImplementer::ImplementsCommonInstructionsExtension(
|
||||
|
||||
// Compatibility with GD <= 5.0.127
|
||||
extension
|
||||
.AddDuplicatedCondition(
|
||||
"StrEqual", "BuiltinCommonInstructions::CompareStrings", {.unscoped = true})
|
||||
.AddDuplicatedCondition("StrEqual",
|
||||
"BuiltinCommonInstructions::CompareStrings",
|
||||
{.unscoped = true})
|
||||
.SetHidden();
|
||||
// end of compatibility code
|
||||
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
@@ -391,20 +392,19 @@ MetadataProvider::GetBehaviorAnyExpressionMetadata(const gd::Platform& platform,
|
||||
}
|
||||
|
||||
const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& node) {
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
gd::String behaviorType =
|
||||
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, node.behaviorName);
|
||||
gd::String behaviorType =
|
||||
objectsContainersList.GetTypeOfBehavior(node.behaviorName);
|
||||
return MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, node.functionName);
|
||||
}
|
||||
else if (!node.objectName.empty()) {
|
||||
gd::String objectType =
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, node.objectName);
|
||||
gd::String objectType =
|
||||
objectsContainersList.GetTypeOfObject(node.objectName);
|
||||
return MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, node.functionName);
|
||||
}
|
||||
@@ -412,10 +412,9 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
|
||||
return MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& functionCall,
|
||||
ExpressionNode& parameter) {
|
||||
int parameterIndex = -1;
|
||||
@@ -429,17 +428,15 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
|
||||
return nullptr;
|
||||
}
|
||||
return MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
platform,
|
||||
objectsContainersList,
|
||||
functionCall,
|
||||
parameterIndex);
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ParameterMetadata* MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& functionCall,
|
||||
int parameterIndex) {
|
||||
// Search the parameter metadata index skipping invisible ones.
|
||||
@@ -448,7 +445,7 @@ const gd::ExpressionMetadata& MetadataProvider::GetFunctionCallMetadata(
|
||||
ExpressionParser2::WrittenParametersFirstIndex(
|
||||
functionCall.objectName, functionCall.behaviorName);
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, functionCall);
|
||||
platform, objectsContainersList, functionCall);
|
||||
|
||||
if (IsBadExpressionMetadata(metadata)) {
|
||||
return nullptr;
|
||||
|
@@ -15,6 +15,7 @@ class ExpressionMetadata;
|
||||
class ExpressionMetadata;
|
||||
class Platform;
|
||||
class PlatformExtension;
|
||||
class ObjectsContainersList;
|
||||
struct FunctionCallNode;
|
||||
struct ExpressionNode;
|
||||
} // namespace gd
|
||||
@@ -237,22 +238,19 @@ class GD_CORE_API MetadataProvider {
|
||||
const gd::Platform& platform, gd::String objectType, gd::String exprType);
|
||||
|
||||
static const gd::ExpressionMetadata& GetFunctionCallMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& node);
|
||||
|
||||
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& functionCall,
|
||||
ExpressionNode& parameter);
|
||||
|
||||
static const gd::ParameterMetadata* GetFunctionCallParameterMetadata(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
FunctionCallNode& functionCall,
|
||||
int parameterIndex);
|
||||
|
||||
|
@@ -34,6 +34,11 @@ class GD_CORE_API ParameterMetadata {
|
||||
*/
|
||||
gd::ValueTypeMetadata &GetValueTypeMetadata() { return valueTypeMetadata; }
|
||||
|
||||
/**
|
||||
* \brief Return the metadata of the parameter type.
|
||||
*/
|
||||
const gd::ValueTypeMetadata &GetValueTypeMetadata() const { return valueTypeMetadata; }
|
||||
|
||||
/**
|
||||
* \brief Set the metadata of the parameter type.
|
||||
*/
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "InstructionMetadata.h"
|
||||
@@ -15,6 +16,8 @@
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
|
||||
namespace gd {
|
||||
const ParameterMetadata ParameterMetadataTools::badParameterMetadata;
|
||||
|
||||
void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
const gd::Project& project,
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
@@ -57,6 +60,55 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName().find(prefix) == 0) cb(parameterMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ParameterMetadataTools::Has(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& parameterName) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName() == parameterName) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const gd::ParameterMetadata& ParameterMetadataTools::Get(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& parameterName) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
++it) {
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName() == parameterName) return parameterMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
return badParameterMetadata;
|
||||
}
|
||||
|
||||
void ParameterMetadataTools::IterateOverParameters(
|
||||
const std::vector<gd::Expression>& parameters,
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
@@ -105,8 +157,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
|
||||
void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer, FunctionCallNode &node,
|
||||
const gd::ObjectsContainersList &objectsContainersList, FunctionCallNode &node,
|
||||
std::function<void(const gd::ParameterMetadata ¶meterMetadata,
|
||||
std::unique_ptr<gd::ExpressionNode> ¶meterNode,
|
||||
size_t parameterIndex, const gd::String &lastObjectName)>
|
||||
@@ -117,8 +168,7 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
const gd::ExpressionMetadata &metadata =
|
||||
isObjectFunction ? MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform,
|
||||
GetTypeOfObject(globalObjectsContainer,
|
||||
objectsContainer, node.objectName),
|
||||
objectsContainersList.GetTypeOfObject(node.objectName),
|
||||
node.functionName)
|
||||
: MetadataProvider::GetAnyExpressionMetadata(
|
||||
platform, node.functionName);
|
||||
|
@@ -3,9 +3,8 @@
|
||||
* 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)
|
||||
#ifndef ParameterMetadataTools_H
|
||||
#define ParameterMetadataTools_H
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "GDCore/String.h"
|
||||
@@ -13,6 +12,7 @@ namespace gd {
|
||||
class Platform;
|
||||
class Project;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class ParameterMetadata;
|
||||
class Expression;
|
||||
struct FunctionCallNode;
|
||||
@@ -27,6 +27,19 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
|
||||
static void ForEachParameterWithPrefix(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb);
|
||||
|
||||
static bool Has(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& parameterName);
|
||||
|
||||
static const gd::ParameterMetadata& Get(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& parameterName);
|
||||
|
||||
/**
|
||||
* Iterate over a list of parameters and their values.
|
||||
* Callback function is called with the parameter metadata, its value
|
||||
@@ -59,8 +72,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
*/
|
||||
static void IterateOverParametersWithIndex(
|
||||
const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer, FunctionCallNode &node,
|
||||
const gd::ObjectsContainersList &objectsContainersList, FunctionCallNode &node,
|
||||
std::function<void(const gd::ParameterMetadata ¶meterMetadata,
|
||||
std::unique_ptr<gd::ExpressionNode> ¶meterNode,
|
||||
size_t parameterIndex,
|
||||
@@ -74,8 +86,8 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
static size_t GetObjectParameterIndexFor(
|
||||
const std::vector<gd::ParameterMetadata>& parametersMetadata,
|
||||
size_t parameterIndex);
|
||||
|
||||
private:
|
||||
static const gd::ParameterMetadata badParameterMetadata;
|
||||
};
|
||||
} // namespace gd
|
||||
|
||||
#endif // ParameterMetadataTools_H
|
||||
#endif
|
||||
|
@@ -121,6 +121,13 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("string", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is a boolean.
|
||||
*/
|
||||
bool IsBoolean() const {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("boolean", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type of the parameter is a number.
|
||||
* \note If you had a new type of parameter, also add it in the IDE (
|
||||
@@ -131,6 +138,24 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
return gd::ValueTypeMetadata::IsTypeExpression("variable", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \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"
|
||||
* parameter (which accepts any variable coming from an object or from containers in the scope).
|
||||
*/
|
||||
bool IsLegacyPreScopedVariable() const {
|
||||
return gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* \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"
|
||||
* parameter (which accepts any variable coming from an object or from containers in the scope).
|
||||
*/
|
||||
static bool IsTypeLegacyPreScopedVariable(const gd::String &type) {
|
||||
return type == "scenevar" || type == "globalvar" || type == "objectvar";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the type is representing one object
|
||||
* (or more, i.e: an object group).
|
||||
@@ -175,8 +200,13 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
parameterType == "externalLayoutName" ||
|
||||
parameterType == "leaderboardId" ||
|
||||
parameterType == "identifier";
|
||||
} else if (type == "boolean") {
|
||||
return parameterType == "yesorno" || parameterType == "trueorfalse";
|
||||
} else if (type == "variable") {
|
||||
return parameterType == "objectvar" || parameterType == "globalvar" ||
|
||||
return
|
||||
parameterType == "variable" || // Any variable.
|
||||
// Old, "pre-scoped" variables:
|
||||
parameterType == "objectvar" || parameterType == "globalvar" ||
|
||||
parameterType == "scenevar";
|
||||
} else if (type == "resource") {
|
||||
return parameterType == "fontResource" ||
|
||||
@@ -196,7 +226,7 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
* \brief Return the expression type from the parameter type.
|
||||
* Declinations of "number" and "string" types (like "forceMultiplier" or
|
||||
* "sceneName") are replaced by "number" and "string".
|
||||
*
|
||||
*
|
||||
* \note It only maps string and number types.
|
||||
*/
|
||||
static const gd::String &GetExpressionPrimitiveValueType(const gd::String ¶meterType);
|
||||
@@ -205,7 +235,7 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
* \brief Return the primitive type from the parameter type.
|
||||
* Declinations of "number" and "string" types (like "forceMultiplier" or
|
||||
* "sceneName") are replaced by "number" and "string".
|
||||
*
|
||||
*
|
||||
* \note It also maps variable and boolean types.
|
||||
*/
|
||||
static const gd::String &GetPrimitiveValueType(const gd::String ¶meterType);
|
||||
|
@@ -11,6 +11,8 @@
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -47,7 +49,14 @@ bool ArbitraryEventsWorker::VisitEvent(gd::BaseEvent& event) {
|
||||
for (std::size_t j = 0; j < actionsVectors.size(); ++j)
|
||||
VisitInstructionList(*actionsVectors[j], false);
|
||||
|
||||
return false;
|
||||
auto allExpressionsWithMetadata = event.GetAllExpressionsWithMetadata();
|
||||
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
|
||||
shouldDelete |= VisitEventExpression(
|
||||
*expressionAndMetadata.first, expressionAndMetadata.second);
|
||||
}
|
||||
|
||||
|
||||
return shouldDelete;
|
||||
}
|
||||
|
||||
bool ArbitraryEventsWorker::VisitLinkEvent(gd::LinkEvent& linkEvent) {
|
||||
@@ -75,6 +84,11 @@ bool ArbitraryEventsWorker::VisitInstruction(gd::Instruction& instruction,
|
||||
return DoVisitInstruction(instruction, isCondition);
|
||||
}
|
||||
|
||||
bool ArbitraryEventsWorker::VisitEventExpression(gd::Expression& expression,
|
||||
const gd::ParameterMetadata& metadata) {
|
||||
return DoVisitEventExpression(expression, metadata);
|
||||
}
|
||||
|
||||
ArbitraryEventsWorkerWithContext::~ArbitraryEventsWorkerWithContext() {}
|
||||
|
||||
|
||||
@@ -141,6 +155,12 @@ void ReadOnlyArbitraryEventsWorker::VisitInstruction(const gd::Instruction& inst
|
||||
DoVisitInstruction(instruction, isCondition);
|
||||
}
|
||||
|
||||
|
||||
void ReadOnlyArbitraryEventsWorker::VisitEventExpression(const gd::Expression& expression,
|
||||
const gd::ParameterMetadata& metadata) {
|
||||
DoVisitEventExpression(expression, metadata);
|
||||
}
|
||||
|
||||
void ReadOnlyArbitraryEventsWorker::StopAnyEventIteration() {
|
||||
shouldStopIteration = true;
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
#include "GDCore/Events/InstructionsList.h"
|
||||
#include "GDCore/Events/EventVisitor.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Instruction;
|
||||
@@ -17,6 +18,8 @@ class BaseEvent;
|
||||
class LinkEvent;
|
||||
class EventsList;
|
||||
class ObjectsContainer;
|
||||
class Expression;
|
||||
class ParameterMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -47,6 +50,7 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
|
||||
void VisitInstructionList(gd::InstructionsList& instructions,
|
||||
bool areConditions);
|
||||
bool VisitInstruction(gd::Instruction& instruction, bool isCondition);
|
||||
bool VisitEventExpression(gd::Expression& expression, const gd::ParameterMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Called to do some work on an event list.
|
||||
@@ -62,9 +66,9 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
|
||||
|
||||
/**
|
||||
* Called to do some work on a link event.
|
||||
*
|
||||
*
|
||||
* Note that DoVisitEvent is also called with this event.
|
||||
*
|
||||
*
|
||||
* \return true if the event must be deleted from the events list, false
|
||||
* otherwise (default).
|
||||
*/
|
||||
@@ -85,6 +89,16 @@ class GD_CORE_API ArbitraryEventsWorker : private EventVisitor {
|
||||
bool isCondition) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called to do some work on an expression of an event.
|
||||
* \return true if the event must be deleted from the list, false
|
||||
* otherwise (default).
|
||||
*/
|
||||
virtual bool DoVisitEventExpression(gd::Expression& expression,
|
||||
const gd::ParameterMetadata& metadata) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,8 +113,7 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
|
||||
: public ArbitraryEventsWorker {
|
||||
public:
|
||||
ArbitraryEventsWorkerWithContext()
|
||||
: currentGlobalObjectsContainer(nullptr),
|
||||
currentObjectsContainer(nullptr){};
|
||||
: projectScopedContainers(nullptr){};
|
||||
virtual ~ArbitraryEventsWorkerWithContext();
|
||||
|
||||
/**
|
||||
@@ -108,30 +121,27 @@ class GD_CORE_API ArbitraryEventsWorkerWithContext
|
||||
* giving the objects container on which the events are applying to.
|
||||
*/
|
||||
void Launch(gd::EventsList& events,
|
||||
const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_) {
|
||||
currentGlobalObjectsContainer = &globalObjectsContainer_;
|
||||
currentObjectsContainer = &objectsContainer_;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_) {
|
||||
projectScopedContainers = &projectScopedContainers_;
|
||||
ArbitraryEventsWorker::Launch(events);
|
||||
};
|
||||
|
||||
void Launch(gd::EventsList& events) = delete;
|
||||
|
||||
protected:
|
||||
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
|
||||
const gd::ProjectScopedContainers& GetProjectScopedContainers() {
|
||||
// Pointers are guaranteed to be not nullptr after
|
||||
// Launch was called.
|
||||
return *currentGlobalObjectsContainer;
|
||||
return *projectScopedContainers;
|
||||
};
|
||||
const gd::ObjectsContainer& GetObjectsContainer() {
|
||||
const gd::ObjectsContainersList& GetObjectsContainersList() {
|
||||
// Pointers are guaranteed to be not nullptr after
|
||||
// Launch was called.
|
||||
return *currentObjectsContainer;
|
||||
return projectScopedContainers->GetObjectsContainersList();
|
||||
};
|
||||
|
||||
private:
|
||||
const gd::ObjectsContainer* currentGlobalObjectsContainer;
|
||||
const gd::ObjectsContainer* currentObjectsContainer;
|
||||
const gd::ProjectScopedContainers* projectScopedContainers;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -162,6 +172,7 @@ protected:
|
||||
void VisitInstructionList(const gd::InstructionsList& instructions,
|
||||
bool areConditions);
|
||||
void VisitInstruction(const gd::Instruction& instruction, bool isCondition);
|
||||
void VisitEventExpression(const gd::Expression& expression, const gd::ParameterMetadata& metadata);
|
||||
|
||||
/**
|
||||
* Called to do some work on an event list.
|
||||
@@ -175,7 +186,7 @@ protected:
|
||||
|
||||
/**
|
||||
* Called to do some work on a link event.
|
||||
*
|
||||
*
|
||||
* Note that DoVisitEvent is also called with this event.
|
||||
*/
|
||||
virtual void DoVisitLinkEvent(const gd::LinkEvent& linkEvent) {};
|
||||
@@ -192,6 +203,13 @@ protected:
|
||||
virtual void DoVisitInstruction(const gd::Instruction& instruction,
|
||||
bool isCondition) {};
|
||||
|
||||
/**
|
||||
* Called to do some work on an expression of an event.
|
||||
*/
|
||||
virtual void DoVisitEventExpression(const gd::Expression& expression,
|
||||
const gd::ParameterMetadata& metadata) {
|
||||
}
|
||||
|
||||
bool shouldStopIteration;
|
||||
};
|
||||
|
||||
@@ -207,8 +225,7 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
|
||||
: public ReadOnlyArbitraryEventsWorker {
|
||||
public:
|
||||
ReadOnlyArbitraryEventsWorkerWithContext()
|
||||
: currentGlobalObjectsContainer(nullptr),
|
||||
currentObjectsContainer(nullptr){};
|
||||
: projectScopedContainers(nullptr){};
|
||||
virtual ~ReadOnlyArbitraryEventsWorkerWithContext();
|
||||
|
||||
/**
|
||||
@@ -216,30 +233,22 @@ class GD_CORE_API ReadOnlyArbitraryEventsWorkerWithContext
|
||||
* giving the objects container on which the events are applying to.
|
||||
*/
|
||||
void Launch(const gd::EventsList& events,
|
||||
const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_) {
|
||||
currentGlobalObjectsContainer = &globalObjectsContainer_;
|
||||
currentObjectsContainer = &objectsContainer_;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_) {
|
||||
projectScopedContainers = &projectScopedContainers_;
|
||||
ReadOnlyArbitraryEventsWorker::Launch(events);
|
||||
};
|
||||
|
||||
void Launch(gd::EventsList& events) = delete;
|
||||
|
||||
protected:
|
||||
const gd::ObjectsContainer& GetGlobalObjectsContainer() {
|
||||
const gd::ProjectScopedContainers& GetProjectScopedContainers() {
|
||||
// Pointers are guaranteed to be not nullptr after
|
||||
// Launch was called.
|
||||
return *currentGlobalObjectsContainer;
|
||||
};
|
||||
const gd::ObjectsContainer& GetObjectsContainer() {
|
||||
// Pointers are guaranteed to be not nullptr after
|
||||
// Launch was called.
|
||||
return *currentObjectsContainer;
|
||||
return *projectScopedContainers;
|
||||
};
|
||||
|
||||
private:
|
||||
const gd::ObjectsContainer* currentGlobalObjectsContainer;
|
||||
const gd::ObjectsContainer* currentObjectsContainer;
|
||||
const gd::ProjectScopedContainers* projectScopedContainers;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
@@ -31,14 +31,10 @@ namespace gd {
|
||||
class GD_CORE_API ExpressionBehaviorRenamer
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionBehaviorRenamer(const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_,
|
||||
const gd::String& objectName_,
|
||||
ExpressionBehaviorRenamer(const gd::String& objectName_,
|
||||
const gd::String& oldBehaviorName_,
|
||||
const gd::String& newBehaviorName_)
|
||||
: hasDoneRenaming(false),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
objectName(objectName_),
|
||||
oldBehaviorName(oldBehaviorName_),
|
||||
newBehaviorName(newBehaviorName_){};
|
||||
@@ -97,8 +93,6 @@ class GD_CORE_API ExpressionBehaviorRenamer
|
||||
|
||||
private:
|
||||
bool hasDoneRenaming;
|
||||
const gd::ObjectsContainer& globalObjectsContainer;
|
||||
const gd::ObjectsContainer& objectsContainer;
|
||||
const gd::String& objectName; // The object name for which the behavior
|
||||
// must be replaced.
|
||||
const gd::String& oldBehaviorName;
|
||||
@@ -132,9 +126,7 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
} else {
|
||||
auto node = parameterValue.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionBehaviorRenamer renamer(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
objectName,
|
||||
ExpressionBehaviorRenamer renamer(objectName,
|
||||
oldBehaviorName,
|
||||
newBehaviorName);
|
||||
node->Visit(renamer);
|
||||
|
@@ -19,6 +19,8 @@
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -33,13 +35,11 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
public:
|
||||
ExpressionObjectsAnalyzer(
|
||||
const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String &rootType_,
|
||||
EventsContext& context_) :
|
||||
platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
context(context_){};
|
||||
virtual ~ExpressionObjectsAnalyzer(){};
|
||||
@@ -58,6 +58,25 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
|
||||
// This is an object variable.
|
||||
context.AddObjectName(projectScopedContainers, node.name);
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
@@ -69,26 +88,41 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(node.identifierName);
|
||||
context.AddObjectName(projectScopedContainers, node.identifierName);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (identifier is a variable but not an object).
|
||||
} else {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
|
||||
// This is an object variable.
|
||||
context.AddObjectName(projectScopedContainers, node.identifierName);
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.objectName.empty()) {
|
||||
context.AddObjectName(node.objectName);
|
||||
context.AddObjectName(projectScopedContainers, node.objectName);
|
||||
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
context.AddBehaviorName(node.objectName, node.objectFunctionOrBehaviorName);
|
||||
context.AddBehaviorName(projectScopedContainers, node.objectName, node.objectFunctionOrBehaviorName);
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (!node.objectName.empty()) {
|
||||
context.AddObjectName(node.objectName);
|
||||
context.AddObjectName(projectScopedContainers, node.objectName);
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
context.AddBehaviorName(node.objectName, node.behaviorName);
|
||||
context.AddBehaviorName(projectScopedContainers, node.objectName, node.behaviorName);
|
||||
}
|
||||
}
|
||||
for (auto& parameter : node.parameters) {
|
||||
@@ -99,8 +133,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
|
||||
EventsContext& context;
|
||||
@@ -121,8 +154,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
const gd::Expression& parameterValue,
|
||||
const gd::String& lastObjectName) {
|
||||
AnalyzeParameter(platform,
|
||||
project,
|
||||
layout,
|
||||
GetProjectScopedContainers(),
|
||||
parameterMetadata,
|
||||
parameterValue,
|
||||
context,
|
||||
@@ -134,8 +166,7 @@ bool EventsContextAnalyzer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
|
||||
void EventsContextAnalyzer::AnalyzeParameter(
|
||||
const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
const gd::Expression& parameter,
|
||||
EventsContext& context,
|
||||
@@ -143,59 +174,39 @@ void EventsContextAnalyzer::AnalyzeParameter(
|
||||
const auto& value = parameter.GetPlainString();
|
||||
const auto& type = metadata.GetType();
|
||||
if (ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(value);
|
||||
context.AddObjectName(projectScopedContainers, value);
|
||||
} else if (ParameterMetadata::IsExpression("number", type)) {
|
||||
auto node = parameter.GetRootNode();
|
||||
|
||||
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "number", context);
|
||||
ExpressionObjectsAnalyzer analyzer(platform, projectScopedContainers, "number", context);
|
||||
node->Visit(analyzer);
|
||||
} else if (ParameterMetadata::IsExpression("string", type)) {
|
||||
auto node = parameter.GetRootNode();
|
||||
|
||||
ExpressionObjectsAnalyzer analyzer(platform, project, layout, "string", context);
|
||||
ExpressionObjectsAnalyzer analyzer(platform, projectScopedContainers, "string", context);
|
||||
node->Visit(analyzer);
|
||||
} else if (ParameterMetadata::IsBehavior(type)) {
|
||||
context.AddBehaviorName(lastObjectName, value);
|
||||
context.AddBehaviorName(projectScopedContainers, lastObjectName, value);
|
||||
}
|
||||
}
|
||||
|
||||
void EventsContext::AddObjectName(const gd::String& objectOrGroupName) {
|
||||
for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) {
|
||||
void EventsContext::AddObjectName(const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::String& objectOrGroupName) {
|
||||
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
for (auto& realObjectName : objectsContainersList.ExpandObjectName(objectOrGroupName)) {
|
||||
objectNames.insert(realObjectName);
|
||||
}
|
||||
referencedObjectOrGroupNames.insert(objectOrGroupName);
|
||||
}
|
||||
|
||||
void EventsContext::AddBehaviorName(const gd::String& objectOrGroupName,
|
||||
void EventsContext::AddBehaviorName(const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName) {
|
||||
for (auto& realObjectName : ExpandObjectsName(objectOrGroupName)) {
|
||||
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
for (auto& realObjectName : objectsContainersList.ExpandObjectName(objectOrGroupName)) {
|
||||
objectOrGroupBehaviorNames[realObjectName].insert(behaviorName);
|
||||
}
|
||||
objectOrGroupBehaviorNames[objectOrGroupName].insert(behaviorName);
|
||||
}
|
||||
|
||||
std::vector<gd::String> EventsContext::ExpandObjectsName(
|
||||
const gd::String& objectName) {
|
||||
// Note: this logic is duplicated in EventsCodeGenerator::ExpandObjectsName
|
||||
std::vector<gd::String> realObjects;
|
||||
if (project.GetObjectGroups().Has(objectName))
|
||||
realObjects =
|
||||
project.GetObjectGroups().Get(objectName).GetAllObjectsNames();
|
||||
else if (layout.GetObjectGroups().Has(objectName))
|
||||
realObjects = layout.GetObjectGroups().Get(objectName).GetAllObjectsNames();
|
||||
else
|
||||
realObjects.push_back(objectName);
|
||||
|
||||
// Ensure that all returned objects actually exists.
|
||||
for (std::size_t i = 0; i < realObjects.size();) {
|
||||
if (!layout.HasObjectNamed(realObjects[i]) &&
|
||||
!project.HasObjectNamed(realObjects[i]))
|
||||
realObjects.erase(realObjects.begin() + i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
|
||||
return realObjects;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -15,6 +15,7 @@ namespace gd {
|
||||
class BaseEvent;
|
||||
class Platform;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Project;
|
||||
class Layout;
|
||||
class EventsList;
|
||||
@@ -29,12 +30,13 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API EventsContext {
|
||||
public:
|
||||
EventsContext(gd::ObjectsContainer& project_, gd::ObjectsContainer& layout_)
|
||||
: project(project_), layout(layout_){};
|
||||
EventsContext(){};
|
||||
virtual ~EventsContext(){};
|
||||
|
||||
void AddObjectName(const gd::String& objectOrGroupName);
|
||||
void AddBehaviorName(const gd::String& objectOrGroupName,
|
||||
void AddObjectName(const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::String& objectOrGroupName);
|
||||
void AddBehaviorName(const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName);
|
||||
|
||||
/**
|
||||
@@ -59,13 +61,9 @@ class GD_CORE_API EventsContext {
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<gd::String> ExpandObjectsName(const gd::String& objectOrGroupName);
|
||||
|
||||
std::set<gd::String> referencedObjectOrGroupNames;
|
||||
std::set<gd::String> objectNames;
|
||||
std::map<gd::String, std::set<gd::String>> objectOrGroupBehaviorNames;
|
||||
gd::ObjectsContainer& project;
|
||||
gd::ObjectsContainer& layout;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -73,15 +71,10 @@ class GD_CORE_API EventsContext {
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
|
||||
class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorkerWithContext {
|
||||
public:
|
||||
EventsContextAnalyzer(const gd::Platform& platform_,
|
||||
gd::ObjectsContainer& project_,
|
||||
gd::ObjectsContainer& layout_)
|
||||
: platform(platform_),
|
||||
project(project_),
|
||||
layout(layout_),
|
||||
context(project, layout){};
|
||||
EventsContextAnalyzer(const gd::Platform& platform_)
|
||||
: platform(platform_) {};
|
||||
virtual ~EventsContextAnalyzer(){};
|
||||
|
||||
/**
|
||||
@@ -90,8 +83,7 @@ class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
|
||||
const EventsContext& GetEventsContext() { return context; }
|
||||
|
||||
static void AnalyzeParameter(const gd::Platform& platform,
|
||||
const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
const gd::Expression& parameter,
|
||||
EventsContext& context,
|
||||
@@ -102,8 +94,6 @@ class GD_CORE_API EventsContextAnalyzer : public ArbitraryEventsWorker {
|
||||
bool isCondition);
|
||||
|
||||
const gd::Platform& platform;
|
||||
gd::ObjectsContainer& project;
|
||||
gd::ObjectsContainer& layout;
|
||||
EventsContext context;
|
||||
};
|
||||
|
||||
|
@@ -34,14 +34,12 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
|
||||
public:
|
||||
IdentifierFinderExpressionNodeWorker(std::set<gd::String>& results_,
|
||||
const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String& identifierType_,
|
||||
const gd::String& objectName_ = "")
|
||||
: results(results_),
|
||||
platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
identifierType(identifierType_),
|
||||
objectName(objectName_){};
|
||||
virtual ~IdentifierFinderExpressionNodeWorker(){};
|
||||
@@ -79,14 +77,14 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
|
||||
const gd::ExpressionMetadata &metadata = isObjectFunction ?
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform,
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
|
||||
projectScopedContainers.GetObjectsContainersList().GetTypeOfObject(objectName),
|
||||
node.functionName):
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
size_t parameterIndex = 0;
|
||||
for (size_t metadataIndex = (isObjectFunction ? 1 : 0); metadataIndex < metadata.parameters.size()
|
||||
&& parameterIndex < node.parameters.size(); ++metadataIndex) {
|
||||
@@ -111,8 +109,7 @@ class GD_CORE_API IdentifierFinderExpressionNodeWorker
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
|
||||
std::set<gd::String>& results; ///< Reference to the std::set where argument
|
||||
///< values must be stored.
|
||||
@@ -166,8 +163,7 @@ class GD_CORE_API IdentifierFinderEventWorker
|
||||
IdentifierFinderExpressionNodeWorker searcher(
|
||||
results,
|
||||
platform,
|
||||
GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
GetProjectScopedContainers(),
|
||||
identifierType,
|
||||
objectName);
|
||||
node->Visit(searcher);
|
||||
@@ -227,7 +223,8 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
identifierType,
|
||||
objectName);
|
||||
eventWorker.Launch(layout.GetEvents(), project, layout);
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
@@ -238,7 +235,8 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
identifierType,
|
||||
objectName);
|
||||
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
|
||||
eventWorker.Launch(externalEvents.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
}
|
||||
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
|
||||
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
|
||||
@@ -247,7 +245,8 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
identifierType,
|
||||
objectName);
|
||||
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
|
||||
eventWorker.Launch(dependencyLayout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, dependencyLayout));
|
||||
}
|
||||
}
|
||||
|
||||
|
277
Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp
Normal file
277
Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Go through the nodes and rename properties,
|
||||
* or signal if the instruction must be renamed if a removed property is used.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionPropertyReplacer
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionPropertyReplacer(
|
||||
const gd::Platform& platform_,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::PropertiesContainer& targetPropertiesContainer_,
|
||||
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames_,
|
||||
const std::unordered_set<gd::String>& removedPropertyNames_)
|
||||
: hasDoneRenaming(false),
|
||||
removedPropertyUsed(false),
|
||||
platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
targetPropertiesContainer(targetPropertiesContainer_),
|
||||
oldToNewPropertyNames(oldToNewPropertyNames_),
|
||||
removedPropertyNames(removedPropertyNames_){};
|
||||
virtual ~ExpressionPropertyReplacer(){};
|
||||
|
||||
bool HasDoneRenaming() const { return hasDoneRenaming; }
|
||||
bool IsRemovedPropertyUsed() const { return removedPropertyUsed; }
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
auto& propertiesContainersList =
|
||||
projectScopedContainers.GetPropertiesContainersList();
|
||||
|
||||
// The node represents a variable or an object name on which a variable
|
||||
// will be accessed, or a property with a child.
|
||||
|
||||
// Match the potential *new* name of the property, because refactorings are
|
||||
// done after changes in the variables container.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
GetPotentialNewName(node.name),
|
||||
[&]() {
|
||||
// Do nothing, it's an object variable.
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}, [&]() {
|
||||
// Do nothing, it's a variable.
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}, [&]() {
|
||||
// This is a property, check if it's coming from the target container with
|
||||
// properties to replace.
|
||||
if (propertiesContainersList.HasPropertiesContainer(
|
||||
targetPropertiesContainer)) {
|
||||
// The node represents a property, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemovePropertyOfTargetPropertyContainer(node.name);
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}, [&]() {
|
||||
// Do nothing, it's a parameter.
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}, [&]() {
|
||||
// This is something else - potentially a deleted property.
|
||||
// Check if it's coming from the target container with
|
||||
// properties to replace.
|
||||
if (propertiesContainersList.HasPropertiesContainer(
|
||||
targetPropertiesContainer)) {
|
||||
// The node represents a property, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemovePropertyOfTargetPropertyContainer(node.name);
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
});
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto& propertiesContainersList =
|
||||
projectScopedContainers.GetPropertiesContainersList();
|
||||
|
||||
// Match the potential *new* name of the property, because refactorings are
|
||||
// done after changes in the variables container.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
GetPotentialNewName(node.identifierName),
|
||||
[&]() {
|
||||
// Do nothing, it's an object variable.
|
||||
}, [&]() {
|
||||
// Do nothing, it's a variable.
|
||||
}, [&]() {
|
||||
// This is a property, check if it's coming from the target container with
|
||||
// properties to replace.
|
||||
if (propertiesContainersList.HasPropertiesContainer(
|
||||
targetPropertiesContainer)) {
|
||||
// The node represents a property, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemovePropertyOfTargetPropertyContainer(node.identifierName);
|
||||
}
|
||||
}, [&]() {
|
||||
// Do nothing, it's a parameter.
|
||||
}, [&]() {
|
||||
// This is something else - potentially a deleted property.
|
||||
// Check if it's coming from the target container with
|
||||
// properties to replace.
|
||||
if (propertiesContainersList.HasPropertiesContainer(
|
||||
targetPropertiesContainer)) {
|
||||
// The node represents a property, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemovePropertyOfTargetPropertyContainer(node.identifierName);
|
||||
}
|
||||
});
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
for (auto& parameter : node.parameters) {
|
||||
parameter->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
|
||||
private:
|
||||
bool hasDoneRenaming;
|
||||
bool removedPropertyUsed;
|
||||
|
||||
const gd::String& GetPotentialNewName(const gd::String& oldName) {
|
||||
return oldToNewPropertyNames.count(oldName) >= 1
|
||||
? oldToNewPropertyNames.find(oldName)->second
|
||||
: oldName;
|
||||
}
|
||||
|
||||
bool RenameOrRemovePropertyOfTargetPropertyContainer(
|
||||
gd::String& propertyName) {
|
||||
if (oldToNewPropertyNames.count(propertyName) >= 1) {
|
||||
propertyName = oldToNewPropertyNames.find(propertyName)->second;
|
||||
hasDoneRenaming = true;
|
||||
return true;
|
||||
} else if (removedPropertyNames.count(propertyName) >= 1) {
|
||||
removedPropertyUsed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Nothing was changed or done.
|
||||
}
|
||||
|
||||
// Scope:
|
||||
const gd::Platform& platform;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
|
||||
// Renaming or removing to do:
|
||||
const gd::PropertiesContainer& targetPropertiesContainer;
|
||||
const std::unordered_map<gd::String, gd::String>& oldToNewPropertyNames;
|
||||
const std::unordered_set<gd::String>& removedPropertyNames;
|
||||
|
||||
gd::String objectNameToUseForVariableAccessor;
|
||||
};
|
||||
|
||||
bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool isCondition) {
|
||||
const auto& metadata = isCondition
|
||||
? gd::MetadataProvider::GetConditionMetadata(
|
||||
platform, instruction.GetType())
|
||||
: gd::MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("number", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("string", type))
|
||||
return; // Not an expression that can contain properties.
|
||||
|
||||
auto node = parameterValue.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionPropertyReplacer renamer(platform,
|
||||
GetProjectScopedContainers(),
|
||||
targetPropertiesContainer,
|
||||
oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
node->Visit(renamer);
|
||||
|
||||
if (renamer.IsRemovedPropertyUsed()) {
|
||||
shouldDeleteInstruction = true;
|
||||
} else if (renamer.HasDoneRenaming()) {
|
||||
instruction.SetParameter(
|
||||
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return shouldDeleteInstruction;
|
||||
}
|
||||
|
||||
bool EventsPropertyReplacer::DoVisitEventExpression(
|
||||
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
|
||||
const gd::String& type = metadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("number", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("string", type))
|
||||
return false; // Not an expression that can contain properties.
|
||||
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionPropertyReplacer renamer(platform,
|
||||
GetProjectScopedContainers(),
|
||||
targetPropertiesContainer,
|
||||
oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
node->Visit(renamer);
|
||||
|
||||
if (renamer.IsRemovedPropertyUsed()) {
|
||||
return true;
|
||||
} else if (renamer.HasDoneRenaming()) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EventsPropertyReplacer::~EventsPropertyReplacer() {}
|
||||
|
||||
} // namespace gd
|
56
Core/GDCore/IDE/Events/EventsPropertyReplacer.h
Normal file
56
Core/GDCore/IDE/Events/EventsPropertyReplacer.h
Normal 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 <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class BaseEvent;
|
||||
class PropertiesContainer;
|
||||
class EventsList;
|
||||
class Platform;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Replace in expressions and in parameters of actions or conditions,
|
||||
* references to the name of a property by another.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API EventsPropertyReplacer
|
||||
: public ArbitraryEventsWorkerWithContext {
|
||||
public:
|
||||
EventsPropertyReplacer(
|
||||
const gd::Platform &platform_,
|
||||
const gd::PropertiesContainer &targetPropertiesContainer_,
|
||||
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames_,
|
||||
const std::unordered_set<gd::String> &removedPropertyNames_)
|
||||
: platform(platform_),
|
||||
targetPropertiesContainer(targetPropertiesContainer_),
|
||||
oldToNewPropertyNames(oldToNewPropertyNames_),
|
||||
removedPropertyNames(removedPropertyNames_){};
|
||||
virtual ~EventsPropertyReplacer();
|
||||
|
||||
private:
|
||||
bool DoVisitInstruction(gd::Instruction &instruction,
|
||||
bool isCondition) override;
|
||||
bool DoVisitEventExpression(gd::Expression &expression,
|
||||
const gd::ParameterMetadata &metadata) override;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::PropertiesContainer &targetPropertiesContainer;
|
||||
const std::unordered_map<gd::String, gd::String> &oldToNewPropertyNames;
|
||||
const std::unordered_set<gd::String> &removedPropertyNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -20,6 +20,7 @@
|
||||
#include "GDCore/IDE/Events/InstructionSentenceFormatter.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
|
||||
using namespace std;
|
||||
@@ -36,14 +37,12 @@ const gd::String EventsRefactorer::searchIgnoredCharacters = ";:,#()";
|
||||
class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionObjectRenamer(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::String &rootType_,
|
||||
const gd::String& objectName_,
|
||||
const gd::String& objectNewName_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
hasDoneRenaming(false),
|
||||
objectName(objectName_),
|
||||
@@ -51,14 +50,13 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
virtual ~ExpressionObjectRenamer(){};
|
||||
|
||||
static bool Rename(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node,
|
||||
const gd::String& objectName,
|
||||
const gd::String& objectNewName) {
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
|
||||
ExpressionObjectRenamer renamer(platform, globalObjectsContainer, objectsContainer, rootType, objectName, objectNewName);
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) {
|
||||
ExpressionObjectRenamer renamer(platform, projectScopedContainers, rootType, objectName, objectNewName);
|
||||
node.Visit(renamer);
|
||||
|
||||
return renamer.HasDoneRenaming();
|
||||
@@ -83,6 +81,29 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
if (node.name == objectName) {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
|
||||
// This is an object variable.
|
||||
hasDoneRenaming = true;
|
||||
node.name = objectNewName;
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
@@ -94,11 +115,29 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
node.identifierName = objectNewName;
|
||||
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
if (node.identifierName == objectName) {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
|
||||
// This is an object variable.
|
||||
hasDoneRenaming = true;
|
||||
node.identifierName = objectNewName;
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
@@ -124,8 +163,7 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
const gd::String& objectNewName;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
@@ -138,26 +176,23 @@ class GD_CORE_API ExpressionObjectRenamer : public ExpressionParser2NodeWorker {
|
||||
class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionObjectFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String &rootType_,
|
||||
const gd::String& objectName_)
|
||||
const gd::String& searchedObjectName_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
hasObject(false),
|
||||
objectName(objectName_){};
|
||||
searchedObjectName(searchedObjectName_){};
|
||||
virtual ~ExpressionObjectFinder(){};
|
||||
|
||||
static bool CheckIfHasObject(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node,
|
||||
const gd::String& objectName) {
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, globalObjectsContainer, objectsContainer, rootType, node)) {
|
||||
ExpressionObjectFinder finder(platform, globalObjectsContainer, objectsContainer, rootType, objectName);
|
||||
if (gd::ExpressionValidator::HasNoErrors(platform, projectScopedContainers, rootType, node)) {
|
||||
ExpressionObjectFinder finder(platform, projectScopedContainers, rootType, objectName);
|
||||
node.Visit(finder);
|
||||
|
||||
return finder.HasFoundObject();
|
||||
@@ -182,6 +217,28 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, objectsContainersList, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
if (node.name == searchedObjectName) {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name, [&]() {
|
||||
// This is an object variable.
|
||||
hasObject = true;
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
@@ -193,19 +250,36 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, globalObjectsContainer, objectsContainer, rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
node.identifierName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
if (node.identifierName == searchedObjectName) {
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName, [&]() {
|
||||
// This is an object variable.
|
||||
hasObject = true;
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
if (node.objectName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (node.objectName == objectName) {
|
||||
if (node.objectName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
}
|
||||
for (auto& parameter : node.parameters) {
|
||||
@@ -216,17 +290,15 @@ class GD_CORE_API ExpressionObjectFinder : public ExpressionParser2NodeWorker {
|
||||
|
||||
private:
|
||||
bool hasObject;
|
||||
const gd::String& objectName;
|
||||
const gd::String& searchedObjectName;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& actions,
|
||||
gd::String oldName,
|
||||
gd::String newName) {
|
||||
@@ -245,7 +317,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
|
||||
actions[aId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -255,7 +327,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
|
||||
actions[aId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -265,8 +337,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
if (!actions[aId].GetSubInstructions().empty())
|
||||
somethingModified =
|
||||
RenameObjectInActions(platform,
|
||||
project,
|
||||
layout,
|
||||
projectScopedContainers,
|
||||
actions[aId].GetSubInstructions(),
|
||||
oldName,
|
||||
newName) ||
|
||||
@@ -278,8 +349,7 @@ bool EventsRefactorer::RenameObjectInActions(const gd::Platform& platform,
|
||||
|
||||
bool EventsRefactorer::RenameObjectInConditions(
|
||||
const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& conditions,
|
||||
gd::String oldName,
|
||||
gd::String newName) {
|
||||
@@ -299,7 +369,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -309,7 +379,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
|
||||
conditions[cId].SetParameter(
|
||||
pNb, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
@@ -319,8 +389,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
if (!conditions[cId].GetSubInstructions().empty())
|
||||
somethingModified =
|
||||
RenameObjectInConditions(platform,
|
||||
project,
|
||||
layout,
|
||||
projectScopedContainers,
|
||||
conditions[cId].GetSubInstructions(),
|
||||
oldName,
|
||||
newName) ||
|
||||
@@ -332,8 +401,7 @@ bool EventsRefactorer::RenameObjectInConditions(
|
||||
|
||||
bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::Expression& expression,
|
||||
gd::ParameterMetadata parameterMetadata,
|
||||
gd::String oldName,
|
||||
@@ -348,7 +416,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
parameterMetadata.GetType())) {
|
||||
auto node = expression.GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "number", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "number", *node, oldName, newName)) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
@@ -357,7 +425,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
parameterMetadata.GetType())) {
|
||||
auto node = expression.GetRootNode();
|
||||
|
||||
if (ExpressionObjectRenamer::Rename(platform, project, layout, "string", *node, oldName, newName)) {
|
||||
if (ExpressionObjectRenamer::Rename(platform, projectScopedContainers, "string", *node, oldName, newName)) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
@@ -366,8 +434,7 @@ bool EventsRefactorer::RenameObjectInEventParameters(
|
||||
}
|
||||
|
||||
void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::EventsList& events,
|
||||
gd::String oldName,
|
||||
gd::String newName) {
|
||||
@@ -376,14 +443,14 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
events[i].GetAllConditionsVectors();
|
||||
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
|
||||
bool somethingModified = RenameObjectInConditions(
|
||||
platform, project, layout, *conditionsVectors[j], oldName, newName);
|
||||
platform, projectScopedContainers, *conditionsVectors[j], oldName, newName);
|
||||
}
|
||||
|
||||
vector<gd::InstructionsList*> actionsVectors =
|
||||
events[i].GetAllActionsVectors();
|
||||
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
|
||||
bool somethingModified = RenameObjectInActions(
|
||||
platform, project, layout, *actionsVectors[j], oldName, newName);
|
||||
platform, projectScopedContainers, *actionsVectors[j], oldName, newName);
|
||||
}
|
||||
|
||||
vector<pair<gd::Expression*, gd::ParameterMetadata>>
|
||||
@@ -393,8 +460,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
gd::ParameterMetadata parameterMetadata =
|
||||
expressionsWithMetadata[j].second;
|
||||
bool somethingModified = RenameObjectInEventParameters(platform,
|
||||
project,
|
||||
layout,
|
||||
projectScopedContainers,
|
||||
*expression,
|
||||
parameterMetadata,
|
||||
oldName,
|
||||
@@ -403,8 +469,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
|
||||
if (events[i].CanHaveSubEvents())
|
||||
RenameObjectInEvents(platform,
|
||||
project,
|
||||
layout,
|
||||
projectScopedContainers,
|
||||
events[i].GetSubEvents(),
|
||||
oldName,
|
||||
newName);
|
||||
@@ -412,8 +477,7 @@ void EventsRefactorer::RenameObjectInEvents(const gd::Platform& platform,
|
||||
}
|
||||
|
||||
bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& actions,
|
||||
gd::String name) {
|
||||
bool somethingModified = false;
|
||||
@@ -435,7 +499,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -445,7 +509,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = actions[aId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -459,8 +523,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
} else if (!actions[aId].GetSubInstructions().empty())
|
||||
somethingModified =
|
||||
RemoveObjectInActions(platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
projectScopedContainers,
|
||||
actions[aId].GetSubInstructions(),
|
||||
name) ||
|
||||
somethingModified;
|
||||
@@ -471,8 +534,7 @@ bool EventsRefactorer::RemoveObjectInActions(const gd::Platform& platform,
|
||||
|
||||
bool EventsRefactorer::RemoveObjectInConditions(
|
||||
const gd::Platform& platform,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& conditions,
|
||||
gd::String name) {
|
||||
bool somethingModified = false;
|
||||
@@ -495,7 +557,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
"number", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "number", *node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "number", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -505,7 +567,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
"string", instrInfos.parameters[pNb].GetType())) {
|
||||
auto node = conditions[cId].GetParameter(pNb).GetRootNode();
|
||||
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, globalObjectsContainer, objectsContainer, "string", *node, name)) {
|
||||
if (ExpressionObjectFinder::CheckIfHasObject(platform, projectScopedContainers, "string", *node, name)) {
|
||||
deleteMe = true;
|
||||
break;
|
||||
}
|
||||
@@ -519,8 +581,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
} else if (!conditions[cId].GetSubInstructions().empty())
|
||||
somethingModified =
|
||||
RemoveObjectInConditions(platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
projectScopedContainers,
|
||||
conditions[cId].GetSubInstructions(),
|
||||
name) ||
|
||||
somethingModified;
|
||||
@@ -530,8 +591,7 @@ bool EventsRefactorer::RemoveObjectInConditions(
|
||||
}
|
||||
|
||||
void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& globalObjectsContainer,
|
||||
gd::ObjectsContainer& objectsContainer,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::EventsList& events,
|
||||
gd::String name) {
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
@@ -539,19 +599,19 @@ void EventsRefactorer::RemoveObjectInEvents(const gd::Platform& platform,
|
||||
events[i].GetAllConditionsVectors();
|
||||
for (std::size_t j = 0; j < conditionsVectors.size(); ++j) {
|
||||
bool conditionsModified = RemoveObjectInConditions(
|
||||
platform, globalObjectsContainer, objectsContainer, *conditionsVectors[j], name);
|
||||
platform, projectScopedContainers, *conditionsVectors[j], name);
|
||||
}
|
||||
|
||||
vector<gd::InstructionsList*> actionsVectors =
|
||||
events[i].GetAllActionsVectors();
|
||||
for (std::size_t j = 0; j < actionsVectors.size(); ++j) {
|
||||
bool actionsModified = RemoveObjectInActions(
|
||||
platform, globalObjectsContainer, objectsContainer, *actionsVectors[j], name);
|
||||
platform, projectScopedContainers, *actionsVectors[j], name);
|
||||
}
|
||||
|
||||
if (events[i].CanHaveSubEvents())
|
||||
RemoveObjectInEvents(
|
||||
platform, globalObjectsContainer, objectsContainer, events[i].GetSubEvents(), name);
|
||||
platform, projectScopedContainers, events[i].GetSubEvents(), name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,20 +647,20 @@ std::vector<EventsSearchResult> EventsRefactorer::ReplaceStringInEvents(
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
bool eventModified = false;
|
||||
|
||||
std::vector<gd::Expression*> allObjectExpressions =
|
||||
events[i].GetAllObjectExpressions();
|
||||
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
|
||||
auto allExpressionsWithMetadata = events[i].GetAllExpressionsWithMetadata();
|
||||
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
|
||||
gd::Expression* expression = expressionAndMetadata.first;
|
||||
|
||||
gd::String newExpressionPlainString =
|
||||
matchCase ? allObjectExpressions[j]->GetPlainString().FindAndReplace(
|
||||
matchCase ? expression->GetPlainString().FindAndReplace(
|
||||
toReplace, newString, true)
|
||||
: ReplaceAllOccurrencesCaseInsensitive(
|
||||
allObjectExpressions[j]->GetPlainString(),
|
||||
expression->GetPlainString(),
|
||||
toReplace,
|
||||
newString);
|
||||
|
||||
if (newExpressionPlainString !=
|
||||
allObjectExpressions[j]->GetPlainString()) {
|
||||
*allObjectExpressions[j] = gd::Expression(newExpressionPlainString);
|
||||
if (newExpressionPlainString != expression->GetPlainString()) {
|
||||
*expression = gd::Expression(newExpressionPlainString);
|
||||
|
||||
if (!eventModified) {
|
||||
modifiedEvents.push_back(EventsSearchResult(
|
||||
@@ -815,14 +875,14 @@ vector<EventsSearchResult> EventsRefactorer::SearchInEvents(
|
||||
for (std::size_t i = 0; i < events.size(); ++i) {
|
||||
bool eventAddedInResults = false;
|
||||
|
||||
std::vector<gd::Expression*> allObjectExpressions =
|
||||
events[i].GetAllObjectExpressions();
|
||||
for (std::size_t j = 0; j < allObjectExpressions.size(); ++j) {
|
||||
auto allExpressionsWithMetadata = events[i].GetAllExpressionsWithMetadata();
|
||||
for (auto& expressionAndMetadata : allExpressionsWithMetadata) {
|
||||
gd::Expression* expression = expressionAndMetadata.first;
|
||||
|
||||
size_t foundPosition =
|
||||
matchCase
|
||||
? allObjectExpressions[j]->GetPlainString().find(search)
|
||||
: allObjectExpressions[j]->GetPlainString().FindCaseInsensitive(
|
||||
search);
|
||||
? expression->GetPlainString().find(search)
|
||||
: expression->GetPlainString().FindCaseInsensitive(search);
|
||||
|
||||
if (foundPosition != gd::String::npos && !eventAddedInResults) {
|
||||
results.push_back(EventsSearchResult(
|
||||
|
@@ -14,6 +14,8 @@
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class ProjectScopedContainers;
|
||||
class Platform;
|
||||
class ExternalEvents;
|
||||
class BaseEvent;
|
||||
@@ -79,8 +81,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* events ).
|
||||
*/
|
||||
static void RenameObjectInEvents(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::EventsList& events,
|
||||
gd::String oldName,
|
||||
gd::String newName);
|
||||
@@ -89,8 +90,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* Remove all actions or conditions using an object
|
||||
*/
|
||||
static void RemoveObjectInEvents(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::EventsList& events,
|
||||
gd::String name);
|
||||
|
||||
@@ -136,8 +136,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* \return true if something was modified.
|
||||
*/
|
||||
static bool RenameObjectInActions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& instructions,
|
||||
gd::String oldName,
|
||||
gd::String newName);
|
||||
@@ -149,8 +148,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* \return true if something was modified.
|
||||
*/
|
||||
static bool RenameObjectInConditions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& instructions,
|
||||
gd::String oldName,
|
||||
gd::String newName);
|
||||
@@ -163,8 +161,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
*/
|
||||
static bool RenameObjectInEventParameters(
|
||||
const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::Expression& expression,
|
||||
gd::ParameterMetadata parameterMetadata,
|
||||
gd::String oldName,
|
||||
@@ -176,8 +173,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* \return true if something was modified.
|
||||
*/
|
||||
static bool RemoveObjectInConditions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& conditions,
|
||||
gd::String name);
|
||||
|
||||
@@ -187,8 +183,7 @@ class GD_CORE_API EventsRefactorer {
|
||||
* \return true if something was modified.
|
||||
*/
|
||||
static bool RemoveObjectInActions(const gd::Platform& platform,
|
||||
gd::ObjectsContainer& project,
|
||||
gd::ObjectsContainer& layout,
|
||||
gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::InstructionsList& conditions,
|
||||
gd::String name);
|
||||
|
||||
|
404
Core/GDCore/IDE/Events/EventsVariableReplacer.cpp
Normal file
404
Core/GDCore/IDE/Events/EventsVariableReplacer.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/IDE/Events/EventsVariableReplacer.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Go through the nodes and rename variables,
|
||||
* or signal if the instruction must be renamed if a removed variable is used.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionVariableReplacer
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionVariableReplacer(
|
||||
const gd::Platform& platform_,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::VariablesContainer& targetVariablesContainer_,
|
||||
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames_,
|
||||
const std::unordered_set<gd::String>& removedVariableNames_)
|
||||
: hasDoneRenaming(false),
|
||||
removedVariableUsed(false),
|
||||
platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
forcedInitialVariablesContainer(nullptr),
|
||||
targetVariablesContainer(targetVariablesContainer_),
|
||||
oldToNewVariableNames(oldToNewVariableNames_),
|
||||
removedVariableNames(removedVariableNames_){};
|
||||
virtual ~ExpressionVariableReplacer(){};
|
||||
|
||||
void SetForcedInitialVariablesContainer(
|
||||
const gd::VariablesContainer* forcedInitialVariablesContainer_) {
|
||||
forcedInitialVariablesContainer = forcedInitialVariablesContainer_;
|
||||
}
|
||||
|
||||
bool HasDoneRenaming() const { return hasDoneRenaming; }
|
||||
bool IsRemovedVariableUsed() const { return removedVariableUsed; }
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
node.expression->Visit(*this);
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
// The node represents a variable or an object name on which a variable
|
||||
// will be accessed.
|
||||
|
||||
if (forcedInitialVariablesContainer) {
|
||||
// A scope was forced. Honor it: it means this node represents a variable
|
||||
// of the forced variables container.
|
||||
if (forcedInitialVariablesContainer == &targetVariablesContainer) {
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Match the potential *new* name of the variable, because refactorings are
|
||||
// done after changes in the variables container.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
GetPotentialNewName(node.name),
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
// Remember the object name.
|
||||
objectNameToUseForVariableAccessor = node.name;
|
||||
if (node.child) node.child->Visit(*this);
|
||||
objectNameToUseForVariableAccessor = "";
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
if (projectScopedContainers.GetVariablesContainersList()
|
||||
.HasVariablesContainer(targetVariablesContainer)) {
|
||||
// The node represents a variable, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
},
|
||||
[&]() {
|
||||
// This is a property.
|
||||
if (node.child) node.child->Visit(*this);
|
||||
},
|
||||
[&]() {
|
||||
// This is a parameter.
|
||||
if (node.child) node.child->Visit(*this);
|
||||
},
|
||||
[&]() {
|
||||
// This is something else - potentially a deleted variable.
|
||||
if (projectScopedContainers.GetVariablesContainersList()
|
||||
.HasVariablesContainer(targetVariablesContainer)) {
|
||||
// The node represents a variable, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
});
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
if (!objectNameToUseForVariableAccessor.empty()) {
|
||||
if (objectsContainersList.HasVariablesContainer(
|
||||
objectNameToUseForVariableAccessor, targetVariablesContainer)) {
|
||||
// The node represents an object variable, and this object variables are
|
||||
// the target. Do the replacement or removals:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(node.name);
|
||||
}
|
||||
}
|
||||
objectNameToUseForVariableAccessor = "";
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
objectNameToUseForVariableAccessor = "";
|
||||
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
|
||||
// The node represents a variable or an object variable in an expression
|
||||
// (and if it's a variable reference or a value does not have any importance
|
||||
// here).
|
||||
|
||||
if (forcedInitialVariablesContainer) {
|
||||
// A scope was forced. Honor it: it means this node represents a variable
|
||||
// of the forced variables container.
|
||||
if (forcedInitialVariablesContainer == &targetVariablesContainer) {
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(node.identifierName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Match the potential *new* name of the variable, because refactorings are
|
||||
// done after changes in the variables container.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
GetPotentialNewName(node.identifierName),
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
if (objectsContainersList.HasVariablesContainer(
|
||||
node.identifierName, targetVariablesContainer)) {
|
||||
// The node represents an object variable, and this object variables
|
||||
// are the target. Do the replacement or removals:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(
|
||||
node.childIdentifierName);
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
if (projectScopedContainers.GetVariablesContainersList()
|
||||
.HasVariablesContainer(targetVariablesContainer)) {
|
||||
// The node represents a variable, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(
|
||||
node.identifierName);
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
// This is a property.
|
||||
},
|
||||
[&]() {
|
||||
// This is a parameter.
|
||||
},
|
||||
[&]() {
|
||||
// This is something else - potentially a deleted variable.
|
||||
if (projectScopedContainers.GetVariablesContainersList()
|
||||
.HasVariablesContainer(targetVariablesContainer)) {
|
||||
// The node represents a variable, that can come from the target
|
||||
// (because the target is in the scope), replace or remove it:
|
||||
RenameOrRemoveVariableOfTargetVariableContainer(
|
||||
node.identifierName);
|
||||
}
|
||||
});
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata& metadata =
|
||||
MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
|
||||
for (size_t parameterIndex = 0; parameterIndex < node.parameters.size();
|
||||
++parameterIndex) {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
projectScopedContainers.GetObjectsContainersList(),
|
||||
node,
|
||||
parameterIndex);
|
||||
|
||||
// Handle legacy pre-scoped variable parameters: in this case, we
|
||||
// force the "scope" at which starts the evalution of variables.
|
||||
if (parameterMetadata && parameterMetadata->GetValueTypeMetadata()
|
||||
.IsLegacyPreScopedVariable()) {
|
||||
const gd::VariablesContainer* oldForcedInitialVariablesContainer =
|
||||
forcedInitialVariablesContainer;
|
||||
|
||||
forcedInitialVariablesContainer = nullptr;
|
||||
if (parameterMetadata->GetType() == "globalvar") {
|
||||
forcedInitialVariablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
} else if (parameterMetadata->GetType() == "scenevar") {
|
||||
forcedInitialVariablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
} else if (parameterMetadata->GetType() == "objectvar") {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
projectScopedContainers.GetObjectsContainersList(),
|
||||
node.objectName,
|
||||
*node.parameters[parameterIndex].get());
|
||||
forcedInitialVariablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(objectName);
|
||||
}
|
||||
|
||||
node.parameters[parameterIndex]->Visit(*this);
|
||||
forcedInitialVariablesContainer = oldForcedInitialVariablesContainer;
|
||||
} else {
|
||||
// For any other parameter, there is no special treatment being needed.
|
||||
node.parameters[parameterIndex]->Visit(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
|
||||
private:
|
||||
bool hasDoneRenaming;
|
||||
bool removedVariableUsed;
|
||||
|
||||
const gd::String& GetPotentialNewName(const gd::String& oldName) {
|
||||
return oldToNewVariableNames.count(oldName) >= 1
|
||||
? oldToNewVariableNames.find(oldName)->second
|
||||
: oldName;
|
||||
}
|
||||
|
||||
bool RenameOrRemoveVariableOfTargetVariableContainer(
|
||||
gd::String& variableName) {
|
||||
if (oldToNewVariableNames.count(variableName) >= 1) {
|
||||
variableName = oldToNewVariableNames.find(variableName)->second;
|
||||
hasDoneRenaming = true;
|
||||
return true;
|
||||
} else if (removedVariableNames.count(variableName) >= 1) {
|
||||
removedVariableUsed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Nothing was changed or done.
|
||||
}
|
||||
|
||||
// Scope:
|
||||
const gd::Platform& platform;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
const gd::VariablesContainer* forcedInitialVariablesContainer;
|
||||
|
||||
// Renaming or removing to do:
|
||||
const gd::VariablesContainer& targetVariablesContainer;
|
||||
const std::unordered_map<gd::String, gd::String>& oldToNewVariableNames;
|
||||
const std::unordered_set<gd::String>& removedVariableNames;
|
||||
|
||||
gd::String objectNameToUseForVariableAccessor;
|
||||
};
|
||||
|
||||
const gd::VariablesContainer*
|
||||
EventsVariableReplacer::FindForcedVariablesContainerIfAny(
|
||||
const gd::String& type, const gd::String& lastObjectName) {
|
||||
// Handle legacy pre-scoped variable parameters: in this case, we
|
||||
// force the "scope" at which starts the evalution of variables.
|
||||
if (type == "objectvar") {
|
||||
return GetProjectScopedContainers()
|
||||
.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(lastObjectName);
|
||||
} else if (type == "globalvar") {
|
||||
return GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
} else if (type == "scenevar") {
|
||||
return GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool EventsVariableReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool isCondition) {
|
||||
const auto& metadata = isCondition
|
||||
? gd::MetadataProvider::GetConditionMetadata(
|
||||
platform, instruction.GetType())
|
||||
: gd::MetadataProvider::GetActionMetadata(
|
||||
platform, instruction.GetType());
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("number", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("string", type))
|
||||
return; // Not an expression that can contain variables.
|
||||
|
||||
auto node = parameterValue.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionVariableReplacer renamer(platform,
|
||||
GetProjectScopedContainers(),
|
||||
targetVariablesContainer,
|
||||
oldToNewVariableNames,
|
||||
removedVariableNames);
|
||||
renamer.SetForcedInitialVariablesContainer(
|
||||
FindForcedVariablesContainerIfAny(type, lastObjectName));
|
||||
node->Visit(renamer);
|
||||
|
||||
if (renamer.IsRemovedVariableUsed()) {
|
||||
shouldDeleteInstruction = true;
|
||||
} else if (renamer.HasDoneRenaming()) {
|
||||
instruction.SetParameter(
|
||||
parameterIndex, ExpressionParser2NodePrinter::PrintNode(*node));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return shouldDeleteInstruction;
|
||||
}
|
||||
|
||||
bool EventsVariableReplacer::DoVisitEventExpression(
|
||||
gd::Expression& expression, const gd::ParameterMetadata& metadata) {
|
||||
const gd::String& type = metadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("number", type) &&
|
||||
!gd::ParameterMetadata::IsExpression("string", type))
|
||||
return false; // Not an expression that can contain variables.
|
||||
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionVariableReplacer renamer(platform,
|
||||
GetProjectScopedContainers(),
|
||||
targetVariablesContainer,
|
||||
oldToNewVariableNames,
|
||||
removedVariableNames);
|
||||
renamer.SetForcedInitialVariablesContainer(
|
||||
FindForcedVariablesContainerIfAny(type, ""));
|
||||
node->Visit(renamer);
|
||||
|
||||
if (renamer.IsRemovedVariableUsed()) {
|
||||
return true;
|
||||
} else if (renamer.HasDoneRenaming()) {
|
||||
expression = ExpressionParser2NodePrinter::PrintNode(*node);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EventsVariableReplacer::~EventsVariableReplacer() {}
|
||||
|
||||
} // namespace gd
|
60
Core/GDCore/IDE/Events/EventsVariableReplacer.h
Normal file
60
Core/GDCore/IDE/Events/EventsVariableReplacer.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 <map>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class BaseEvent;
|
||||
class VariablesContainer;
|
||||
class EventsList;
|
||||
class Platform;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Replace in expressions and in parameters of actions or conditions,
|
||||
* references to the name of a variable by another.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API EventsVariableReplacer
|
||||
: public ArbitraryEventsWorkerWithContext {
|
||||
public:
|
||||
EventsVariableReplacer(
|
||||
const gd::Platform &platform_,
|
||||
const gd::VariablesContainer &targetVariablesContainer_,
|
||||
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames_,
|
||||
const std::unordered_set<gd::String> &removedVariableNames_)
|
||||
: platform(platform_),
|
||||
targetVariablesContainer(targetVariablesContainer_),
|
||||
oldToNewVariableNames(oldToNewVariableNames_),
|
||||
removedVariableNames(removedVariableNames_){};
|
||||
virtual ~EventsVariableReplacer();
|
||||
|
||||
private:
|
||||
bool DoVisitInstruction(gd::Instruction &instruction,
|
||||
bool isCondition) override;
|
||||
bool DoVisitEventExpression(gd::Expression &expression,
|
||||
const gd::ParameterMetadata &metadata) override;
|
||||
|
||||
const gd::VariablesContainer *FindForcedVariablesContainerIfAny(
|
||||
const gd::String &type, const gd::String &lastObjectName);
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::VariablesContainer &targetVariablesContainer;
|
||||
gd::String objectName;
|
||||
const std::unordered_map<gd::String, gd::String> &oldToNewVariableNames;
|
||||
const std::unordered_set<gd::String> &removedVariableNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -17,6 +17,7 @@
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/IDE/DependenciesAnalyzer.h"
|
||||
|
||||
@@ -34,14 +35,12 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
public:
|
||||
VariableFinderExpressionNodeWorker(std::set<gd::String>& results_,
|
||||
const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String& parameterType_,
|
||||
const gd::String& objectName_ = "")
|
||||
: results(results_),
|
||||
platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
parameterType(parameterType_),
|
||||
objectName(objectName_){};
|
||||
virtual ~VariableFinderExpressionNodeWorker(){};
|
||||
@@ -60,6 +59,9 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
// We don't check variables or object variables here, because object variables only work
|
||||
// if the variable is already declared.
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
@@ -70,7 +72,10 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
// We don't check object variables here, because object variables only work
|
||||
// if the variable is already declared.
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
bool considerFunction = objectName.empty() || node.objectName == objectName;
|
||||
@@ -79,7 +84,7 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
const gd::ExpressionMetadata &metadata = isObjectFunction ?
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform,
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, objectName),
|
||||
projectScopedContainers.GetObjectsContainersList().GetTypeOfObject(objectName),
|
||||
node.functionName):
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, node.functionName);
|
||||
|
||||
@@ -110,8 +115,7 @@ class GD_CORE_API VariableFinderExpressionNodeWorker
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
|
||||
std::set<gd::String>& results; ///< Reference to the std::set where argument
|
||||
///< values must be stored.
|
||||
@@ -163,8 +167,7 @@ class GD_CORE_API VariableFinderEventWorker
|
||||
VariableFinderExpressionNodeWorker searcher(
|
||||
results,
|
||||
platform,
|
||||
GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
GetProjectScopedContainers(),
|
||||
parameterType,
|
||||
objectName);
|
||||
node->Visit(searcher);
|
||||
@@ -252,7 +255,8 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
parameterType,
|
||||
objectName);
|
||||
eventWorker.Launch(layout.GetEvents(), project, layout);
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
@@ -263,7 +267,8 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
parameterType,
|
||||
objectName);
|
||||
eventWorker.Launch(externalEvents.GetEvents(), project, layout);
|
||||
eventWorker.Launch(externalEvents.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
}
|
||||
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
|
||||
const gd::Layout& dependencyLayout = project.GetLayout(sceneName);
|
||||
@@ -272,7 +277,8 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
|
||||
platform,
|
||||
parameterType,
|
||||
objectName);
|
||||
eventWorker.Launch(dependencyLayout.GetEvents(), project, dependencyLayout);
|
||||
eventWorker.Launch(dependencyLayout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, dependencyLayout));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,21 +10,15 @@ namespace gd {
|
||||
const gd::ParameterMetadata
|
||||
ExpressionCompletionDescription::badParameterMetadata;
|
||||
|
||||
const gd::ObjectConfiguration
|
||||
ExpressionCompletionDescription::badObjectConfiguration;
|
||||
|
||||
/**
|
||||
* \brief Turn an ExpressionCompletionDescription to a string.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os,
|
||||
ExpressionCompletionDescription const& value) {
|
||||
os << "{ " << value.GetCompletionKind() << ", " << value.GetType() << ", "
|
||||
<< value.GetPrefix() << ", " << value.GetObjectName() << ", "
|
||||
<< value.GetBehaviorName() << ", "
|
||||
<< (value.IsExact() ? "exact" : "non-exact") << ", "
|
||||
<< (value.IsLastParameter() ? "last parameter" : "not last parameter")
|
||||
<< ", "
|
||||
<< (value.HasParameterMetadata()
|
||||
? gd::String::From(&value.GetParameterMetadata())
|
||||
: "no parameter metadata")
|
||||
<< " }";
|
||||
os << value.ToString();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -40,11 +41,10 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
* operations.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionLeftSideTypeFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer);
|
||||
platform, objectsContainersList);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -53,11 +53,9 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
|
||||
protected:
|
||||
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_)
|
||||
const gd::ObjectsContainersList &objectsContainersList_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
type("unknown") {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
@@ -85,7 +83,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, node);
|
||||
platform, objectsContainersList, node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
type = "unknown";
|
||||
}
|
||||
@@ -113,8 +111,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
gd::String type;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -21,6 +21,7 @@
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -31,7 +32,7 @@ namespace gd {
|
||||
/**
|
||||
* \brief Find the type of the expression or sub-expression that a given node
|
||||
* represents.
|
||||
*
|
||||
*
|
||||
* The type returned by this worker is a mix of:
|
||||
* - an expected type looking up like a parameter declaration
|
||||
* - an actual type looking down, but only looking at the most left branch
|
||||
@@ -50,12 +51,11 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
* sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionTypeFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer, rootType);
|
||||
platform, objectsContainersList, rootType);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -64,12 +64,10 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
|
||||
protected:
|
||||
ExpressionTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
rootType(rootType_),
|
||||
type(ExpressionTypeFinder::unknownType),
|
||||
child(nullptr) {};
|
||||
@@ -114,9 +112,8 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
type = ExpressionTypeFinder::unknownType;
|
||||
}
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
platform,
|
||||
objectsContainersList,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
@@ -129,7 +126,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (child == nullptr) {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, globalObjectsContainer, objectsContainer, node);
|
||||
platform, objectsContainersList, node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
VisitParent(node);
|
||||
}
|
||||
@@ -141,8 +138,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
gd::MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
objectsContainersList,
|
||||
node,
|
||||
*child);
|
||||
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
|
||||
@@ -162,9 +158,8 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
platform,
|
||||
objectsContainersList,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
@@ -188,8 +183,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
ExpressionNode *child;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -11,16 +11,20 @@
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Events/Expression.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Project/VariablesContainersList.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -59,61 +63,167 @@ size_t GetMaximumParametersNumber(
|
||||
|
||||
} // namespace
|
||||
|
||||
ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::FunctionCallNode& function) {
|
||||
bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
const gd::IdentifierNode& identifier) {
|
||||
auto validateVariableTypeForExpression =
|
||||
[this, &identifier](gd::Variable::Type type) {
|
||||
// Collections type can't be used directly in expressions, a child
|
||||
// must be accessed.
|
||||
if (type == Variable::Structure) {
|
||||
RaiseTypeError(_("You need to specify the name of the child variable "
|
||||
"to access. For example: `MyVariable.child`."),
|
||||
identifier.identifierNameLocation);
|
||||
} else if (type == Variable::Array) {
|
||||
RaiseTypeError(_("You need to specify the name of the child variable "
|
||||
"to access. For example: `MyVariable[0]`."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
} else {
|
||||
// Number, string or boolean variables can be used in expressions.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
|
||||
return projectScopedContainers.MatchIdentifierWithName<bool>(identifier.identifierName,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
if (identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("An object variable or expression should be entered."),
|
||||
identifier.identifierNameLocation);
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
if (!objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName)) {
|
||||
RaiseTypeError(_("This variable does not exist on this object or group."),
|
||||
identifier.identifierNameLocation);
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
return true; // We found a variable.
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
|
||||
// Try to identify a declared variable with the name (and maybe the child
|
||||
// variable).
|
||||
|
||||
const gd::Variable& variable =
|
||||
variablesContainersList.Get(identifier.identifierName);
|
||||
|
||||
if (identifier.childIdentifierName.empty()) {
|
||||
// Just the root variable is accessed, check it can be used in an
|
||||
// expression.
|
||||
validateVariableTypeForExpression(variable.GetType());
|
||||
|
||||
return true; // We found a variable.
|
||||
} else {
|
||||
// A child variable is accessed, check it can be used in an expression.
|
||||
if (!variable.HasChild(identifier.childIdentifierName)) {
|
||||
RaiseTypeError(_("No child variable with this name found."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
const gd::Variable& childVariable =
|
||||
variable.GetChild(identifier.childIdentifierName);
|
||||
return true; // We found a variable.
|
||||
}
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
return true; // We found a property, even if the child is not allowed.
|
||||
}
|
||||
|
||||
return true; // We found a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
if (!identifier.childIdentifierName.empty()) {
|
||||
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
return true; // We found a parameter, even if the child is not allowed.
|
||||
}
|
||||
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, identifier.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (!valueTypeMetadata.IsNumber() && !valueTypeMetadata.IsString() && !valueTypeMetadata.IsBoolean()) {
|
||||
RaiseTypeError(_("This parameter is not a string, number or boolean - it can't be used in an expression."),
|
||||
identifier.identifierNameLocation);
|
||||
return true; // We found a parameter, even though the type is incompatible.
|
||||
}
|
||||
|
||||
return true; // We found a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
ExpressionValidator::Type ExpressionValidator::ValidateFunction(
|
||||
const gd::FunctionCallNode& function) {
|
||||
ReportAnyError(function);
|
||||
|
||||
gd::String objectType = function.objectName.empty() ? "" :
|
||||
GetTypeOfObject(globalObjectsContainer, objectsContainer, function.objectName);
|
||||
|
||||
gd::String behaviorType = function.behaviorName.empty() ? "" :
|
||||
GetTypeOfBehavior(globalObjectsContainer, objectsContainer, function.behaviorName);
|
||||
|
||||
const gd::ExpressionMetadata &metadata = function.behaviorName.empty() ?
|
||||
function.objectName.empty() ?
|
||||
MetadataProvider::GetAnyExpressionMetadata(platform, function.functionName) :
|
||||
MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, function.functionName) :
|
||||
MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, function.functionName);
|
||||
auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
|
||||
gd::String objectType =
|
||||
function.objectName.empty()
|
||||
? ""
|
||||
: objectsContainersList.GetTypeOfObject(function.objectName);
|
||||
|
||||
gd::String behaviorType = function.behaviorName.empty()
|
||||
? ""
|
||||
: objectsContainersList.GetTypeOfBehavior(function.behaviorName);
|
||||
|
||||
const gd::ExpressionMetadata& metadata =
|
||||
function.behaviorName.empty()
|
||||
? function.objectName.empty()
|
||||
? MetadataProvider::GetAnyExpressionMetadata(
|
||||
platform, function.functionName)
|
||||
: MetadataProvider::GetObjectAnyExpressionMetadata(
|
||||
platform, objectType, function.functionName)
|
||||
: MetadataProvider::GetBehaviorAnyExpressionMetadata(
|
||||
platform, behaviorType, function.functionName);
|
||||
|
||||
Type returnType = StringToType(metadata.GetReturnType());
|
||||
|
||||
if (!function.objectName.empty() &&
|
||||
!globalObjectsContainer.HasObjectNamed(function.objectName) &&
|
||||
!globalObjectsContainer.GetObjectGroups().Has(function.objectName) &&
|
||||
!objectsContainer.HasObjectNamed(function.objectName) &&
|
||||
!objectsContainer.GetObjectGroups().Has(function.objectName)) {
|
||||
!objectsContainersList.HasObjectOrGroupNamed(function.objectName)) {
|
||||
RaiseTypeError(_("This object doesn't exist."),
|
||||
function.objectNameLocation, /*isFatal=*/false);
|
||||
function.objectNameLocation,
|
||||
/*isFatal=*/false);
|
||||
return returnType;
|
||||
}
|
||||
|
||||
if (!function.behaviorName.empty() &&
|
||||
!gd::HasBehaviorInObjectOrGroup(globalObjectsContainer, objectsContainer,
|
||||
function.objectName,
|
||||
function.behaviorName)) {
|
||||
!objectsContainersList.HasBehaviorInObjectOrGroup(function.objectName,
|
||||
function.behaviorName)) {
|
||||
RaiseTypeError(_("This behavior is not attached to this object."),
|
||||
function.behaviorNameLocation, /*isFatal=*/false);
|
||||
function.behaviorNameLocation,
|
||||
/*isFatal=*/false);
|
||||
return returnType;
|
||||
}
|
||||
|
||||
if (!function.objectName.empty() &&
|
||||
// If the function needs a capability on the object that may not be covered
|
||||
// by all objects, check it now.
|
||||
!metadata.GetRequiredBaseObjectCapability().empty()) {
|
||||
const gd::ObjectMetadata &objectMetadata =
|
||||
// If the function needs a capability on the object that may not be
|
||||
// covered by all objects, check it now.
|
||||
!metadata.GetRequiredBaseObjectCapability().empty()) {
|
||||
const gd::ObjectMetadata& objectMetadata =
|
||||
MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
}
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
RaiseError(
|
||||
"invalid_function_name",
|
||||
_("Cannot find an expression with this name: ") +
|
||||
function.functionName + "\n" +
|
||||
_("Double check that you've not made any typo in the name."),
|
||||
function.location);
|
||||
return returnType;
|
||||
RaiseError("invalid_function_name",
|
||||
_("Cannot find an expression with this name: ") +
|
||||
function.functionName + "\n" +
|
||||
_("Double check that you've not made any typo in the name."),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// Validate the type of the function
|
||||
@@ -128,9 +238,9 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
} else if (parentType != Type::Number &&
|
||||
parentType != Type::NumberOrString) {
|
||||
RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"number, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
"number, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
} else if (returnType == Type::String) {
|
||||
@@ -144,16 +254,16 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
} else if (parentType != Type::String &&
|
||||
parentType != Type::NumberOrString) {
|
||||
RaiseTypeError(_("You tried to use an expression that returns a "
|
||||
"string, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
"string, but another type is expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
} else {
|
||||
if (parentType != returnType) {
|
||||
RaiseTypeError(
|
||||
_("You tried to use an expression with the wrong return type:") + " " +
|
||||
TypeToString(returnType),
|
||||
_("You tried to use an expression with the wrong return type:") +
|
||||
" " + TypeToString(returnType),
|
||||
function.location);
|
||||
return returnType;
|
||||
}
|
||||
@@ -162,10 +272,12 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
// Validate parameters count
|
||||
size_t minParametersCount = GetMinimumParametersNumber(
|
||||
metadata.parameters,
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
|
||||
function.behaviorName));
|
||||
size_t maxParametersCount = GetMaximumParametersNumber(
|
||||
metadata.parameters,
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName, function.behaviorName));
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
|
||||
function.behaviorName));
|
||||
if (function.parameters.size() < minParametersCount ||
|
||||
function.parameters.size() > maxParametersCount) {
|
||||
gd::String expectedCountMessage =
|
||||
@@ -179,28 +291,29 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
if (function.parameters.size() < minParametersCount) {
|
||||
RaiseError(
|
||||
"too_few_parameters",
|
||||
_("You have not entered enough parameters for the expression.") + " " +
|
||||
expectedCountMessage,
|
||||
_("You have not entered enough parameters for the expression.") +
|
||||
" " + expectedCountMessage,
|
||||
function.location);
|
||||
} else {
|
||||
RaiseError(
|
||||
"extra_parameter",
|
||||
_("This parameter was not expected by this expression. Remove it "
|
||||
"or verify that you've entered the proper expression name.") + " " +
|
||||
expectedCountMessage,
|
||||
ExpressionParserLocation(
|
||||
function.parameters[maxParametersCount]->location.GetStartPosition(),
|
||||
function.location.GetEndPosition() - 1));
|
||||
"or verify that you've entered the proper expression name.") +
|
||||
" " + expectedCountMessage,
|
||||
ExpressionParserLocation(function.parameters[maxParametersCount]
|
||||
->location.GetStartPosition(),
|
||||
function.location.GetEndPosition() - 1));
|
||||
}
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// TODO: reverse the order of diagnostic?
|
||||
size_t writtenParametersFirstIndex =
|
||||
ExpressionParser2::WrittenParametersFirstIndex(
|
||||
function.objectName, function.behaviorName);
|
||||
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
|
||||
function.behaviorName);
|
||||
int metadataIndex = writtenParametersFirstIndex;
|
||||
for (int parameterIndex = 0; parameterIndex < function.parameters.size(); parameterIndex++) {
|
||||
for (int parameterIndex = 0; parameterIndex < function.parameters.size();
|
||||
parameterIndex++) {
|
||||
auto& parameter = function.parameters[parameterIndex];
|
||||
while (metadata.GetParameters()[metadataIndex].IsCodeOnly()) {
|
||||
// The sizes are already checked above.
|
||||
@@ -208,45 +321,45 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
}
|
||||
auto& parameterMetadata = metadata.GetParameters()[metadataIndex];
|
||||
|
||||
if (!parameterMetadata.IsOptional() || dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
|
||||
if (!parameterMetadata.IsOptional() ||
|
||||
dynamic_cast<EmptyNode*>(parameter.get()) == nullptr) {
|
||||
auto currentParentType = parentType;
|
||||
parentType = StringToType(parameterMetadata.GetType());
|
||||
parameter->Visit(*this);
|
||||
parentType = currentParentType;
|
||||
|
||||
const gd::String &expectedParameterType = parameterMetadata.GetType();
|
||||
|
||||
const gd::String& expectedParameterType = parameterMetadata.GetType();
|
||||
if (gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::variableTypeString, expectedParameterType)) {
|
||||
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr
|
||||
&& dynamic_cast<VariableNode *>(parameter.get()) == nullptr) {
|
||||
RaiseError(
|
||||
"malformed_variable_parameter",
|
||||
_("A variable name was expected but something else was "
|
||||
"written. Enter just the name of the variable for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
ExpressionValidator::variableTypeString, expectedParameterType)) {
|
||||
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr &&
|
||||
dynamic_cast<VariableNode*>(parameter.get()) == nullptr) {
|
||||
RaiseError("malformed_variable_parameter",
|
||||
_("A variable name was expected but something else was "
|
||||
"written. Enter just the name of the variable for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
}
|
||||
} else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
|
||||
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr) {
|
||||
RaiseError(
|
||||
"malformed_object_parameter",
|
||||
_("An object name was expected but something else was "
|
||||
"written. Enter just the name of the object for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
if (dynamic_cast<IdentifierNode*>(parameter.get()) == nullptr) {
|
||||
RaiseError("malformed_object_parameter",
|
||||
_("An object name was expected but something else was "
|
||||
"written. Enter just the name of the object for this "
|
||||
"parameter."),
|
||||
parameter->location);
|
||||
}
|
||||
}
|
||||
// String and number are already checked in children.
|
||||
else if (!gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::numberTypeString, expectedParameterType)
|
||||
&& !gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::stringTypeString, expectedParameterType)) {
|
||||
RaiseError(
|
||||
"unknown_parameter_type",
|
||||
_("This function is improperly set up. Reach out to the "
|
||||
"extension developer or a GDevelop maintainer to fix "
|
||||
"this issue"),
|
||||
parameter->location);
|
||||
ExpressionValidator::numberTypeString,
|
||||
expectedParameterType) &&
|
||||
!gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::stringTypeString,
|
||||
expectedParameterType)) {
|
||||
RaiseError("unknown_parameter_type",
|
||||
_("This function is improperly set up. Reach out to the "
|
||||
"extension developer or a GDevelop maintainer to fix "
|
||||
"this issue"),
|
||||
parameter->location);
|
||||
}
|
||||
}
|
||||
metadataIndex++;
|
||||
@@ -254,55 +367,60 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
|
||||
return returnType;
|
||||
}
|
||||
|
||||
// TODO factorize in a file with an enum and helpers?
|
||||
const gd::String ExpressionValidator::unknownTypeString = "unknown";
|
||||
const gd::String ExpressionValidator::numberTypeString = "number";
|
||||
const gd::String ExpressionValidator::stringTypeString = "string";
|
||||
const gd::String ExpressionValidator::numberOrStringTypeString = "number|string";
|
||||
const gd::String ExpressionValidator::variableTypeString = "variable";
|
||||
const gd::String ExpressionValidator::objectTypeString = "object";
|
||||
const gd::String ExpressionValidator::emptyTypeString = "empty";
|
||||
// TODO factorize in a file with an enum and helpers?
|
||||
const gd::String ExpressionValidator::unknownTypeString = "unknown";
|
||||
const gd::String ExpressionValidator::numberTypeString = "number";
|
||||
const gd::String ExpressionValidator::stringTypeString = "string";
|
||||
const gd::String ExpressionValidator::numberOrStringTypeString =
|
||||
"number|string";
|
||||
const gd::String ExpressionValidator::variableTypeString = "variable";
|
||||
const gd::String ExpressionValidator::objectTypeString = "object";
|
||||
const gd::String ExpressionValidator::emptyTypeString = "empty";
|
||||
|
||||
const gd::String &ExpressionValidator::TypeToString(Type type) {
|
||||
switch (type) {
|
||||
case Type::Unknown:
|
||||
const gd::String& ExpressionValidator::TypeToString(Type type) {
|
||||
switch (type) {
|
||||
case Type::Unknown:
|
||||
return unknownTypeString;
|
||||
case Type::Number:
|
||||
case Type::Number:
|
||||
return numberTypeString;
|
||||
case Type::String:
|
||||
case Type::String:
|
||||
return stringTypeString;
|
||||
case Type::NumberOrString:
|
||||
case Type::NumberOrString:
|
||||
return numberOrStringTypeString;
|
||||
case Type::Variable:
|
||||
case Type::Variable:
|
||||
return variableTypeString;
|
||||
case Type::Object:
|
||||
case Type::Object:
|
||||
return objectTypeString;
|
||||
case Type::Empty:
|
||||
case Type::Empty:
|
||||
return emptyTypeString;
|
||||
}
|
||||
return unknownTypeString;
|
||||
}
|
||||
return unknownTypeString;
|
||||
}
|
||||
|
||||
ExpressionValidator::Type ExpressionValidator::StringToType(const gd::String &type) {
|
||||
if (type == ExpressionValidator::numberTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString, type)) {
|
||||
return Type::Number;
|
||||
}
|
||||
if (type == ExpressionValidator::stringTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString, type)) {
|
||||
return Type::String;
|
||||
}
|
||||
if (type == ExpressionValidator::numberOrStringTypeString) {
|
||||
return Type::NumberOrString;
|
||||
}
|
||||
if (type == ExpressionValidator::variableTypeString
|
||||
|| gd::ParameterMetadata::IsExpression(ExpressionValidator::variableTypeString, type)) {
|
||||
return Type::Variable;
|
||||
}
|
||||
if (type == ExpressionValidator::objectTypeString
|
||||
|| gd::ParameterMetadata::IsObject(type)) {
|
||||
return Type::Object;
|
||||
}
|
||||
return Type::Unknown;
|
||||
ExpressionValidator::Type ExpressionValidator::StringToType(
|
||||
const gd::String& type) {
|
||||
if (type == ExpressionValidator::numberTypeString ||
|
||||
gd::ParameterMetadata::IsExpression(ExpressionValidator::numberTypeString,
|
||||
type)) {
|
||||
return Type::Number;
|
||||
}
|
||||
if (type == ExpressionValidator::stringTypeString ||
|
||||
gd::ParameterMetadata::IsExpression(ExpressionValidator::stringTypeString,
|
||||
type)) {
|
||||
return Type::String;
|
||||
}
|
||||
if (type == ExpressionValidator::numberOrStringTypeString) {
|
||||
return Type::NumberOrString;
|
||||
}
|
||||
if (type == ExpressionValidator::variableTypeString ||
|
||||
gd::ParameterMetadata::IsExpression(
|
||||
ExpressionValidator::variableTypeString, type)) {
|
||||
return Type::Variable;
|
||||
}
|
||||
if (type == ExpressionValidator::objectTypeString ||
|
||||
gd::ParameterMetadata::IsObject(type)) {
|
||||
return Type::Object;
|
||||
}
|
||||
return Type::Unknown;
|
||||
}
|
||||
} // namespace gd
|
||||
|
@@ -13,13 +13,18 @@
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/VariablesContainersList.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class VariablesContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
class VariablesContainersList;
|
||||
class ProjectScopedContainers;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -33,14 +38,13 @@ namespace gd {
|
||||
class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionValidator(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers & projectScopedContainers_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
parentType(StringToType(gd::ParameterMetadata::GetExpressionValueType(rootType_))),
|
||||
childType(Type::Unknown) {};
|
||||
childType(Type::Unknown),
|
||||
forbidsUsageOfBracketsBecauseParentIsObject(false) {};
|
||||
virtual ~ExpressionValidator(){};
|
||||
|
||||
/**
|
||||
@@ -48,11 +52,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
* any error including non-fatal ones.
|
||||
*/
|
||||
static bool HasNoErrors(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ProjectScopedContainers & projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionValidator validator(platform, globalObjectsContainer, objectsContainer, rootType);
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, rootType);
|
||||
node.Visit(validator);
|
||||
return validator.GetAllErrors().empty();
|
||||
}
|
||||
@@ -82,7 +85,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
|
||||
|
||||
node.leftHandSide->Visit(*this);
|
||||
const Type leftType = childType;
|
||||
|
||||
@@ -185,28 +188,69 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
ReportAnyError(node);
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
childType = Type::Variable;
|
||||
if (parentType == Type::String) {
|
||||
RaiseTypeError(_("Variables must be surrounded by VariableString()."),
|
||||
node.location);
|
||||
} else if (parentType == Type::Number) {
|
||||
RaiseTypeError(_("Variables must be surrounded by Variable()."),
|
||||
node.location);
|
||||
} else if (parentType == Type::NumberOrString) {
|
||||
RaiseTypeError(
|
||||
_("Variables must be surrounded by Variable() or VariableString()."),
|
||||
node.location);
|
||||
} else if (parentType != Type::Variable) {
|
||||
|
||||
if (parentType == Type::Variable) {
|
||||
childType = Type::Variable;
|
||||
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
} 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;
|
||||
|
||||
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
|
||||
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
|
||||
const auto& propertiesContainerList = projectScopedContainers.GetPropertiesContainersList();
|
||||
|
||||
forbidsUsageOfBracketsBecauseParentIsObject = false;
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
|
||||
// While understood by the parser, it's forbidden to use the bracket notation just after
|
||||
// an object name (`MyObject["MyVariable"]`).
|
||||
forbidsUsageOfBracketsBecauseParentIsObject = true;
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
// Being in this node implies that there is at least a child - which is not supported for properties.
|
||||
RaiseTypeError(_("Accessing a child variable of a property is not possible - just write the property name."),
|
||||
node.location);
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
// Being in this node implies that there is at least a child - which is not supported for parameters.
|
||||
RaiseTypeError(_("Accessing a child variable of a parameter is not possible - just write the parameter name."),
|
||||
node.location);
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
RaiseTypeError(_("No object, variable or property with this name found."),
|
||||
node.location);
|
||||
});
|
||||
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
|
||||
forbidsUsageOfBracketsBecauseParentIsObject = false;
|
||||
} else {
|
||||
RaiseTypeError(_("You entered a variable, but this type was expected:") +
|
||||
" " + TypeToString(parentType),
|
||||
node.location);
|
||||
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
|
||||
// In the case we accessed an object variable (`MyObject.MyVariable`),
|
||||
// brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid).
|
||||
forbidsUsageOfBracketsBecauseParentIsObject = false;
|
||||
|
||||
if (node.child) {
|
||||
node.child->Visit(*this);
|
||||
}
|
||||
@@ -215,6 +259,15 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
VariableBracketAccessorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
|
||||
if (forbidsUsageOfBracketsBecauseParentIsObject) {
|
||||
RaiseError("brackets_not_allowed_for_objects",
|
||||
_("You can't use the brackets to access an object variable. "
|
||||
"Use a dot followed by the variable name, like this: "
|
||||
"`MyObject.MyVariable`."),
|
||||
node.location);
|
||||
}
|
||||
forbidsUsageOfBracketsBecauseParentIsObject = false;
|
||||
|
||||
Type currentParentType = parentType;
|
||||
parentType = Type::NumberOrString;
|
||||
node.expression->Visit(*this);
|
||||
@@ -227,19 +280,29 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
ReportAnyError(node);
|
||||
if (parentType == Type::String) {
|
||||
RaiseTypeError(_("You must wrap your text inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
node.location);
|
||||
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
|
||||
// The identifier is not a variable, so either the variable is not properly declared
|
||||
// or it's a text without quotes.
|
||||
RaiseTypeError(_("You must wrap your text inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
node.location);
|
||||
}
|
||||
}
|
||||
else if (parentType == Type::Number) {
|
||||
RaiseTypeError(
|
||||
_("You must enter a number."), node.location);
|
||||
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
|
||||
// The identifier is not a variable, so the variable is not properly declared.
|
||||
RaiseTypeError(
|
||||
_("You must enter a number."), node.location);
|
||||
}
|
||||
}
|
||||
else if (parentType == Type::NumberOrString) {
|
||||
RaiseTypeError(
|
||||
_("You must enter a number or a text, wrapped inside double quotes "
|
||||
"(example: \"Hello world\")."),
|
||||
node.location);
|
||||
if (!ValidateObjectVariableOrVariableOrProperty(node)) {
|
||||
// The identifier is not a variable, so either the variable is not properly declared
|
||||
// or it's a text without quotes.
|
||||
RaiseTypeError(
|
||||
_("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::Object && parentType != Type::Variable) {
|
||||
// It can't happen.
|
||||
@@ -278,6 +341,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
private:
|
||||
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
|
||||
Type ValidateFunction(const gd::FunctionCallNode& function);
|
||||
bool ValidateObjectVariableOrVariableOrProperty(const gd::IdentifierNode& identifier);
|
||||
|
||||
void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
|
||||
if (node.diagnostic && node.diagnostic->IsError()) {
|
||||
@@ -291,7 +355,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
}
|
||||
|
||||
void RaiseError(const gd::String &type,
|
||||
void RaiseError(const gd::String &type,
|
||||
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) {
|
||||
auto diagnostic = gd::make_unique<ExpressionParserError>(
|
||||
type, message, location);
|
||||
@@ -329,11 +393,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
std::vector<ExpressionParserDiagnostic*> fatalErrors;
|
||||
std::vector<ExpressionParserDiagnostic*> allErrors;
|
||||
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
|
||||
Type childType;
|
||||
Type parentType;
|
||||
Type childType; ///< The type "discovered" down the tree and passed up.
|
||||
Type parentType; ///< The type "required" by the top of the tree.
|
||||
bool forbidsUsageOfBracketsBecauseParentIsObject;
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ObjectsContainersList.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -31,6 +32,10 @@ namespace gd {
|
||||
* \brief Find the object name that should be used as a context of the
|
||||
* expression or sub-expression that a given node represents.
|
||||
*
|
||||
* This is needed because of the legacy convention where a "objectvar"
|
||||
* parameter represents a variable of the object represented by the previous "object"
|
||||
* parameter.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWorker {
|
||||
@@ -41,12 +46,11 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
|
||||
* context of the expression or sub-expression that a given node represents.
|
||||
*/
|
||||
static const gd::String GetObjectName(const gd::Platform &platform,
|
||||
const gd::ObjectsContainer &globalObjectsContainer,
|
||||
const gd::ObjectsContainer &objectsContainer,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::String& rootObjectName,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionVariableOwnerFinder typeFinder(
|
||||
platform, globalObjectsContainer, objectsContainer, rootObjectName);
|
||||
platform, objectsContainersList, rootObjectName);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetObjectName();
|
||||
}
|
||||
@@ -55,12 +59,10 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
|
||||
|
||||
protected:
|
||||
ExpressionVariableOwnerFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::String& rootObjectName_)
|
||||
: platform(platform_),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
rootObjectName(rootObjectName_),
|
||||
objectName(""),
|
||||
variableNode(nullptr) {};
|
||||
@@ -126,9 +128,8 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
|
||||
}
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
platform,
|
||||
objectsContainersList,
|
||||
functionCall,
|
||||
parameterIndex);
|
||||
if (parameterMetadata == nullptr
|
||||
@@ -142,9 +143,8 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
|
||||
for (int previousIndex = parameterIndex - 1; previousIndex >= 0; previousIndex--) {
|
||||
const gd::ParameterMetadata* previousParameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
platform,
|
||||
objectsContainersList,
|
||||
functionCall,
|
||||
previousIndex);
|
||||
if (previousParameterMetadata != nullptr
|
||||
@@ -162,8 +162,7 @@ class GD_CORE_API ExpressionVariableOwnerFinder : public ExpressionParser2NodeWo
|
||||
gd::ExpressionNode *variableNode;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::String &rootObjectName;
|
||||
};
|
||||
|
||||
|
@@ -31,16 +31,14 @@ namespace gd {
|
||||
class GD_CORE_API ExpressionParameterMover
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionParameterMover(const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_,
|
||||
ExpressionParameterMover(const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::String& behaviorType_,
|
||||
const gd::String& objectType_,
|
||||
const gd::String& functionName_,
|
||||
std::size_t oldIndex_,
|
||||
std::size_t newIndex_)
|
||||
: hasDoneMoving(false),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
behaviorType(behaviorType_),
|
||||
objectType(objectType_),
|
||||
functionName(functionName_),
|
||||
@@ -98,16 +96,16 @@ class GD_CORE_API ExpressionParameterMover
|
||||
// This refactor only applies on events object functions
|
||||
// and events object functions doesn't exist yet.
|
||||
// This is a dead code.
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
const gd::String& thisObjectType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
|
||||
if (thisObjectType == objectType) {
|
||||
moveParameter(node.parameters, 1);
|
||||
hasDoneMoving = true;
|
||||
}
|
||||
} else if (!behaviorType.empty() && !node.behaviorName.empty()) {
|
||||
// Move parameter of a behavior function
|
||||
const gd::String& thisBehaviorType = gd::GetTypeOfBehavior(
|
||||
globalObjectsContainer, objectsContainer, node.behaviorName);
|
||||
const gd::String& thisBehaviorType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfBehavior(node.behaviorName);
|
||||
if (thisBehaviorType == behaviorType) {
|
||||
moveParameter(node.parameters, 2);
|
||||
hasDoneMoving = true;
|
||||
@@ -126,8 +124,7 @@ class GD_CORE_API ExpressionParameterMover
|
||||
|
||||
private:
|
||||
bool hasDoneMoving;
|
||||
const gd::ObjectsContainer& globalObjectsContainer;
|
||||
const gd::ObjectsContainer& objectsContainer;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
const gd::String& behaviorType; // The behavior type of the function which
|
||||
// must have a parameter moved (optional).
|
||||
const gd::String& objectType; // The object type of the function which
|
||||
@@ -155,8 +152,7 @@ bool ExpressionsParameterMover::DoVisitInstruction(gd::Instruction& instruction,
|
||||
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionParameterMover mover(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
ExpressionParameterMover mover(GetProjectScopedContainers(),
|
||||
behaviorType,
|
||||
objectType,
|
||||
functionName,
|
||||
|
@@ -23,22 +23,20 @@
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Go through the nodes and change the given object name to a new one.
|
||||
* \brief Go through the nodes and change the given function name to a new name.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionFunctionRenamer
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
ExpressionFunctionRenamer(const gd::ObjectsContainer& globalObjectsContainer_,
|
||||
const gd::ObjectsContainer& objectsContainer_,
|
||||
ExpressionFunctionRenamer(const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::String& behaviorType_,
|
||||
const gd::String& objectType_,
|
||||
const gd::String& oldFunctionName_,
|
||||
const gd::String& newFunctionName_)
|
||||
: hasDoneRenaming(false),
|
||||
globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
behaviorType(behaviorType_),
|
||||
objectType(objectType_),
|
||||
oldFunctionName(oldFunctionName_),
|
||||
@@ -71,16 +69,18 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
node.expression->Visit(*this);
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
// Nothing to do as this is either a variable, an object variable a property or something else
|
||||
// but not an expression.
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
// Behavior function name
|
||||
if (!behaviorType.empty() &&
|
||||
node.behaviorFunctionName == oldFunctionName) {
|
||||
const gd::String& thisBehaviorType =
|
||||
gd::GetTypeOfBehavior(globalObjectsContainer,
|
||||
objectsContainer,
|
||||
node.objectFunctionOrBehaviorName);
|
||||
const gd::String& thisBehaviorType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfBehavior(
|
||||
node.objectFunctionOrBehaviorName);
|
||||
if (thisBehaviorType == behaviorType) {
|
||||
node.behaviorFunctionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
@@ -90,8 +90,8 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
// Object function name
|
||||
if (behaviorType.empty() && !objectType.empty() &&
|
||||
node.objectFunctionOrBehaviorName == oldFunctionName) {
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
const gd::String& thisObjectType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
|
||||
if (thisObjectType == objectType) {
|
||||
node.objectFunctionOrBehaviorName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
@@ -104,16 +104,16 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
if (behaviorType.empty() && !objectType.empty() &&
|
||||
!node.objectName.empty()) {
|
||||
// Replace an object function
|
||||
const gd::String& thisObjectType = gd::GetTypeOfObject(
|
||||
globalObjectsContainer, objectsContainer, node.objectName);
|
||||
const gd::String& thisObjectType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfObject(node.objectName);
|
||||
if (thisObjectType == objectType) {
|
||||
node.functionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
}
|
||||
} else if (!behaviorType.empty() && !node.behaviorName.empty()) {
|
||||
// Replace a behavior function
|
||||
const gd::String& thisBehaviorType = gd::GetTypeOfBehavior(
|
||||
globalObjectsContainer, objectsContainer, node.behaviorName);
|
||||
const gd::String& thisBehaviorType = projectScopedContainers
|
||||
.GetObjectsContainersList().GetTypeOfBehavior(node.behaviorName);
|
||||
if (thisBehaviorType == behaviorType) {
|
||||
node.functionName = newFunctionName;
|
||||
hasDoneRenaming = true;
|
||||
@@ -132,8 +132,7 @@ class GD_CORE_API ExpressionFunctionRenamer
|
||||
|
||||
private:
|
||||
bool hasDoneRenaming;
|
||||
const gd::ObjectsContainer& globalObjectsContainer;
|
||||
const gd::ObjectsContainer& objectsContainer;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
const gd::String& behaviorType; // The behavior type for which the expression
|
||||
// must be replaced (optional).
|
||||
const gd::String& objectType; // The object type for which the expression
|
||||
@@ -159,8 +158,7 @@ bool ExpressionsRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
|
||||
auto node = expression.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionFunctionRenamer renamer(GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(),
|
||||
ExpressionFunctionRenamer renamer(GetProjectScopedContainers(),
|
||||
behaviorType,
|
||||
objectType,
|
||||
oldFunctionName,
|
||||
|
@@ -34,13 +34,12 @@ class GD_CORE_API ExpressionIdentifierStringFinder
|
||||
public:
|
||||
ExpressionIdentifierStringFinder(
|
||||
const gd::Platform &platform_,
|
||||
const gd::ObjectsContainer &globalObjectsContainer_,
|
||||
const gd::ObjectsContainer &objectsContainer_,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_,
|
||||
const gd::String &expressionPlainString_,
|
||||
const gd::String ¶meterType_, const gd::String &objectName_,
|
||||
const gd::String &layerName_, const gd::String &oldName_)
|
||||
: platform(platform_), globalObjectsContainer(globalObjectsContainer_),
|
||||
objectsContainer(objectsContainer_),
|
||||
: platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
expressionPlainString(expressionPlainString_),
|
||||
parameterType(parameterType_), objectName(objectName_),
|
||||
layerName(layerName_), oldName(oldName_){};
|
||||
@@ -82,7 +81,7 @@ protected:
|
||||
void OnVisitFunctionCallNode(FunctionCallNode &node) override {
|
||||
gd::String lastLayerName;
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
platform, globalObjectsContainer, objectsContainer, node,
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node,
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
std::unique_ptr<gd::ExpressionNode> ¶meterNode,
|
||||
size_t parameterIndex, const gd::String &lastObjectName) {
|
||||
@@ -123,8 +122,7 @@ protected:
|
||||
|
||||
private:
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainer &globalObjectsContainer;
|
||||
const gd::ObjectsContainer &objectsContainer;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
/// It's used to extract parameter content.
|
||||
const gd::String &expressionPlainString;
|
||||
const gd::String &oldName;
|
||||
@@ -176,7 +174,7 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
|
||||
auto node = parameterValue.GetRootNode();
|
||||
if (node) {
|
||||
ExpressionIdentifierStringFinder finder(
|
||||
platform, GetGlobalObjectsContainer(), GetObjectsContainer(),
|
||||
platform, GetProjectScopedContainers(),
|
||||
parameterValue.GetPlainString(), parameterType, objectName,
|
||||
layerName, oldName);
|
||||
node->Visit(finder);
|
||||
|
@@ -108,6 +108,33 @@ void UsedExtensionsFinder::OnVisitUnaryOperatorNode(UnaryOperatorNode& node) {
|
||||
// Add variable extension and visit sub-expressions on variable nodes
|
||||
void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
|
||||
result.GetUsedExtensions().insert("BuiltinVariables");
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
} else {
|
||||
GetProjectScopedContainers().MatchIdentifierWithName<void>(node.name,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
project.GetCurrentPlatform(), node.name);
|
||||
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
|
||||
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
|
||||
result.GetUsedIncludeFiles().insert(includeFile);
|
||||
}
|
||||
}, [&]() {
|
||||
// This is a variable.
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
}, [&]() {
|
||||
// This is a parameter.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
});
|
||||
}
|
||||
|
||||
if (node.child) node.child->Visit(*this);
|
||||
};
|
||||
|
||||
@@ -127,9 +154,10 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
|
||||
// Add extensions bound to Objects/Behaviors/Functions
|
||||
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetGlobalObjectsContainer(),
|
||||
GetObjectsContainer(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) ||
|
||||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
|
||||
// An object or object variable is used.
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
project.GetCurrentPlatform(), node.identifierName);
|
||||
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
|
||||
|
@@ -16,7 +16,9 @@
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -86,15 +88,17 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
|
||||
// Add layouts events
|
||||
worker.Launch(layout.GetEvents(), project, layout);
|
||||
worker.Launch(layout.GetEvents(), projectScopedContainers);
|
||||
|
||||
// Add external events events
|
||||
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
|
||||
auto &externalEvents = project.GetExternalEvents(s);
|
||||
if (externalEvents.GetAssociatedLayout() == layout.GetName()) {
|
||||
worker.Launch(externalEvents.GetEvents(), project, layout);
|
||||
worker.Launch(externalEvents.GetEvents(), projectScopedContainers);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,15 +112,19 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
// Add layouts events
|
||||
for (std::size_t s = 0; s < project.GetLayoutsCount(); s++) {
|
||||
auto &layout = project.GetLayout(s);
|
||||
worker.Launch(layout.GetEvents(), project, layout);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
worker.Launch(layout.GetEvents(), projectScopedContainers);
|
||||
}
|
||||
// Add external events events
|
||||
for (std::size_t s = 0; s < project.GetExternalEventsCount(); s++) {
|
||||
const auto &externalEvents = project.GetExternalEvents(s);
|
||||
const gd::String &associatedLayout = externalEvents.GetAssociatedLayout();
|
||||
if (project.HasLayoutNamed(associatedLayout)) {
|
||||
worker.Launch(project.GetExternalEvents(s).GetEvents(), project,
|
||||
project.GetLayout(associatedLayout));
|
||||
auto &layout = project.GetLayout(associatedLayout);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
worker.Launch(project.GetExternalEvents(s).GetEvents(), projectScopedContainers);
|
||||
}
|
||||
}
|
||||
// Add events based extensions
|
||||
@@ -130,9 +138,11 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
|
||||
project, eventsFunctionsExtension, *eventsFunction,
|
||||
globalObjectsAndGroups, objectsAndGroups);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
|
||||
// Add (behavior) events functions
|
||||
@@ -169,9 +179,13 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
gd::EventsFunctionTools::BehaviorEventsFunctionToObjectsContainer(
|
||||
project, eventsBasedBehavior, *eventsFunction, globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetSharedPropertyDescriptors());
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +199,12 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
|
||||
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedObject.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
#include "WholeProjectRefactorer.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
@@ -15,6 +17,8 @@
|
||||
#include "GDCore/IDE/Events/CustomObjectTypeRenamer.h"
|
||||
#include "GDCore/IDE/Events/EventsBehaviorRenamer.h"
|
||||
#include "GDCore/IDE/Events/EventsRefactorer.h"
|
||||
#include "GDCore/IDE/Events/EventsPropertyReplacer.h"
|
||||
#include "GDCore/IDE/Events/EventsVariableReplacer.h"
|
||||
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
|
||||
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
|
||||
#include "GDCore/IDE/Events/InstructionsParameterMover.h"
|
||||
@@ -46,6 +50,8 @@
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/ObjectGroup.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
@@ -132,6 +138,72 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
|
||||
}
|
||||
}
|
||||
|
||||
VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::SerializerElement &oldSerializedVariablesContainer,
|
||||
const gd::VariablesContainer &newVariablesContainer) {
|
||||
gd::VariablesChangeset changeset;
|
||||
|
||||
gd::VariablesContainer oldVariablesContainer;
|
||||
oldVariablesContainer.UnserializeFrom(oldSerializedVariablesContainer);
|
||||
|
||||
if (oldVariablesContainer.GetPersistentUuid() !=
|
||||
newVariablesContainer.GetPersistentUuid()) {
|
||||
gd::LogWarning(
|
||||
_("Called ComputeChangesetForVariablesContainer on variables containers "
|
||||
"that are different - they can't be compared."));
|
||||
return changeset;
|
||||
}
|
||||
|
||||
std::unordered_map<gd::String, gd::String> removedUuidAndNames;
|
||||
for (std::size_t i = 0; i < oldVariablesContainer.Count(); ++i) {
|
||||
const auto &variable = oldVariablesContainer.Get(i);
|
||||
const auto &variableName = oldVariablesContainer.GetNameAt(i);
|
||||
|
||||
// All variables are candidate to be removed.
|
||||
removedUuidAndNames[variable.GetPersistentUuid()] = variableName;
|
||||
}
|
||||
for (std::size_t i = 0; i < newVariablesContainer.Count(); ++i) {
|
||||
const auto &variable = newVariablesContainer.Get(i);
|
||||
const auto &variableName = newVariablesContainer.GetNameAt(i);
|
||||
|
||||
auto existingOldVariableUuidAndName =
|
||||
removedUuidAndNames.find(variable.GetPersistentUuid());
|
||||
if (existingOldVariableUuidAndName == removedUuidAndNames.end()) {
|
||||
// This is a new variable.
|
||||
} else {
|
||||
const gd::String &oldName = existingOldVariableUuidAndName->second;
|
||||
|
||||
if (oldName != variableName) {
|
||||
// This is a renamed variable.
|
||||
changeset.oldToNewVariableNames[oldName] = variableName;
|
||||
}
|
||||
|
||||
// Renamed or not, this is not a removed variable.
|
||||
removedUuidAndNames.erase(variable.GetPersistentUuid());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &removedUuidAndName : removedUuidAndNames) {
|
||||
changeset.removedVariableNames.insert(removedUuidAndName.second);
|
||||
}
|
||||
|
||||
return changeset;
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::VariablesContainer &newVariablesContainer,
|
||||
const gd::VariablesChangeset& changeset) {
|
||||
gd::EventsVariableReplacer eventsVariableReplacer(
|
||||
project.GetCurrentPlatform(),
|
||||
newVariablesContainer,
|
||||
changeset.oldToNewVariableNames,
|
||||
changeset.removedVariableNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
eventsVariableReplacer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::UpdateExtensionNameInEventsBasedBehavior(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
@@ -671,6 +743,15 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
|
||||
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
std::unordered_set<gd::String> removedPropertyNames;
|
||||
gd::EventsPropertyReplacer eventsPropertyReplacer(
|
||||
project.GetCurrentPlatform(),
|
||||
properties,
|
||||
oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
@@ -698,7 +779,7 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
const gd::EventsBasedBehavior &eventsBasedBehavior,
|
||||
const gd::String &oldPropertyName, const gd::String &newPropertyName) {
|
||||
auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
|
||||
auto &properties = eventsBasedBehavior.GetSharedPropertyDescriptors();
|
||||
if (!properties.Has(oldPropertyName))
|
||||
return;
|
||||
|
||||
@@ -732,6 +813,15 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
|
||||
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
std::unordered_set<gd::String> removedPropertyNames;
|
||||
gd::EventsPropertyReplacer eventsPropertyReplacer(
|
||||
project.GetCurrentPlatform(),
|
||||
properties,
|
||||
oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
gd::PlatformExtension::GetBehaviorEventsFunctionFullType(
|
||||
@@ -780,6 +870,15 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
|
||||
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
std::unordered_set<gd::String> removedPropertyNames;
|
||||
gd::EventsPropertyReplacer eventsPropertyReplacer(
|
||||
project.GetCurrentPlatform(),
|
||||
properties,
|
||||
oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
gd::PlatformExtension::GetObjectEventsFunctionFullType(
|
||||
@@ -1299,10 +1398,12 @@ void WholeProjectRefactorer::DoRenameObject(
|
||||
void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
gd::Project &project, gd::Layout &layout, const gd::String &objectName,
|
||||
bool isObjectGroup, bool removeEventsAndGroups) {
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
|
||||
// Remove object in the current layout
|
||||
if (removeEventsAndGroups) {
|
||||
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
|
||||
project, layout,
|
||||
projectScopedContainers,
|
||||
layout.GetEvents(), objectName);
|
||||
}
|
||||
if (!isObjectGroup) { // Object groups can't have instances or be in other
|
||||
@@ -1321,8 +1422,9 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
|
||||
for (auto &externalEventsName :
|
||||
GetAssociatedExternalEvents(project, layout.GetName())) {
|
||||
auto &externalEvents = project.GetExternalEvents(externalEventsName);
|
||||
|
||||
gd::EventsRefactorer::RemoveObjectInEvents(
|
||||
project.GetCurrentPlatform(), project, layout,
|
||||
project.GetCurrentPlatform(), projectScopedContainers,
|
||||
externalEvents.GetEvents(), objectName);
|
||||
}
|
||||
}
|
||||
@@ -1344,9 +1446,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
const gd::String &newName, bool isObjectGroup) {
|
||||
if (oldName == newName || newName.empty() || oldName.empty())
|
||||
return;
|
||||
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
|
||||
// Rename object in the current layout
|
||||
gd::EventsRefactorer::RenameObjectInEvents(
|
||||
project.GetCurrentPlatform(), project, layout, layout.GetEvents(),
|
||||
project.GetCurrentPlatform(), projectScopedContainers, layout.GetEvents(),
|
||||
oldName, newName);
|
||||
|
||||
if (!isObjectGroup) { // Object groups can't have instances or be in other
|
||||
@@ -1362,7 +1467,7 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
GetAssociatedExternalEvents(project, layout.GetName())) {
|
||||
auto &externalEvents = project.GetExternalEvents(externalEventsName);
|
||||
gd::EventsRefactorer::RenameObjectInEvents(
|
||||
project.GetCurrentPlatform(), project, layout,
|
||||
project.GetCurrentPlatform(), projectScopedContainers,
|
||||
externalEvents.GetEvents(), oldName, newName);
|
||||
}
|
||||
|
||||
@@ -1512,10 +1617,13 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer, const gd::String &objectName,
|
||||
bool isObjectGroup, bool removeEventsAndGroups) {
|
||||
// Remove object in the current layout
|
||||
// In theory we should pass a ProjectScopedContainers to this function so it does not have to construct one.
|
||||
// In practice, this is ok because we only deal with objects.
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsContainer, objectsContainer);
|
||||
|
||||
if (removeEventsAndGroups) {
|
||||
gd::EventsRefactorer::RemoveObjectInEvents(
|
||||
project.GetCurrentPlatform(), globalObjectsContainer, objectsContainer,
|
||||
project.GetCurrentPlatform(), projectScopedContainers,
|
||||
eventsFunction.GetEvents(), objectName);
|
||||
}
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
@@ -1547,9 +1655,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer, const gd::String &oldName,
|
||||
const gd::String &newName, bool isObjectGroup) {
|
||||
// Rename object in the current layout
|
||||
// In theory we should pass a ProjectScopedContainers to this function so it does not have to construct one.
|
||||
// In practice, this is ok because we only deal with objects.
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsContainer, objectsContainer);
|
||||
|
||||
gd::EventsRefactorer::RenameObjectInEvents(
|
||||
project.GetCurrentPlatform(), globalObjectsContainer, objectsContainer,
|
||||
project.GetCurrentPlatform(), projectScopedContainers,
|
||||
eventsFunction.GetEvents(), oldName, newName);
|
||||
|
||||
if (!isObjectGroup) { // Object groups can't be in other groups
|
||||
@@ -1594,7 +1705,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
if (layout.HasObjectNamed(objectName))
|
||||
continue;
|
||||
|
||||
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
|
||||
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
|
||||
removeEventsAndGroups);
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,9 @@
|
||||
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Platform;
|
||||
class Project;
|
||||
@@ -18,6 +20,7 @@ class String;
|
||||
class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
class ObjectsContainer;
|
||||
class VariablesContainer;
|
||||
class EventsBasedBehavior;
|
||||
class EventsBasedObject;
|
||||
class ArbitraryEventsWorker;
|
||||
@@ -30,10 +33,20 @@ class Behavior;
|
||||
class BehaviorMetadata;
|
||||
class UnfilledRequiredBehaviorPropertyProblem;
|
||||
class ProjectBrowser;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
struct VariablesChangeset {
|
||||
std::unordered_set<gd::String> removedVariableNames;
|
||||
std::unordered_map<gd::String, gd::String> oldToNewVariableNames;
|
||||
|
||||
bool HasRemovedVariables() { return !removedVariableNames.empty(); }
|
||||
|
||||
VariablesChangeset& ClearRemovedVariables() { removedVariableNames.clear(); return *this; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Tool functions to do refactoring on the whole project after
|
||||
* changes like deletion or renaming of an object.
|
||||
@@ -45,6 +58,23 @@ namespace gd {
|
||||
class GD_CORE_API WholeProjectRefactorer {
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Compute the changes made on the variables of a variable container.
|
||||
*/
|
||||
static VariablesChangeset ComputeChangesetForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::SerializerElement &oldSerializedVariablesContainer,
|
||||
const gd::VariablesContainer &newVariablesContainer);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project according to the changes (renaming or deletion)
|
||||
* made to variables.
|
||||
*/
|
||||
static void ApplyRefactoringForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::VariablesContainer &newVariablesContainer,
|
||||
const gd::VariablesChangeset& changeset);
|
||||
|
||||
/**
|
||||
* \brief Refactor the project **before** an events function extension is
|
||||
* renamed.
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "AbstractEventsBasedEntity.h"
|
||||
|
||||
#include "EventsFunctionsContainer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/MakeUnique.h"
|
||||
@@ -13,7 +14,10 @@ namespace gd {
|
||||
AbstractEventsBasedEntity::AbstractEventsBasedEntity(
|
||||
const gd::String& _name,
|
||||
gd::EventsFunctionsContainer::FunctionOwner functionContainerSource)
|
||||
: name(_name), fullName(""), eventsFunctionsContainer(functionContainerSource) {}
|
||||
: name(_name),
|
||||
fullName(""),
|
||||
eventsFunctionsContainer(functionContainerSource),
|
||||
propertyDescriptors(functionContainerSource) {}
|
||||
|
||||
void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("description", description);
|
||||
@@ -27,8 +31,8 @@ void AbstractEventsBasedEntity::SerializeTo(SerializerElement& element) const {
|
||||
"propertyDescriptor", element.AddChild("propertyDescriptors"));
|
||||
}
|
||||
|
||||
void AbstractEventsBasedEntity::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
void AbstractEventsBasedEntity::UnserializeFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
description = element.GetStringAttribute("description");
|
||||
name = element.GetStringAttribute("name");
|
||||
fullName = element.GetStringAttribute("fullName");
|
||||
|
@@ -8,7 +8,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include "GDCore/Project/NamedPropertyDescriptor.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
@@ -98,15 +98,14 @@ class GD_CORE_API AbstractEventsBasedEntity {
|
||||
/**
|
||||
* \brief Return a reference to the list of the properties.
|
||||
*/
|
||||
SerializableWithNameList<NamedPropertyDescriptor>& GetPropertyDescriptors() {
|
||||
gd::PropertiesContainer& GetPropertyDescriptors() {
|
||||
return propertyDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return a const reference to the list of the properties.
|
||||
*/
|
||||
const SerializableWithNameList<NamedPropertyDescriptor>& GetPropertyDescriptors()
|
||||
const {
|
||||
const gd::PropertiesContainer& GetPropertyDescriptors() const {
|
||||
return propertyDescriptors;
|
||||
}
|
||||
|
||||
@@ -150,7 +149,7 @@ class GD_CORE_API AbstractEventsBasedEntity {
|
||||
gd::String fullName;
|
||||
gd::String description;
|
||||
gd::EventsFunctionsContainer eventsFunctionsContainer;
|
||||
SerializableWithNameList<NamedPropertyDescriptor> propertyDescriptors;
|
||||
gd::PropertiesContainer propertyDescriptors;
|
||||
gd::String extensionName;
|
||||
};
|
||||
|
||||
|
@@ -31,10 +31,10 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API BehaviorConfigurationContainer {
|
||||
public:
|
||||
|
||||
BehaviorConfigurationContainer(){};
|
||||
BehaviorConfigurationContainer(const gd::String& name_, const gd::String& type_)
|
||||
: name(name_), type(type_){};
|
||||
BehaviorConfigurationContainer() : folded(false){};
|
||||
BehaviorConfigurationContainer(const gd::String& name_,
|
||||
const gd::String& type_)
|
||||
: name(name_), type(type_), folded(false){};
|
||||
virtual ~BehaviorConfigurationContainer();
|
||||
virtual BehaviorConfigurationContainer* Clone() const { return new BehaviorConfigurationContainer(*this); }
|
||||
|
||||
@@ -61,7 +61,7 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
/**
|
||||
* \brief Called when the IDE wants to know about the custom properties of the
|
||||
* behavior.
|
||||
*
|
||||
*
|
||||
* \return a std::map with properties names as key.
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
@@ -104,6 +104,17 @@ class GD_CORE_API BehaviorConfigurationContainer {
|
||||
const gd::SerializerElement& GetContent() const { return content; };
|
||||
gd::SerializerElement& GetContent() { return content; };
|
||||
|
||||
/**
|
||||
* \brief Set if the behavior configuration panel should be folded in the UI.
|
||||
*/
|
||||
void SetFolded(bool fold = true) { folded = fold; }
|
||||
|
||||
/**
|
||||
* \brief True if the behavior configuration panel should be folded in the UI.
|
||||
*/
|
||||
bool IsFolded() const { return folded; }
|
||||
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Called when the IDE wants to know about the custom properties of the
|
||||
@@ -148,6 +159,7 @@ protected:
|
||||
///< in the form "ExtensionName::BehaviorTypeName"
|
||||
|
||||
gd::SerializerElement content; // Storage for the behavior properties
|
||||
bool folded;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -17,7 +18,7 @@
|
||||
using namespace gd;
|
||||
|
||||
void CustomConfigurationHelper::InitializeContent(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
gd::SerializerElement &configurationContent) {
|
||||
for (auto &&property : properties.GetInternalVector()) {
|
||||
auto &element = configurationContent.AddChild(property->GetName());
|
||||
@@ -35,7 +36,7 @@ void CustomConfigurationHelper::InitializeContent(
|
||||
}
|
||||
|
||||
std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetProperties(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
const gd::SerializerElement &configurationContent) {
|
||||
auto behaviorProperties = std::map<gd::String, gd::PropertyDescriptor>();
|
||||
|
||||
@@ -71,7 +72,7 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
}
|
||||
|
||||
bool CustomConfigurationHelper::UpdateProperty(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
gd::SerializerElement &configurationContent,
|
||||
const gd::String &propertyName,
|
||||
const gd::String &newValue) {
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/EventsBasedBehavior.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -25,15 +26,15 @@ public:
|
||||
CustomConfigurationHelper() {}
|
||||
|
||||
static void InitializeContent(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
gd::SerializerElement &behaviorContent);
|
||||
|
||||
static std::map<gd::String, gd::PropertyDescriptor> GetProperties(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
const gd::SerializerElement &behaviorContent);
|
||||
|
||||
static bool UpdateProperty(
|
||||
const gd::SerializableWithNameList<gd::NamedPropertyDescriptor> &properties,
|
||||
const gd::PropertiesContainer &properties,
|
||||
gd::SerializerElement &behaviorContent,
|
||||
const gd::String &name,
|
||||
const gd::String &value);
|
||||
|
@@ -13,7 +13,8 @@ namespace gd {
|
||||
EventsBasedBehavior::EventsBasedBehavior()
|
||||
: AbstractEventsBasedEntity(
|
||||
"MyBehavior",
|
||||
gd::EventsFunctionsContainer::FunctionOwner::Behavior) {}
|
||||
gd::EventsFunctionsContainer::FunctionOwner::Behavior),
|
||||
sharedPropertyDescriptors(gd::EventsFunctionsContainer::FunctionOwner::Behavior) {}
|
||||
|
||||
void EventsBasedBehavior::SerializeTo(SerializerElement& element) const {
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
|
@@ -9,7 +9,7 @@
|
||||
#include <vector>
|
||||
#include "GDCore/Project/AbstractEventsBasedEntity.h"
|
||||
#include "GDCore/Project/NamedPropertyDescriptor.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
#include "GDCore/Project/PropertiesContainer.h"
|
||||
#include "GDCore/Project/EventsFunctionsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
@@ -91,14 +91,14 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
|
||||
/**
|
||||
* \brief Return a reference to the list of shared properties.
|
||||
*/
|
||||
SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors() {
|
||||
gd::PropertiesContainer& GetSharedPropertyDescriptors() {
|
||||
return sharedPropertyDescriptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return a const reference to the list of shared properties.
|
||||
*/
|
||||
const SerializableWithNameList<NamedPropertyDescriptor>& GetSharedPropertyDescriptors()
|
||||
const gd::PropertiesContainer& GetSharedPropertyDescriptors()
|
||||
const {
|
||||
return sharedPropertyDescriptors;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class GD_CORE_API EventsBasedBehavior: public AbstractEventsBasedEntity {
|
||||
private:
|
||||
gd::String objectType;
|
||||
bool isPrivate = false;
|
||||
SerializableWithNameList<NamedPropertyDescriptor> sharedPropertyDescriptors;
|
||||
gd::PropertiesContainer sharedPropertyDescriptors;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -13,19 +13,75 @@
|
||||
|
||||
namespace gd {
|
||||
class Effect;
|
||||
}
|
||||
namespace gd {
|
||||
class Camera;
|
||||
}
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
namespace gd {
|
||||
class EffectsContainer;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief A camera is used to render a specific area of a layout.
|
||||
*
|
||||
* \see gd::Layout
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API Camera {
|
||||
public:
|
||||
Camera();
|
||||
~Camera(){};
|
||||
|
||||
/**
|
||||
* \brief Change the viewport, i.e the area of the window where the camera
|
||||
* will be displayed. \note The coordinates must be between 0 and 1.
|
||||
*/
|
||||
void SetViewport(float x1_, float y1_, float x2_, float y2_) {
|
||||
x1 = x1_;
|
||||
x2 = x2_;
|
||||
y1 = y1_;
|
||||
y2 = y2_;
|
||||
};
|
||||
void SetViewportX1(float x1_) { x1 = x1_; };
|
||||
void SetViewportY1(float y1_) { y1 = y1_; };
|
||||
void SetViewportX2(float x2_) { x2 = x2_; };
|
||||
void SetViewportY2(float y2_) { y2 = y2_; };
|
||||
float GetViewportX1() const { return x1; };
|
||||
float GetViewportY1() const { return y1; };
|
||||
float GetViewportX2() const { return x2; };
|
||||
float GetViewportY2() const { return y2; };
|
||||
|
||||
/**
|
||||
* \brief Change the size of the rendered area of the scene, in pixels.
|
||||
*/
|
||||
void SetSize(float width_, float height_) {
|
||||
width = width_;
|
||||
height = height_;
|
||||
};
|
||||
float GetWidth() const { return width; };
|
||||
float GetHeight() const { return height; };
|
||||
|
||||
void SetUseDefaultSize(bool useDefaultSize = true) {
|
||||
defaultSize = useDefaultSize;
|
||||
};
|
||||
bool UseDefaultSize() const { return defaultSize; }
|
||||
|
||||
void SetUseDefaultViewport(bool useDefaultViewport = true) {
|
||||
defaultViewport = useDefaultViewport;
|
||||
};
|
||||
bool UseDefaultViewport() const { return defaultViewport; }
|
||||
|
||||
private:
|
||||
bool defaultSize; ///< True if the camera use the default window size
|
||||
bool defaultViewport; ///< True if the camera use the default viewport size
|
||||
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
float width; ///< The width of the rendered area
|
||||
float height; ///< The height of the rendered area
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Represents a layer of a layout.
|
||||
*
|
||||
@@ -241,68 +297,6 @@ struct LayerHasName : public std::binary_function<gd::Layer, gd::String, bool> {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A camera is used to render a specific area of a layout.
|
||||
*
|
||||
* \see gd::Layout
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API Camera {
|
||||
public:
|
||||
Camera();
|
||||
~Camera(){};
|
||||
|
||||
/**
|
||||
* \brief Change the viewport, i.e the area of the window where the camera
|
||||
* will be displayed. \note The coordinates must be between 0 and 1.
|
||||
*/
|
||||
void SetViewport(float x1_, float y1_, float x2_, float y2_) {
|
||||
x1 = x1_;
|
||||
x2 = x2_;
|
||||
y1 = y1_;
|
||||
y2 = y2_;
|
||||
};
|
||||
void SetViewportX1(float x1_) { x1 = x1_; };
|
||||
void SetViewportY1(float y1_) { y1 = y1_; };
|
||||
void SetViewportX2(float x2_) { x2 = x2_; };
|
||||
void SetViewportY2(float y2_) { y2 = y2_; };
|
||||
float GetViewportX1() const { return x1; };
|
||||
float GetViewportY1() const { return y1; };
|
||||
float GetViewportX2() const { return x2; };
|
||||
float GetViewportY2() const { return y2; };
|
||||
|
||||
/**
|
||||
* \brief Change the size of the rendered area of the scene, in pixels.
|
||||
*/
|
||||
void SetSize(float width_, float height_) {
|
||||
width = width_;
|
||||
height = height_;
|
||||
};
|
||||
float GetWidth() const { return width; };
|
||||
float GetHeight() const { return height; };
|
||||
|
||||
void SetUseDefaultSize(bool useDefaultSize = true) {
|
||||
defaultSize = useDefaultSize;
|
||||
};
|
||||
bool UseDefaultSize() const { return defaultSize; }
|
||||
|
||||
void SetUseDefaultViewport(bool useDefaultViewport = true) {
|
||||
defaultViewport = useDefaultViewport;
|
||||
};
|
||||
bool UseDefaultViewport() const { return defaultViewport; }
|
||||
|
||||
private:
|
||||
bool defaultSize; ///< True if the camera use the default window size
|
||||
bool defaultViewport; ///< True if the camera use the default viewport size
|
||||
|
||||
float x1;
|
||||
float y1;
|
||||
float x2;
|
||||
float y2;
|
||||
float width; ///< The width of the rendered area
|
||||
float height; ///< The height of the rendered area
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_LAYER_H
|
||||
|
@@ -742,7 +742,7 @@ gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
|
||||
vector<gd::String> GD_CORE_API
|
||||
GetBehaviorsOfObject(const gd::ObjectsContainer& project,
|
||||
const gd::ObjectsContainer& layout,
|
||||
gd::String name,
|
||||
const gd::String& name,
|
||||
bool searchInGroups) {
|
||||
bool behaviorsAlreadyInserted = false;
|
||||
vector<gd::String> behaviors;
|
||||
|
@@ -435,6 +435,7 @@ std::vector<gd::String> GetHiddenLayers(const Layout& layout);
|
||||
* \note If a group contains only objects of a same type, then the group has
|
||||
* this type. Otherwise, it is considered as an object without any specific
|
||||
* type.
|
||||
* \deprecated Use gd::ObjectsContainersList::GetTypeOfObject instead.
|
||||
*
|
||||
* @return Type of the object/group.
|
||||
*/
|
||||
@@ -444,6 +445,7 @@ gd::String GD_CORE_API GetTypeOfObject(const ObjectsContainer& game,
|
||||
bool searchInGroups = true);
|
||||
/**
|
||||
* \brief Check if an object or all objects of a group has a behavior.
|
||||
* \deprecated Use gd::ObjectsContainersList::HasBehaviorInObjectOrGroup instead.
|
||||
*/
|
||||
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
const gd::ObjectsContainer &layout,
|
||||
@@ -479,6 +481,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
/**
|
||||
* \brief Get a type from a behavior name
|
||||
* @return Type of the behavior.
|
||||
* @deprecated - Use GetTypeOfBehaviorInObjectOrGroup instead.
|
||||
*/
|
||||
gd::String GD_CORE_API GetTypeOfBehavior(const ObjectsContainer& game,
|
||||
const ObjectsContainer& layout,
|
||||
@@ -495,7 +498,7 @@ gd::String GD_CORE_API GetTypeOfBehavior(const ObjectsContainer& game,
|
||||
std::vector<gd::String> GD_CORE_API
|
||||
GetBehaviorsOfObject(const ObjectsContainer& game,
|
||||
const ObjectsContainer& layout,
|
||||
gd::String objectName,
|
||||
const gd::String& objectName,
|
||||
bool searchInGroups = true);
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
#include "LoadingScreen.h"
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -35,6 +36,7 @@ Object::Object(const gd::String& name_,
|
||||
}
|
||||
|
||||
void Object::Init(const gd::Object& object) {
|
||||
persistentUuid = object.persistentUuid;
|
||||
name = object.name;
|
||||
assetStoreId = object.assetStoreId;
|
||||
objectVariables = object.objectVariables;
|
||||
@@ -127,6 +129,8 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
|
||||
|
||||
void Object::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
persistentUuid = element.GetStringAttribute("persistentUuid");
|
||||
|
||||
SetType(element.GetStringAttribute("type"));
|
||||
assetStoreId = element.GetStringAttribute("assetStoreId");
|
||||
name = element.GetStringAttribute("name", name, "nom");
|
||||
@@ -197,6 +201,9 @@ void Object::UnserializeFrom(gd::Project& project,
|
||||
}
|
||||
|
||||
void Object::SerializeTo(SerializerElement& element) const {
|
||||
if (!persistentUuid.empty())
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("assetStoreId", GetAssetStoreId());
|
||||
element.SetAttribute("type", GetType());
|
||||
@@ -227,4 +234,18 @@ void Object::SerializeTo(SerializerElement& element) const {
|
||||
configuration->SerializeTo(element);
|
||||
}
|
||||
|
||||
Object& Object::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
objectVariables.ResetPersistentUuid();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Object& Object::ClearPersistentUuid() {
|
||||
persistentUuid = "";
|
||||
objectVariables.ClearPersistentUuid();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -243,6 +243,18 @@ class GD_CORE_API Object {
|
||||
* \see DoUnserializeFrom
|
||||
*/
|
||||
void UnserializeFrom(gd::Project& project, const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID, used to recognize
|
||||
* the same object between serialization.
|
||||
*/
|
||||
Object& ResetPersistentUuid();
|
||||
|
||||
/**
|
||||
* \brief Remove the persistent UUID - when the object no
|
||||
* longer need to be recognized between serializations.
|
||||
*/
|
||||
Object& ClearPersistentUuid();
|
||||
///@}
|
||||
|
||||
protected:
|
||||
@@ -259,6 +271,8 @@ class GD_CORE_API Object {
|
||||
gd::String tags; ///< Comma-separated list of tags
|
||||
gd::EffectsContainer
|
||||
effectsContainer; ///< The effects container for the object.
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
///< useful for computing changesets.
|
||||
|
||||
/**
|
||||
* Initialize object using another object. Used by copy-ctor and assign-op.
|
||||
|
@@ -162,4 +162,11 @@ void ObjectGroupsContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
|
||||
objectGroups.insert(objectGroups.begin() + newIndex, std::move(objectGroup));
|
||||
}
|
||||
|
||||
void ObjectGroupsContainer::ForEachNameWithPrefix(const gd::String& prefix,
|
||||
std::function<void(const gd::String& name)> fn) const {
|
||||
for (const auto& objectGroup: objectGroups) {
|
||||
if (objectGroup->GetName().find(prefix) == 0) fn(objectGroup->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -116,6 +116,11 @@ class GD_CORE_API ObjectGroupsContainer {
|
||||
* \brief Clear all groups of the container.
|
||||
*/
|
||||
inline void Clear() { objectGroups.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each group name starting with the prefix passed in parameter.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name)> fn) const;
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
|
250
Core/GDCore/Project/ObjectsContainersList.cpp
Normal file
250
Core/GDCore/Project/ObjectsContainersList.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
#include "ObjectsContainersList.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
ObjectsContainersList
|
||||
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
|
||||
const gd::Project& project, const gd::Layout& layout) {
|
||||
ObjectsContainersList objectsContainersList;
|
||||
objectsContainersList.Add(project);
|
||||
objectsContainersList.Add(layout);
|
||||
return objectsContainersList;
|
||||
}
|
||||
|
||||
ObjectsContainersList
|
||||
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
|
||||
const gd::ObjectsContainer& globalObjectsContainer,
|
||||
const gd::ObjectsContainer& objectsContainer) {
|
||||
ObjectsContainersList objectsContainersList;
|
||||
objectsContainersList.Add(globalObjectsContainer);
|
||||
objectsContainersList.Add(objectsContainer);
|
||||
return objectsContainersList;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectOrGroupNamed(
|
||||
const gd::String& name) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(name) || (*it)->GetObjectGroups().Has(name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectNamed(const gd::String& name) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectOrGroupWithVariableNamed(
|
||||
const gd::String& objectOrGroupName, const gd::String& variableName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
return variables.Has(variableName);
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasVariablesContainer(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::VariablesContainer& variablesContainer) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
return &variablesContainer ==
|
||||
&(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const gd::VariablesContainer*
|
||||
ObjectsContainersList::GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
return &(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachNameWithPrefix(
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration)> fn)
|
||||
const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
for (const auto& object : (*it)->GetObjects()) {
|
||||
if (object->GetName().find(prefix) == 0)
|
||||
fn(object->GetName(), &object->GetConfiguration());
|
||||
}
|
||||
(*it)->GetObjectGroups().ForEachNameWithPrefix(
|
||||
prefix, [&](const gd::String& name) { fn(name, nullptr); });
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::ExpandObjectName(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& onlyObjectToSelectIfPresent) const {
|
||||
std::vector<gd::String> realObjects;
|
||||
|
||||
// Check progressively each object container to find the object or the group
|
||||
// with the specified name.
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
// We found the object, it's a single object with this name.
|
||||
realObjects.push_back(objectOrGroupName);
|
||||
break;
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// We found a group with this name, expand the object names inside of it.
|
||||
realObjects =
|
||||
(*it)->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the "current object" is present, use it and only it.
|
||||
if (!onlyObjectToSelectIfPresent.empty() &&
|
||||
find(realObjects.begin(),
|
||||
realObjects.end(),
|
||||
onlyObjectToSelectIfPresent) != realObjects.end()) {
|
||||
realObjects.clear();
|
||||
realObjects.push_back(onlyObjectToSelectIfPresent);
|
||||
}
|
||||
|
||||
// Ensure that all returned objects actually exists (i.e: if some groups have
|
||||
// names refering to non existing objects, don't return them).
|
||||
for (std::size_t i = 0; i < realObjects.size();) {
|
||||
if (!HasObjectNamed(realObjects[i]))
|
||||
realObjects.erase(realObjects.begin() + i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
|
||||
return realObjects;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObject(
|
||||
std::function<void(const gd::Object& object)> fn) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
const auto& objectsContainer = **it;
|
||||
for (const auto& object : objectsContainer.GetObjects()) {
|
||||
fn(*object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gd::String ObjectsContainersList::GetTypeOfObject(
|
||||
const gd::String& objectName) const {
|
||||
if (objectsContainers.size() != 2) {
|
||||
std::cout << this << std::endl;
|
||||
std::cout << objectsContainers.size() << std::endl;
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
return gd::GetTypeOfObject(
|
||||
*objectsContainers[0], *objectsContainers[1], objectName, true);
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasBehaviorInObjectOrGroup(
|
||||
const gd::String& objectOrGroupName, const gd::String& behaviorName) const {
|
||||
if (objectsContainers.size() != 2) {
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
return gd::HasBehaviorInObjectOrGroup(*objectsContainers[0],
|
||||
*objectsContainers[1],
|
||||
objectOrGroupName,
|
||||
behaviorName,
|
||||
true);
|
||||
}
|
||||
|
||||
gd::String ObjectsContainersList::GetTypeOfBehaviorInObjectOrGroup(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) const {
|
||||
if (objectsContainers.size() != 2) {
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
return gd::GetTypeOfBehaviorInObjectOrGroup(*objectsContainers[0],
|
||||
*objectsContainers[1],
|
||||
objectOrGroupName,
|
||||
behaviorName,
|
||||
searchInGroups);
|
||||
}
|
||||
|
||||
gd::String ObjectsContainersList::GetTypeOfBehavior(
|
||||
const gd::String& behaviorName, bool searchInGroups) const {
|
||||
if (objectsContainers.size() != 2) {
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
return gd::GetTypeOfBehavior(
|
||||
*objectsContainers[0], *objectsContainers[1], behaviorName, searchInGroups);
|
||||
}
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
|
||||
const gd::String& objectName, bool searchInGroups) const {
|
||||
if (objectsContainers.size() != 2) {
|
||||
// TODO: rework forwarded methods so they can work with any number of
|
||||
// containers.
|
||||
gd::LogFatalError(
|
||||
"ObjectsContainersList::GetTypeOfObject called with objectsContainers "
|
||||
"not being exactly 2. This is a logical error and will crash.");
|
||||
}
|
||||
|
||||
return gd::GetBehaviorsOfObject(
|
||||
*objectsContainers[0], *objectsContainers[1], objectName, searchInGroups);
|
||||
}
|
||||
|
||||
} // namespace gd
|
142
Core/GDCore/Project/ObjectsContainersList.h
Normal file
142
Core/GDCore/Project/ObjectsContainersList.h
Normal file
@@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "Variable.h"
|
||||
|
||||
namespace gd {
|
||||
class String;
|
||||
class Project;
|
||||
class Layout;
|
||||
class ObjectsContainer;
|
||||
class VariablesContainer;
|
||||
class Object;
|
||||
class ObjectConfiguration;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief A list of objects containers, useful for accessing objects in a
|
||||
* scoped way, along with methods to access them.
|
||||
*
|
||||
* \see gd::Object
|
||||
* \see gd::ObjectsContainer
|
||||
* \see gd::Project
|
||||
* \see gd::Layout
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API ObjectsContainersList {
|
||||
public:
|
||||
virtual ~ObjectsContainersList(){};
|
||||
|
||||
static ObjectsContainersList MakeNewObjectsContainersListForProjectAndLayout(
|
||||
const gd::Project& project, const gd::Layout& layout);
|
||||
|
||||
static ObjectsContainersList MakeNewObjectsContainersListForContainers(
|
||||
const gd::ObjectsContainer& globalObjectsContainer,
|
||||
const gd::ObjectsContainer& objectsContainer);
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object or group exists.
|
||||
*/
|
||||
bool HasObjectOrGroupNamed(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object or group has the specified variable in
|
||||
* its declared variables.
|
||||
*/
|
||||
bool HasObjectOrGroupWithVariableNamed(const gd::String& objectOrGroupName,
|
||||
const gd::String& variableName) const;
|
||||
|
||||
/**
|
||||
* \brief Check if the specified object or group has the specified variables
|
||||
* container.
|
||||
*/
|
||||
bool HasVariablesContainer(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::VariablesContainer& variablesContainer) const;
|
||||
|
||||
/**
|
||||
* \brief Return the container of the variables for the specified object or
|
||||
* group of objects.
|
||||
*/
|
||||
const gd::VariablesContainer* GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group name.
|
||||
* \note If a group contains only objects of a same type, then the group has
|
||||
* this type. Otherwise, it is considered as an object without any specific
|
||||
* type.
|
||||
*
|
||||
* @return Type of the object/group.
|
||||
*/
|
||||
gd::String GetTypeOfObject(const gd::String& objectName) const;
|
||||
|
||||
/**
|
||||
* \brief Check if an object or all object of a group has a behavior.
|
||||
*/
|
||||
bool HasBehaviorInObjectOrGroup(const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName) const;
|
||||
|
||||
/**
|
||||
* \brief Get the type of a behavior if an object or all objects of a group has it.
|
||||
*/
|
||||
gd::String GetTypeOfBehaviorInObjectOrGroup(const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorName,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from a behavior name
|
||||
* @return Type of the behavior.
|
||||
* @deprecated - Use GetTypeOfBehaviorInObjectOrGroup instead.
|
||||
*/
|
||||
gd::String GetTypeOfBehavior(const gd::String& behaviorName, bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get behaviors of an object/group
|
||||
* \note The behaviors of a group are the behaviors which are found in common
|
||||
* when looking all the objects of the group.
|
||||
*
|
||||
* @return Vector containing names of behaviors
|
||||
*/
|
||||
std::vector<gd::String>
|
||||
GetBehaviorsOfObject(const gd::String& objectName,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Return a list containing all objects refered to by the group.
|
||||
* If an object name is passed, then only this object name is returned.
|
||||
*
|
||||
* If \a onlyObjectToSelectIfPresent is set and present in the group(s),
|
||||
* only this object will be returned. This is useful for considering this
|
||||
* object as the "currently selected" object, when generating a condition or
|
||||
* an action.
|
||||
*/
|
||||
std::vector<gd::String> ExpandObjectName(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& onlyObjectToSelectIfPresent = "") const;
|
||||
|
||||
void ForEachObject(std::function<void(const gd::Object& object)> fn) const;
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each object or group name starting with the prefix passed in parameter.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::ObjectConfiguration* objectConfiguration)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
ObjectsContainersList(){};
|
||||
|
||||
private:
|
||||
bool HasObjectNamed(const gd::String& name) const;
|
||||
|
||||
void Add(const gd::ObjectsContainer& objectsContainer) {
|
||||
objectsContainers.push_back(&objectsContainer);
|
||||
};
|
||||
|
||||
std::vector<const gd::ObjectsContainer*> objectsContainers;
|
||||
};
|
||||
|
||||
} // namespace gd
|
174
Core/GDCore/Project/ProjectScopedContainers.h
Normal file
174
Core/GDCore/Project/ProjectScopedContainers.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "ObjectsContainersList.h"
|
||||
#include "PropertiesContainersList.h"
|
||||
#include "VariablesContainersList.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class VariablesContainersList;
|
||||
class PropertiesContainersList;
|
||||
class NamedPropertyDescriptor;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Holds references to variables, objects, properties and other
|
||||
* containers.
|
||||
*
|
||||
* This is useful to access elements of a project from a specific location,
|
||||
* honoring the scope of each element.
|
||||
*
|
||||
* For example, in an expression, when an identifier is written, this class is
|
||||
* used to know what this identifier refers too.
|
||||
*/
|
||||
class ProjectScopedContainers {
|
||||
public:
|
||||
ProjectScopedContainers(
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::VariablesContainersList &variablesContainersList_,
|
||||
const gd::PropertiesContainersList &propertiesContainersList_)
|
||||
: objectsContainersList(objectsContainersList_),
|
||||
variablesContainersList(variablesContainersList_),
|
||||
propertiesContainersList(propertiesContainersList_){};
|
||||
virtual ~ProjectScopedContainers(){};
|
||||
|
||||
static ProjectScopedContainers
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(const gd::Project &project,
|
||||
const gd::Layout &layout) {
|
||||
ProjectScopedContainers projectScopedContainers(
|
||||
ObjectsContainersList::MakeNewObjectsContainersListForProjectAndLayout(
|
||||
project, layout),
|
||||
VariablesContainersList::
|
||||
MakeNewVariablesContainersListForProjectAndLayout(project, layout),
|
||||
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
|
||||
|
||||
return projectScopedContainers;
|
||||
}
|
||||
|
||||
static ProjectScopedContainers MakeNewProjectScopedContainersFor(
|
||||
const gd::ObjectsContainer &globalObjectsContainers,
|
||||
const gd::ObjectsContainer &objectsContainers) {
|
||||
ProjectScopedContainers projectScopedContainers(
|
||||
ObjectsContainersList::MakeNewObjectsContainersListForContainers(
|
||||
globalObjectsContainers, objectsContainers),
|
||||
VariablesContainersList::MakeNewEmptyVariablesContainersList(),
|
||||
PropertiesContainersList::MakeNewEmptyPropertiesContainersList());
|
||||
|
||||
return projectScopedContainers;
|
||||
}
|
||||
|
||||
ProjectScopedContainers &AddPropertiesContainer(
|
||||
const gd::PropertiesContainer &container) {
|
||||
propertiesContainersList.Add(container);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProjectScopedContainers &AddParameters(
|
||||
const std::vector<gd::ParameterMetadata> ¶meters) {
|
||||
parametersVectorsList.push_back(¶meters);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class ReturnType>
|
||||
ReturnType MatchIdentifierWithName(
|
||||
const gd::String &name,
|
||||
std::function<ReturnType()> objectCallback,
|
||||
std::function<ReturnType()> variableCallback,
|
||||
std::function<ReturnType()> propertyCallback,
|
||||
std::function<ReturnType()> parameterCallback,
|
||||
std::function<ReturnType()> notFoundCallback) const {
|
||||
if (objectsContainersList.HasObjectOrGroupNamed(name))
|
||||
return objectCallback();
|
||||
else if (variablesContainersList.Has(name))
|
||||
return variableCallback();
|
||||
else if (ParameterMetadataTools::Has(parametersVectorsList, name))
|
||||
return parameterCallback();
|
||||
else if (propertiesContainersList.Has(name))
|
||||
return propertyCallback();
|
||||
|
||||
return notFoundCallback();
|
||||
};
|
||||
|
||||
void ForEachIdentifierWithPrefix(
|
||||
const gd::String &prefix,
|
||||
std::function<void(const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration)>
|
||||
objectCallback,
|
||||
std::function<void(const gd::String &name, const gd::Variable &variable)>
|
||||
variableCallback,
|
||||
std::function<void(const gd::NamedPropertyDescriptor &property)>
|
||||
propertyCallback,
|
||||
std::function<void(const gd::ParameterMetadata ¶meter)>
|
||||
parameterCallback) const {
|
||||
std::set<gd::String> namesAlreadySeen;
|
||||
|
||||
objectsContainersList.ForEachNameWithPrefix(
|
||||
prefix,
|
||||
[&](const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
namesAlreadySeen.insert(name);
|
||||
objectCallback(name, objectConfiguration);
|
||||
}
|
||||
});
|
||||
variablesContainersList.ForEachVariableWithPrefix(
|
||||
prefix, [&](const gd::String &name, const gd::Variable &variable) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
namesAlreadySeen.insert(name);
|
||||
variableCallback(name, variable);
|
||||
}
|
||||
});
|
||||
gd::ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
parametersVectorsList,
|
||||
prefix,
|
||||
[&](const gd::ParameterMetadata ¶meter) {
|
||||
if (namesAlreadySeen.count(parameter.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(parameter.GetName());
|
||||
parameterCallback(parameter);
|
||||
}
|
||||
});
|
||||
propertiesContainersList.ForEachPropertyWithPrefix(
|
||||
prefix, [&](const gd::NamedPropertyDescriptor &property) {
|
||||
if (namesAlreadySeen.count(property.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(property.GetName());
|
||||
propertyCallback(property);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const gd::ObjectsContainersList &GetObjectsContainersList() const {
|
||||
return objectsContainersList;
|
||||
};
|
||||
|
||||
const gd::VariablesContainersList &GetVariablesContainersList() const {
|
||||
return variablesContainersList;
|
||||
};
|
||||
|
||||
const gd::PropertiesContainersList &GetPropertiesContainersList() const {
|
||||
return propertiesContainersList;
|
||||
};
|
||||
|
||||
const std::vector<const std::vector<gd::ParameterMetadata> *> &GetParametersVectorsList() const {
|
||||
return parametersVectorsList;
|
||||
};
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
ProjectScopedContainers(){};
|
||||
|
||||
private:
|
||||
gd::ObjectsContainersList objectsContainersList;
|
||||
gd::VariablesContainersList variablesContainersList;
|
||||
gd::PropertiesContainersList propertiesContainersList;
|
||||
std::vector<const std::vector<gd::ParameterMetadata> *> parametersVectorsList;
|
||||
};
|
||||
|
||||
} // namespace gd
|
46
Core/GDCore/Project/PropertiesContainer.h
Normal file
46
Core/GDCore/Project/PropertiesContainer.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include "EventsFunctionsContainer.h"
|
||||
#include "GDCore/Tools/SerializableWithNameList.h"
|
||||
#include "NamedPropertyDescriptor.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief A container of properties, used for custom behaviors, custom objects,
|
||||
* extensions...
|
||||
*
|
||||
* \see gd::NamedPropertyDescriptor
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class PropertiesContainer
|
||||
: public SerializableWithNameList<NamedPropertyDescriptor> {
|
||||
public:
|
||||
PropertiesContainer(EventsFunctionsContainer::FunctionOwner owner)
|
||||
: SerializableWithNameList<NamedPropertyDescriptor>(), owner(owner) {}
|
||||
|
||||
PropertiesContainer(const PropertiesContainer& other)
|
||||
: SerializableWithNameList<NamedPropertyDescriptor>(other),
|
||||
owner(other.owner) {}
|
||||
|
||||
PropertiesContainer& operator=(const PropertiesContainer& other) {
|
||||
if (this != &other) {
|
||||
SerializableWithNameList<NamedPropertyDescriptor>::operator=(other);
|
||||
owner = other.owner;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ForEachPropertyWithPrefix(const gd::String& prefix, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const {
|
||||
for (const auto& property: elements) {
|
||||
if (property->GetName().find(prefix) == 0) fn(*property);
|
||||
}
|
||||
}
|
||||
|
||||
EventsFunctionsContainer::FunctionOwner GetOwner() const { return owner; }
|
||||
|
||||
private:
|
||||
EventsFunctionsContainer::FunctionOwner owner;
|
||||
};
|
||||
|
||||
} // namespace gd
|
67
Core/GDCore/Project/PropertiesContainersList.cpp
Normal file
67
Core/GDCore/Project/PropertiesContainersList.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "PropertiesContainersList.h"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "PropertiesContainer.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
NamedPropertyDescriptor PropertiesContainersList::badNamedPropertyDescriptor;
|
||||
PropertiesContainer PropertiesContainersList::badPropertiesContainer(gd::EventsFunctionsContainer::FunctionOwner::Extension);
|
||||
|
||||
PropertiesContainersList
|
||||
PropertiesContainersList::MakeNewPropertiesContainersListFor(
|
||||
const gd::PropertiesContainer& propertiesContainer) {
|
||||
PropertiesContainersList propertiesContainersList;
|
||||
propertiesContainersList.Add(propertiesContainer);
|
||||
return propertiesContainersList;
|
||||
}
|
||||
|
||||
PropertiesContainersList
|
||||
PropertiesContainersList::MakeNewEmptyPropertiesContainersList() {
|
||||
PropertiesContainersList propertiesContainersList;
|
||||
return propertiesContainersList;
|
||||
}
|
||||
|
||||
bool PropertiesContainersList::Has(const gd::String& name) const {
|
||||
for (auto it = propertiesContainers.rbegin();
|
||||
it != propertiesContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->Has(name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<std::reference_wrapper<const gd::PropertiesContainer>,
|
||||
std::reference_wrapper<const NamedPropertyDescriptor>>
|
||||
PropertiesContainersList::Get(const gd::String& name) const {
|
||||
for (auto it = propertiesContainers.rbegin();
|
||||
it != propertiesContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->Has(name)) return {**it, (*it)->Get(name)};
|
||||
}
|
||||
|
||||
return {badPropertiesContainer, badNamedPropertyDescriptor};
|
||||
}
|
||||
|
||||
bool PropertiesContainersList::HasPropertiesContainer(const gd::PropertiesContainer& propertiesContainer) const {
|
||||
for (auto it = propertiesContainers.rbegin(); it != propertiesContainers.rend();
|
||||
++it) {
|
||||
if (*it == &propertiesContainer) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PropertiesContainersList::ForEachPropertyWithPrefix(
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const {
|
||||
for (auto it = propertiesContainers.rbegin(); it != propertiesContainers.rend();
|
||||
++it) {
|
||||
(*it)->ForEachPropertyWithPrefix(prefix, fn);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
84
Core/GDCore/Project/PropertiesContainersList.h
Normal file
84
Core/GDCore/Project/PropertiesContainersList.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "PropertiesContainer.h"
|
||||
|
||||
namespace gd {
|
||||
class String;
|
||||
class Project;
|
||||
class Layout;
|
||||
class NamedPropertyDescriptor;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief A list of property containers, useful for accessing properties in a
|
||||
* scoped way.
|
||||
*
|
||||
* \see gd::NamedPropertyDescriptor
|
||||
* \see gd::Project
|
||||
* \see gd::Layout
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API PropertiesContainersList {
|
||||
public:
|
||||
virtual ~PropertiesContainersList(){};
|
||||
|
||||
static PropertiesContainersList MakeNewPropertiesContainersListFor(
|
||||
const gd::PropertiesContainer& propertiesContainer);
|
||||
static PropertiesContainersList MakeNewEmptyPropertiesContainersList();
|
||||
|
||||
/**
|
||||
* \brief Add a new container of properties in the list.
|
||||
* Add containers in order from the most global one to the most local one.
|
||||
*/
|
||||
void Add(const gd::PropertiesContainer& propertiesContainer) {
|
||||
propertiesContainers.push_back(&propertiesContainer);
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified property is in one of the containers.
|
||||
*/
|
||||
bool Has(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Return a reference to the property called \a name, and the
|
||||
* properties container holding it.
|
||||
*/
|
||||
std::pair<std::reference_wrapper<const gd::PropertiesContainer>,
|
||||
std::reference_wrapper<const NamedPropertyDescriptor>>
|
||||
Get(const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified properties container is present.
|
||||
*/
|
||||
bool HasPropertiesContainer(const gd::PropertiesContainer& propertiesContainer) const;
|
||||
|
||||
/**
|
||||
* Get the properties container at the bottom of the scope (so the most
|
||||
* "local" one).
|
||||
*/
|
||||
const PropertiesContainer* GetBottomMostPropertiesContainer() const {
|
||||
if (propertiesContainers.empty()) return nullptr;
|
||||
return propertiesContainers.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each property having a name starting with the specified prefix.
|
||||
*/
|
||||
void ForEachPropertyWithPrefix(const gd::String& prefix, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
PropertiesContainersList(){};
|
||||
|
||||
private:
|
||||
std::vector<const gd::PropertiesContainer*> propertiesContainers;
|
||||
static NamedPropertyDescriptor badNamedPropertyDescriptor;
|
||||
static PropertiesContainer badPropertiesContainer;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -228,6 +229,9 @@ void Variable::SerializeTo(SerializerElement& element) const {
|
||||
element.SetStringAttribute("type", TypeAsString(GetType()));
|
||||
if (IsFolded()) element.SetBoolAttribute("folded", true);
|
||||
|
||||
if (!persistentUuid.empty())
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
if (type == Type::String) {
|
||||
element.SetStringAttribute("value", GetString());
|
||||
} else if (type == Type::Number) {
|
||||
@@ -254,6 +258,8 @@ void Variable::SerializeTo(SerializerElement& element) const {
|
||||
void Variable::UnserializeFrom(const SerializerElement& element) {
|
||||
type = StringAsType(element.GetStringAttribute("type", "string"));
|
||||
|
||||
persistentUuid = element.GetStringAttribute("persistentUuid");
|
||||
|
||||
// Compatibility with GD <= 5.0.0-beta102
|
||||
// Before, everything was stored as strings.
|
||||
// We can unserialize primitives as string as they can be converted from/to
|
||||
@@ -290,7 +296,12 @@ void Variable::UnserializeFrom(const SerializerElement& element) {
|
||||
PushNew().UnserializeFrom(childElement);
|
||||
}
|
||||
}
|
||||
} // namespace gd
|
||||
}
|
||||
|
||||
Variable& Variable::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<gd::String> Variable::GetAllChildrenNames() const {
|
||||
std::vector<gd::String> names;
|
||||
@@ -338,7 +349,8 @@ Variable::Variable(const Variable& other)
|
||||
str(other.str),
|
||||
folded(other.folded),
|
||||
boolVal(other.boolVal),
|
||||
type(other.type) {
|
||||
type(other.type),
|
||||
persistentUuid(other.persistentUuid) {
|
||||
CopyChildren(other);
|
||||
}
|
||||
|
||||
@@ -349,6 +361,7 @@ Variable& Variable::operator=(const Variable& other) {
|
||||
folded = other.folded;
|
||||
boolVal = other.boolVal;
|
||||
type = other.type;
|
||||
persistentUuid = other.persistentUuid;
|
||||
CopyChildren(other);
|
||||
}
|
||||
|
||||
|
@@ -338,6 +338,24 @@ class GD_CORE_API Variable {
|
||||
* \brief Unserialize the variable.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID used to recognize
|
||||
* the same variable between serialization.
|
||||
*/
|
||||
Variable& ResetPersistentUuid();
|
||||
|
||||
/**
|
||||
* \brief Remove the persistent UUID - when the variable no
|
||||
* longer needs to be recognized between serializations.
|
||||
*/
|
||||
Variable& ClearPersistentUuid() { persistentUuid = ""; return *this; };
|
||||
|
||||
/**
|
||||
* \brief Get the persistent UUID used to recognize
|
||||
* the same variable between serialization.
|
||||
*/
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; };
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -361,6 +379,8 @@ class GD_CORE_API Variable {
|
||||
mutable std::vector<std::shared_ptr<Variable>>
|
||||
childrenArray; ///< Children, when the variable is considered as an
|
||||
///< array.
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
///< useful for computing changesets.
|
||||
|
||||
/**
|
||||
* Initialize children by copying them from another variable. Used by
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
#include "GDCore/Tools/UUID/UUID.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -161,7 +162,18 @@ void VariablesContainer::Move(std::size_t oldIndex, std::size_t newIndex) {
|
||||
variables.insert(variables.begin() + newIndex, nameAndVariable);
|
||||
}
|
||||
|
||||
void VariablesContainer::ForEachVariableWithPrefix(
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const {
|
||||
for (const auto& nameAndVariable: variables) {
|
||||
if (nameAndVariable.first.find(prefix) == 0) fn(nameAndVariable.first, *nameAndVariable.second);
|
||||
}
|
||||
}
|
||||
|
||||
void VariablesContainer::SerializeTo(SerializerElement& element) const {
|
||||
if (!persistentUuid.empty())
|
||||
element.SetStringAttribute("persistentUuid", persistentUuid);
|
||||
|
||||
element.ConsiderAsArrayOf("variable");
|
||||
for (std::size_t j = 0; j < variables.size(); j++) {
|
||||
SerializerElement& variableElement = element.AddChild("variable");
|
||||
@@ -171,6 +183,8 @@ void VariablesContainer::SerializeTo(SerializerElement& element) const {
|
||||
}
|
||||
|
||||
void VariablesContainer::UnserializeFrom(const SerializerElement& element) {
|
||||
persistentUuid = element.GetStringAttribute("persistentUuid");
|
||||
|
||||
Clear();
|
||||
element.ConsiderAsArrayOf("variable", "Variable");
|
||||
for (std::size_t j = 0; j < element.GetChildrenCount(); j++) {
|
||||
@@ -183,6 +197,24 @@ void VariablesContainer::UnserializeFrom(const SerializerElement& element) {
|
||||
}
|
||||
}
|
||||
|
||||
VariablesContainer& VariablesContainer::ResetPersistentUuid() {
|
||||
persistentUuid = UUID::MakeUuid4();
|
||||
for(auto& variable: variables) {
|
||||
variable.second->ResetPersistentUuid();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VariablesContainer& VariablesContainer::ClearPersistentUuid() {
|
||||
persistentUuid = "";
|
||||
for(auto& variable: variables) {
|
||||
variable.second->ClearPersistentUuid();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VariablesContainer::VariablesContainer(const VariablesContainer& other) {
|
||||
Init(other);
|
||||
}
|
||||
@@ -195,6 +227,7 @@ VariablesContainer& VariablesContainer::operator=(
|
||||
}
|
||||
|
||||
void VariablesContainer::Init(const gd::VariablesContainer& other) {
|
||||
persistentUuid = other.persistentUuid;
|
||||
variables.clear();
|
||||
for (auto& it : other.variables) {
|
||||
variables.push_back(
|
||||
|
@@ -137,6 +137,11 @@ class GD_CORE_API VariablesContainer {
|
||||
* \brief Clear all variables of the container.
|
||||
*/
|
||||
inline void Clear() { variables.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable with a name starting with the specified prefix.
|
||||
*/
|
||||
void ForEachVariableWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
@@ -152,10 +157,30 @@ class GD_CORE_API VariablesContainer {
|
||||
* \brief Unserialize the variable container.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID, used to recognize
|
||||
* the same variables between serialization.
|
||||
*/
|
||||
VariablesContainer& ResetPersistentUuid();
|
||||
|
||||
/**
|
||||
* \brief Remove the persistent UUID - when the variables no
|
||||
* longer need to be recognized between serializations.
|
||||
*/
|
||||
VariablesContainer& ClearPersistentUuid();
|
||||
|
||||
/**
|
||||
* \brief Get the persistent UUID used to recognize
|
||||
* the same variables between serialization.
|
||||
*/
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; };
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<gd::String, std::shared_ptr<gd::Variable>>> variables;
|
||||
mutable gd::String persistentUuid; ///< A persistent random version 4 UUID,
|
||||
///< useful for computing changesets.
|
||||
static gd::Variable badVariable;
|
||||
static gd::String badName;
|
||||
|
||||
|
64
Core/GDCore/Project/VariablesContainersList.cpp
Normal file
64
Core/GDCore/Project/VariablesContainersList.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "VariablesContainersList.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
Variable VariablesContainersList::badVariable;
|
||||
|
||||
VariablesContainersList
|
||||
VariablesContainersList::MakeNewVariablesContainersListForProjectAndLayout(
|
||||
const gd::Project& project, const gd::Layout& layout) {
|
||||
VariablesContainersList variablesContainersList;
|
||||
variablesContainersList.Add(project.GetVariables());
|
||||
variablesContainersList.Add(layout.GetVariables());
|
||||
return variablesContainersList;
|
||||
}
|
||||
|
||||
VariablesContainersList
|
||||
VariablesContainersList::MakeNewEmptyVariablesContainersList() {
|
||||
VariablesContainersList variablesContainersList;
|
||||
return variablesContainersList;
|
||||
}
|
||||
|
||||
bool VariablesContainersList::Has(const gd::String& name) const {
|
||||
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->Has(name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Variable& VariablesContainersList::Get(const gd::String& name) const {
|
||||
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->Has(name)) return (*it)->Get(name);
|
||||
}
|
||||
|
||||
return badVariable;
|
||||
}
|
||||
|
||||
bool VariablesContainersList::HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const {
|
||||
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
|
||||
++it) {
|
||||
if (*it == &variablesContainer) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void VariablesContainersList::ForEachVariableWithPrefix(
|
||||
const gd::String& prefix,
|
||||
std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const {
|
||||
for (auto it = variablesContainers.rbegin(); it != variablesContainers.rend();
|
||||
++it) {
|
||||
(*it)->ForEachVariableWithPrefix(prefix, fn);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gd
|
85
Core/GDCore/Project/VariablesContainersList.h
Normal file
85
Core/GDCore/Project/VariablesContainersList.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace gd {
|
||||
class String;
|
||||
class Project;
|
||||
class Layout;
|
||||
class VariablesContainer;
|
||||
class Variable;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief A list of variables containers, useful for accessing variables in a
|
||||
* scoped way.
|
||||
*
|
||||
* \see gd::Variable
|
||||
* \see gd::Project
|
||||
* \see gd::Layout
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API VariablesContainersList {
|
||||
public:
|
||||
virtual ~VariablesContainersList(){};
|
||||
|
||||
static VariablesContainersList
|
||||
MakeNewVariablesContainersListForProjectAndLayout(const gd::Project& project,
|
||||
const gd::Layout& layout);
|
||||
|
||||
static VariablesContainersList MakeNewEmptyVariablesContainersList();
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified variable is in one of the containers.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified variable container is present.
|
||||
*/
|
||||
bool HasVariablesContainer(const gd::VariablesContainer& variablesContainer) const;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
return variablesContainers.front();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the variables container at the bottom of the scope (so the most "local" one).
|
||||
* \brief Avoid using apart when a scope must be forced.
|
||||
*/
|
||||
const VariablesContainer* GetBottomMostVariablesContainer() const {
|
||||
if (variablesContainers.empty()) return nullptr;
|
||||
return variablesContainers.back();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable having a name starting with the specified prefix.
|
||||
*/
|
||||
void ForEachVariableWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::Variable& variable)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a temporary. */
|
||||
VariablesContainersList() {};
|
||||
private:
|
||||
|
||||
void Add(const gd::VariablesContainer& variablesContainer) {
|
||||
variablesContainers.push_back(&variablesContainer);
|
||||
};
|
||||
|
||||
std::vector<const gd::VariablesContainer*> variablesContainers;
|
||||
static Variable badVariable;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -251,26 +251,26 @@ String& String::replace_if(iterator i1, iterator i2, std::function<bool(char32_t
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::RemoveConsecutiveOccurrences(iterator i1, iterator i2, const char c)
|
||||
{
|
||||
std::vector<std::pair<size_type, size_type>> ranges_to_remove;
|
||||
for(iterator current_index = i1.base(); current_index < i2.base(); current_index++)
|
||||
{
|
||||
if (*current_index == c){
|
||||
iterator current_subindex = current_index;
|
||||
std::advance(current_subindex, 1);
|
||||
if (*current_subindex == c) {
|
||||
while(current_subindex < end() && *current_subindex == c)
|
||||
{
|
||||
current_subindex++;
|
||||
}
|
||||
replace(std::distance(begin(), current_index),
|
||||
std::distance(current_index, current_subindex),
|
||||
c);
|
||||
|
||||
std::advance(current_index, 1);
|
||||
}
|
||||
String &String::RemoveConsecutiveOccurrences(iterator i1,
|
||||
iterator i2,
|
||||
const char c) {
|
||||
iterator end = i2;
|
||||
for (iterator current_index = i1.base(); current_index < end.base();
|
||||
current_index++) {
|
||||
if (*current_index == c) {
|
||||
iterator current_subindex = current_index;
|
||||
current_subindex++;
|
||||
while (current_subindex < end.base() && *current_subindex == c) {
|
||||
current_subindex++;
|
||||
}
|
||||
difference_type difference_to_replace =
|
||||
std::distance(current_index, current_subindex);
|
||||
if (difference_to_replace > 1) {
|
||||
replace(
|
||||
std::distance(begin(), current_index), difference_to_replace, c);
|
||||
std::advance(end, -(difference_to_replace - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Events/Builtin/StandardEvent.h"
|
||||
#include "GDCore/Events/Builtin/ForEachChildVariableEvent.h"
|
||||
#include "GDCore/Events/Builtin/RepeatEvent.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterOptions.h"
|
||||
#include "catch.hpp"
|
||||
@@ -103,6 +105,8 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
|
||||
commonInstructionsExtension->SetExtensionInformation(
|
||||
"BuiltinCommonInstructions", "instruction extension", "", "", "");
|
||||
commonInstructionsExtension->AddEvent("Standard", "Standard event", "", "", "", std::make_shared<gd::StandardEvent>());
|
||||
commonInstructionsExtension->AddEvent("ForEachChildVariable", "For each child variable event", "", "", "", std::make_shared<gd::ForEachChildVariableEvent>());
|
||||
commonInstructionsExtension->AddEvent("Repeat", "Repeat event", "", "", "", std::make_shared<gd::RepeatEvent>());
|
||||
|
||||
std::shared_ptr<gd::PlatformExtension> baseObjectExtension =
|
||||
std::shared_ptr<gd::PlatformExtension>(new gd::PlatformExtension);
|
||||
@@ -243,6 +247,20 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
|
||||
.AddParameter("soundfile", "Parameter 3 (an audio resource)")
|
||||
.SetFunctionName("doSomethingWithResources");
|
||||
|
||||
extension
|
||||
->AddAction("DoSomethingWithLegacyPreScopedVariables",
|
||||
"Do something with variables",
|
||||
"This does something with variables",
|
||||
"Do something with variables please",
|
||||
"",
|
||||
"",
|
||||
"")
|
||||
.AddParameter("scenevar", "Scene variable")
|
||||
.AddParameter("globalvar", "Global variable")
|
||||
.AddParameter("object", "Some object")
|
||||
.AddParameter("objectvar", "Some variable of the object")
|
||||
.SetFunctionName("doSomethingWithVariables");
|
||||
|
||||
extension->AddExpression("GetNumber", "Get me a number", "", "", "")
|
||||
.SetFunctionName("getNumber");
|
||||
extension
|
||||
@@ -345,7 +363,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.AddParameter("expression", _("Number parameter"))
|
||||
.AddParameter("string", _("String parameter"))
|
||||
.AddParameter("", _("Identifier parameter"))
|
||||
.AddParameter("expression", _("Identifier parameter"))
|
||||
.SetFunctionName("getObjectStringWith3Param");
|
||||
object
|
||||
.AddStrExpression("GetObjectStringWith2ObjectParam",
|
||||
@@ -560,15 +578,15 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
|
||||
extension
|
||||
->AddExpression(
|
||||
"LayerEffectParameter",
|
||||
_("Effect parameter (number)"),
|
||||
_("Return the value of a parameter of an effect."),
|
||||
_("Effect property (number)"),
|
||||
_("Return the value of a property of an effect."),
|
||||
_("Effects"),
|
||||
"")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("layerEffectName", _("Effect name"))
|
||||
.AddParameter("layerEffectParameterName", _("Parameter name"));
|
||||
.AddParameter("layerEffectParameterName", _("Property name"));
|
||||
}
|
||||
|
||||
{
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
namespace {
|
||||
@@ -35,6 +36,8 @@ TEST_CASE("EventsBehaviorRenamer (expressions)", "[common]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
|
||||
auto &object1 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
@@ -57,7 +60,7 @@ TEST_CASE("EventsBehaviorRenamer (expressions)", "[common]") {
|
||||
// Rename the first behavior.
|
||||
gd::EventsBehaviorRenamer behaviorRenamer(
|
||||
platform, "Object1", "MyBehavior", "MyRenamedBehavior");
|
||||
behaviorRenamer.Launch(layout1.GetEvents(), project, layout1);
|
||||
behaviorRenamer.Launch(layout1.GetEvents(), projectScopedContainers);
|
||||
|
||||
// Verify the expression were updated.
|
||||
REQUIRE(EnsureStandardEvent(layout1.GetEvents().GetEvent(0))
|
||||
@@ -74,6 +77,8 @@ TEST_CASE("EventsBehaviorRenamer (instructions)", "[common]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
|
||||
auto &object1 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
@@ -109,7 +114,7 @@ TEST_CASE("EventsBehaviorRenamer (instructions)", "[common]") {
|
||||
// Rename MyBehavior.
|
||||
gd::EventsBehaviorRenamer behaviorRenamer(
|
||||
platform, "Object1", "MyBehavior", "MyRenamedBehavior");
|
||||
behaviorRenamer.Launch(layout1.GetEvents(), project, layout1);
|
||||
behaviorRenamer.Launch(layout1.GetEvents(), projectScopedContainers);
|
||||
|
||||
// Ensure the action using MyBehavior has its parameter renamed.
|
||||
REQUIRE(EnsureStandardEvent(layout1.GetEvents().GetEvent(0))
|
||||
|
@@ -20,6 +20,13 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
|
||||
// Add some variables and objects:
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 1);
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable", 2).GetChild("MyChild");
|
||||
layout1.GetVariables().InsertNew("MySceneStructureVariable2", 2).GetChild("MyChild");
|
||||
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
layout1.InsertNewObject(
|
||||
project, "MyExtension::Sprite", "MyOtherSpriteObject", 1);
|
||||
@@ -27,6 +34,15 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"MyExtension::FakeObjectWithDefaultBehavior",
|
||||
"FakeObjectWithDefaultBehavior",
|
||||
2);
|
||||
|
||||
// Also insert a variable having the same name as an object:
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "ObjectWithNameReused", 3);
|
||||
layout1.GetVariables().InsertNew("ObjectWithNameReused", 3).GetChild("MyChild");
|
||||
|
||||
// Also insert a global variable having the same name as a scene variable:
|
||||
layout1.GetVariables().InsertNew("SceneVariableWithNameReused", 4);
|
||||
project.GetVariables().InsertNew("SceneVariableWithNameReused", 0);
|
||||
|
||||
auto &group = layout1.GetObjectGroups().InsertNew("AllObjects");
|
||||
group.AddObject("MySpriteObject");
|
||||
group.AddObject("MyOtherSpriteObject");
|
||||
@@ -269,7 +285,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
parser.ParseExpression("MySpriteObject.GetObjectStringWith3Param("
|
||||
"MySpriteObject.GetObjectNumber() / 2.3, "
|
||||
"MySpriteObject.GetObjectStringWith1Param("
|
||||
"MyExtension::GetNumber()), test)");
|
||||
"MyExtension::GetNumber()), UnknownObject.Unknown)");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("string",
|
||||
"",
|
||||
codeGenerator,
|
||||
@@ -281,8 +297,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"MySpriteObject.getObjectStringWith3Param(MySpriteObject."
|
||||
"getObjectNumber() ?? 0 / 2.3, "
|
||||
"MySpriteObject.getObjectStringWith1Param(getNumber()) ?? \"\", "
|
||||
"/* Error during generation, unrecognized identifier type: "
|
||||
"unknown with value test */ \"test\") ?? \"\"");
|
||||
"0) ?? \"\"");
|
||||
}
|
||||
SECTION("missing parameter") {
|
||||
{
|
||||
@@ -392,9 +407,178 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"123) ?? \"\"");
|
||||
}
|
||||
}
|
||||
SECTION("Function name") {
|
||||
SECTION("Properties (1 level)") {
|
||||
gd::PropertiesContainer propertiesContainer(gd::EventsFunctionsContainer::Extension);
|
||||
|
||||
auto projectScopedContainersWithProperties = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithProperties.AddPropertiesContainer(propertiesContainer);
|
||||
|
||||
propertiesContainer.InsertNew("MyProperty");
|
||||
propertiesContainer.InsertNew("MyProperty2");
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithProperties);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyProperty + MyProperty2");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getPropertyMyProperty() + getPropertyMyProperty2()");
|
||||
}
|
||||
}
|
||||
SECTION("Parameters (1 level)") {
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ParameterMetadata param1;
|
||||
param1.SetName("MyParameter1");
|
||||
param1.SetType("number");
|
||||
gd::ParameterMetadata param2;
|
||||
param2.SetName("MyParameter2");
|
||||
param2.SetType("string");
|
||||
parameters.push_back(param1);
|
||||
parameters.push_back(param2);
|
||||
|
||||
auto projectScopedContainersWithParameters = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
projectScopedContainersWithParameters.AddParameters(parameters);
|
||||
|
||||
gd::EventsCodeGenerator codeGeneratorWithProperties(platform, projectScopedContainersWithParameters);
|
||||
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MyParameter1 + MyParameter2");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGeneratorWithProperties,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getParameterMyParameter1() + getParameterMyParameter2()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (1 level)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneVariable + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneVariable + MySceneVariable2");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneVariable).getAsNumber() + getLayoutVariable(MySceneVariable2).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (conflict with a global variable)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("SceneVariableWithNameReused + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(SceneVariableWithNameReused).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable.MyChild + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable.MyChild + MySceneStructureVariable2.MyChild");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Scene variables (2 levels with bracket accessor)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"MyChild\"] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySceneStructureVariable[\"MyChild\"] + MySceneStructureVariable2[\"MyChild\"]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getLayoutVariable(MySceneStructureVariable).getChild(\"MyChild\").getAsNumber() + getLayoutVariable(MySceneStructureVariable2).getChild(\"MyChild\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Object variable with non existing object (invalid)") {
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.GetObjectNumber");
|
||||
parser.ParseExpression("MyNonExistingSpriteObject.MyVariable");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
@@ -404,6 +588,124 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "0");
|
||||
}
|
||||
SECTION("Object variables (1 level)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable + MySpriteObject.MyVariable2");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getAsNumber() + getVariableForObject(MySpriteObject, MyVariable2).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (conflict with a scene variable)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("ObjectWithNameReused.MyVariable + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(ObjectWithNameReused, MyVariable).getAsNumber() + 1");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (1 level with bracket accessor) (invalid)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject[\"BracketNotationCantBeUsedHere\"] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "fakeBadVariable.getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable + MySpriteObject.MyVariable2");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getAsNumber() + getVariableForObject(MySpriteObject, MyVariable2).getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (2 levels)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable.MyChildVariable + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getChild(\"MyChildVariable\").getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable.MyChildVariable + MySpriteObject.MyVariable2.MyChildVariable");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getChild(\"MyChildVariable\").getAsNumber() + getVariableForObject(MySpriteObject, MyVariable2).getChild(\"MyChildVariable\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Object variables (2 levels with bracket accessor)") {
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable[\"MyChildVariable\"] + 1");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getChild(\"MyChildVariable\").getAsNumber() + 1");
|
||||
}
|
||||
{
|
||||
auto node =
|
||||
parser.ParseExpression("MySpriteObject.MyVariable[\"MyChildVariable\"] + MySpriteObject.MyVariable2[\"MyChildVariable\"]");
|
||||
gd::ExpressionCodeGenerator expressionCodeGenerator("number",
|
||||
"",
|
||||
codeGenerator,
|
||||
context);
|
||||
|
||||
REQUIRE(node);
|
||||
node->Visit(expressionCodeGenerator);
|
||||
REQUIRE(expressionCodeGenerator.GetOutput() == "getVariableForObject(MySpriteObject, MyVariable).getChild(\"MyChildVariable\").getAsNumber() + getVariableForObject(MySpriteObject, MyVariable2).getChild(\"MyChildVariable\").getAsNumber()");
|
||||
}
|
||||
}
|
||||
SECTION("Invalid variables") {
|
||||
SECTION("empty variable") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
@@ -459,7 +761,7 @@ TEST_CASE("ExpressionCodeGenerator", "[common][events]") {
|
||||
"\"world\" ]", "")
|
||||
== "getLayoutVariable(myVariable).getChild(\"hello\" + \"world\")");
|
||||
}
|
||||
SECTION("object variable") {
|
||||
SECTION("object variable (legacy)") {
|
||||
REQUIRE(gd::ExpressionCodeGenerator::GenerateExpressionCode(
|
||||
codeGenerator, context, "objectvar", "myVariable", "MySpriteObject")
|
||||
== "getVariableForObject(MySpriteObject, myVariable)");
|
||||
|
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
#include "GDCore/IDE/Events/ExpressionCompletionFinder.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
@@ -12,6 +14,7 @@
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
@@ -19,7 +22,14 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto& layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
|
||||
layout1.GetVariables().InsertNew("myVariable");
|
||||
auto& object1 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
|
||||
object1.GetVariables().InsertNew("myObjectVariable");
|
||||
|
||||
gd::ProjectScopedContainers projectScopedContainers =
|
||||
gd::ProjectScopedContainers::
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
@@ -28,38 +38,49 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
size_t location) {
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
return gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
|
||||
platform, project, layout1, type, *node, location);
|
||||
auto completions =
|
||||
gd::ExpressionCompletionFinder::GetCompletionDescriptionsFor(
|
||||
platform, projectScopedContainers, type, *node, location);
|
||||
|
||||
std::vector<gd::String> completionsAsString;
|
||||
for (const auto& completion : completions) {
|
||||
completionsAsString.push_back(completion.ToString());
|
||||
}
|
||||
return completionsAsString;
|
||||
};
|
||||
|
||||
const std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedEmptyCompletions;
|
||||
const std::vector<gd::String> expectedEmptyCompletions;
|
||||
|
||||
SECTION("Identifier") {
|
||||
SECTION("Object or expression completions when type is string") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "My", 0, 2)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "My", 2) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions when type is number") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("number", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"number", "My", 0, 2)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "My", 2) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions when type is number|string") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"number|string", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"number|string", "My", 0, 2)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number|string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number|string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number|string", "My", 0) ==
|
||||
expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number|string", "My", 1) ==
|
||||
@@ -68,26 +89,51 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions in a variable name") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("string", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "My", 0, 2)};
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My", 54) == expectedEmptyCompletions);
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My",
|
||||
52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My",
|
||||
53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[\"abc\" + My",
|
||||
54) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object or expression completions in a variable index") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("number", "My", 0, 2),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"number", "My", 0, 2)};
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::GetVariableAsNumber(MyVariable[12345 + My", 54) == expectedEmptyCompletions);
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, number, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number", "My", 0, 2).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[12345 + My",
|
||||
52) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[12345 + My",
|
||||
53) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor(
|
||||
"number",
|
||||
"MyExtension::GetVariableAsNumber(MyVariable[12345 + My",
|
||||
54) == expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Object when type is an object") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("object", "My", 0, 2)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, object, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("object", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("object", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("object", "My", 2) == expectedEmptyCompletions);
|
||||
@@ -96,9 +142,11 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
SECTION("Object when type is an object (alternate type)") {
|
||||
// Also test alternate types also considered as objects (but that can
|
||||
// result in different code generation):
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"objectPtr", "My", 0, 2)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, objectPtr, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 0) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 1) == expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("objectPtr", "My", 2) ==
|
||||
@@ -121,13 +169,15 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
|
||||
SECTION("Free function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Function", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedExactCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
"string", "Function", 0, 8)
|
||||
.SetIsExact(true)};
|
||||
.ToString()};
|
||||
std::vector<gd::String> expectedExactCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
"string", "Function", 0, 8)
|
||||
.SetIsExact(true)
|
||||
.ToString()};
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 0) ==
|
||||
expectedCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "Function(", 1) ==
|
||||
@@ -147,43 +197,48 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
REQUIRE(getCompletionsFor("string", "Function(\"", 9) ==
|
||||
expectedEmptyCompletions);
|
||||
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject("unknown", "a", 9, 10),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"unknown", "a", 9, 10)};
|
||||
REQUIRE(getCompletionsFor("string", "Function(a", 9) ==
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 0, unknown, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }",
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("unknown", "My", 9, 10).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "Function(My", 10) ==
|
||||
expectedCompletions);
|
||||
}
|
||||
SECTION("Function with a Variable as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForVariable(
|
||||
"scenevar", "myVar", 33, 38)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 3, no type, 1, no prefix, myVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number",
|
||||
"MyExtension::GetVariableAsNumber(myVar",
|
||||
33) == expectedCompletions);
|
||||
}
|
||||
SECTION("Object function with a Variable as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForVariable(
|
||||
"objectvar", "myVar", 35, 40, "MyObject")};
|
||||
getCompletionsFor("number",
|
||||
"MyObject.GetObjectVariableAsNumber(myVar",
|
||||
35);
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
"{ 3, no type, 1, no prefix, myObjectVariable, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, no object configuration }",
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number",
|
||||
"MyObject.GetObjectVariableAsNumber(myVar",
|
||||
"MyObject.GetObjectVariableAsNumber(myObj",
|
||||
35) == expectedCompletions);
|
||||
}
|
||||
SECTION("Function with a Layer as argument") {
|
||||
std::vector<gd::ExpressionCompletionDescription> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForText(
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedCompletions{
|
||||
gd::ExpressionCompletionDescription::ForTextWithPrefix(
|
||||
"layer",
|
||||
gd::MetadataProvider::GetExpressionMetadata(platform,
|
||||
"MyExtension::MouseX")
|
||||
.GetParameter(0),
|
||||
gd::MetadataProvider::GetExpressionMetadata(platform, "MyExtension::MouseX").GetParameter(0),
|
||||
"",
|
||||
20,
|
||||
21,
|
||||
false)};
|
||||
false)
|
||||
.ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number", "MyExtension::MouseX(\"", 20) ==
|
||||
expectedCompletions);
|
||||
}
|
||||
@@ -191,16 +246,15 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
|
||||
SECTION("Partial object or behavior function") {
|
||||
SECTION("Test with string type") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"Func", 9, 13, "MyObject"),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 9, 13, "MyObject")};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 9, 13, "MyObject").ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func", 7) ==
|
||||
@@ -215,16 +269,15 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
expectedEmptyCompletions);
|
||||
}
|
||||
SECTION("Test with 'number|string' type") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"number|string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"Func", 9, 13, "MyObject"),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"number|string", "Func", 9, 13, "MyObject")};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, number|string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("number|string", "Func", 9, 13, "MyObject").ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("number|string", "MyObject.Func", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("number|string", "MyObject.Func", 7) ==
|
||||
@@ -242,21 +295,18 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
|
||||
SECTION("Object function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"Func", 9, 13, "MyObject"),
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 9, 13, "MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 9, 13, "MyObject")
|
||||
.SetIsExact(true)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorOrFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("Func", 9, 13, "MyObject").ToString(),
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 9, 13, "MyObject").ToString()
|
||||
};
|
||||
std::vector<gd::String> expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 9, 13, "MyObject").SetIsExact(true).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.Func(", 7) ==
|
||||
@@ -278,18 +328,16 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
|
||||
SECTION("Partial behavior function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"MyBehavior", 9, 19, "MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 21, 25, "MyObject", "MyBehavior")};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()};
|
||||
std::vector<gd::String> expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 21, 25, "MyObject", "MyBehavior").ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func", 7) ==
|
||||
@@ -310,18 +358,17 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
expectedFunctionCompletions);
|
||||
}
|
||||
SECTION("Test 2") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"MyBehavior", 9, 19, "MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "", 21, 21, "MyObject", "MyBehavior")};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()
|
||||
};
|
||||
std::vector<gd::String> expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "", 21, 21, "MyObject", "MyBehavior").ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::", 7) ==
|
||||
@@ -341,23 +388,20 @@ TEST_CASE("ExpressionCompletionFinder", "[common][events]") {
|
||||
|
||||
SECTION("Behavior function") {
|
||||
SECTION("Test 1") {
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedObjectCompletions{
|
||||
gd::ExpressionCompletionDescription::ForObject(
|
||||
"string", "MyObject", 0, 8)};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehavior(
|
||||
"MyBehavior", 9, 19, "MyObject")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 21, 25, "MyObject", "MyBehavior")};
|
||||
std::vector<gd::ExpressionCompletionDescription>
|
||||
expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpression(
|
||||
"string", "Func", 21, 25, "MyObject", "MyBehavior")
|
||||
.SetIsExact(true)};
|
||||
// clang-format off
|
||||
std::vector<gd::String> expectedObjectCompletions{
|
||||
"{ 0, string, 1, no prefix, MyObject, no object name, no behavior name, non-exact, not last parameter, no parameter metadata, with object configuration }"
|
||||
};
|
||||
std::vector<gd::String> expectedBehaviorCompletions{
|
||||
gd::ExpressionCompletionDescription::ForBehaviorWithPrefix("MyBehavior", 9, 19, "MyObject").ToString()
|
||||
};
|
||||
std::vector<gd::String> expectedFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 21, 25, "MyObject", "MyBehavior").ToString()
|
||||
};
|
||||
std::vector<gd::String> expectedExactFunctionCompletions{
|
||||
gd::ExpressionCompletionDescription::ForExpressionWithPrefix("string", "Func", 21, 25, "MyObject", "MyBehavior").SetIsExact(true).ToString()
|
||||
};
|
||||
// clang-format on
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 0) ==
|
||||
expectedObjectCompletions);
|
||||
REQUIRE(getCompletionsFor("string", "MyObject.MyBehavior::Func(", 7) ==
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@
|
||||
#include "GDCore/IDE/Events/ExpressionValidator.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("ExpressionParser2 - Benchmarks", "[common][events]") {
|
||||
@@ -21,14 +22,16 @@ TEST_CASE("ExpressionParser2 - Benchmarks", "[common][events]") {
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0);
|
||||
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout1);
|
||||
|
||||
gd::ExpressionParser2 parser;
|
||||
|
||||
auto parseExpression = [&parser, &project, &platform, &layout1](const gd::String &expression) {
|
||||
auto parseExpressionWithType = [&parser, &project, &platform, &layout1,
|
||||
auto parseExpression = [&parser, &platform, &projectScopedContainers](const gd::String &expression) {
|
||||
auto parseExpressionWithType = [&parser, &platform, &projectScopedContainers,
|
||||
&expression](const gd::String &type) {
|
||||
auto node = parser.ParseExpression(expression);
|
||||
REQUIRE(node != nullptr);
|
||||
gd::ExpressionValidator validator(platform, project, layout1, type);
|
||||
gd::ExpressionValidator validator(platform, projectScopedContainers, type);
|
||||
node->Visit(validator);
|
||||
};
|
||||
|
||||
|
@@ -17,13 +17,6 @@
|
||||
|
||||
using namespace gd;
|
||||
|
||||
namespace {
|
||||
|
||||
void SetupProject(gd::Project &project, gd::Platform &platform){
|
||||
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Layout", "[common]") {
|
||||
|
||||
SECTION("Find the type of a behavior in a object") {
|
||||
|
39
Core/tests/String.cpp
Normal file
39
Core/tests/String.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 String class of GDevelop Core.
|
||||
*/
|
||||
#include "GDCore/String.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("String", "[common]") {
|
||||
SECTION("ReplaceConsecutiveOccurrences") {
|
||||
gd::String str = " ";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), ' ') ==
|
||||
" ");
|
||||
str = "L'opacité de NewSprite = \" \"";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), ' ') ==
|
||||
"L'opacité de NewSprite = \" \"");
|
||||
str = "ccccccccc";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), 'c') ==
|
||||
"c");
|
||||
str = "c";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), 'c') ==
|
||||
"c");
|
||||
str = "";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), ' ') ==
|
||||
"");
|
||||
str = "Set animation of NewSprite to ";
|
||||
REQUIRE(str.RemoveConsecutiveOccurrences(str.begin(), str.end(), ' ') ==
|
||||
"Set animation of NewSprite to ");
|
||||
}
|
||||
}
|
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* 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 project refactoring
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "DummyPlatform.h"
|
||||
#include "GDCore/Events/Builtin/ForEachChildVariableEvent.h"
|
||||
#include "GDCore/Events/Builtin/LinkEvent.h"
|
||||
#include "GDCore/Events/Builtin/RepeatEvent.h"
|
||||
#include "GDCore/Events/Builtin/StandardEvent.h"
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/WholeProjectRefactorer.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
TEST_CASE("WholeProjectRefactorer::ApplyRefactoringForVariablesContainer",
|
||||
"[common]") {
|
||||
SECTION("Variable renamed (project, layout, object)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
gd::StandardEvent &event =
|
||||
dynamic_cast<gd::StandardEvent &>(layout1.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::Standard"));
|
||||
gd::ForEachChildVariableEvent &forEachChildVariableEvent =
|
||||
dynamic_cast<gd::ForEachChildVariableEvent &>(
|
||||
layout1.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::ForEachChildVariable"));
|
||||
gd::RepeatEvent &repeatEvent =
|
||||
dynamic_cast<gd::RepeatEvent &>(layout1.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::Repeat"));
|
||||
|
||||
// Declare global variables.
|
||||
project.GetVariables().InsertNew("MyGlobalVariable", 0).SetValue(123);
|
||||
project.GetVariables().InsertNew("MyGlobalVariable2", 0).SetValue(123);
|
||||
project.GetVariables().InsertNew("SharedVariableName", 0).SetValue(123);
|
||||
project.GetVariables()
|
||||
.InsertNew("MyGlobalStructureVariable", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
project.GetVariables()
|
||||
.InsertNew("MyGlobalStructureVariable2", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
// Declare variables in the scene.
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0).SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 0).SetValue(123);
|
||||
layout1.GetVariables().InsertNew("SharedVariableName", 0).SetValue(123);
|
||||
layout1.GetVariables()
|
||||
.InsertNew("MySceneStructureVariable", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
layout1.GetVariables()
|
||||
.InsertNew("MySceneStructureVariable2", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
// Declare variables in objects.
|
||||
auto &object1 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
object1.GetVariables().InsertNew("MyObjectVariable");
|
||||
object1.GetVariables()
|
||||
.InsertNew("MyObjectStructureVariable")
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
auto &object2 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
|
||||
object2.GetVariables().InsertNew("MyObjectVariable");
|
||||
object2.GetVariables()
|
||||
.InsertNew("MyObjectStructureVariable")
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
// Create an event using the variables.
|
||||
// clang-format off
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomething");
|
||||
action.SetParametersCount(1);
|
||||
action.SetParameter(
|
||||
0,
|
||||
gd::Expression(
|
||||
"1 + "
|
||||
"MySceneVariable + "
|
||||
"MySceneVariable2 + "
|
||||
"Object1.MyObjectVariable + "
|
||||
"Object2.MyObjectVariable + "
|
||||
"Object1.MyObjectStructureVariable.MyChild + "
|
||||
"Object2.MyObjectStructureVariable.MyChild + "
|
||||
"MySceneStructureVariable.MyChild + "
|
||||
"MySceneStructureVariable2.MyChild + "
|
||||
"MyGlobalVariable + "
|
||||
"MyGlobalVariable2 + "
|
||||
"MyGlobalStructureVariable.MyChild + "
|
||||
"MyGlobalStructureVariable2.MyChild"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
// Expressions with "old" "scenevar", "globalvar", "objectvar":
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomething");
|
||||
action.SetParametersCount(1);
|
||||
action.SetParameter(
|
||||
0,
|
||||
gd::Expression(
|
||||
// "objectvar" (in a free expression):
|
||||
"1 + "
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectVariable, Object2, MyObjectVariable) + "
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild) + "
|
||||
// "objectvar" (using the name of the object being called):
|
||||
"Object1.GetObjectVariableAsNumber(MyObjectVariable) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectVariable) + "
|
||||
"Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
|
||||
"Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
|
||||
// "globalvar" and "scenevar":
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable2) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(SharedVariableName) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneVariable) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneVariable2) + "
|
||||
"MyExtension::GetVariableAsNumber(SharedVariableName) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable.MyChild) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable.MyChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable.MyChild.GrandChild) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild.GrandChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable.MyChild.GrandChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MySceneVariable"));
|
||||
action.SetParameter(1, gd::Expression("MyGlobalVariable"));
|
||||
action.SetParameter(2, gd::Expression("Object1"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("SharedVariableName"));
|
||||
action.SetParameter(1, gd::Expression("SharedVariableName"));
|
||||
action.SetParameter(2, gd::Expression("Object2"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
|
||||
forEachChildVariableEvent.SetIterableVariableName("MySceneStructureVariable");
|
||||
repeatEvent.SetRepeatExpression("1 + MySceneVariable + Object1.MyObjectVariable + Object2.MyObjectVariable");
|
||||
// clang-format on
|
||||
|
||||
// Do a copy of layout1 to ensure other scene is unchanged after the
|
||||
// refactoring.
|
||||
gd::Layout layout2 = layout1;
|
||||
layout2.SetName("Layout2");
|
||||
project.InsertLayout(layout2, 1);
|
||||
gd::SerializerElement originalSerializedLayout2;
|
||||
layout2.SerializeTo(originalSerializedLayout2);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
project.GetVariables().ResetPersistentUuid();
|
||||
layout1.GetVariables().ResetPersistentUuid();
|
||||
object1.ResetPersistentUuid();
|
||||
gd::SerializerElement originalSerializedProjectVariables;
|
||||
project.GetVariables().SerializeTo(originalSerializedProjectVariables);
|
||||
gd::SerializerElement originalSerializedLayoutVariables;
|
||||
layout1.GetVariables().SerializeTo(originalSerializedLayoutVariables);
|
||||
gd::SerializerElement originalSerializedObject1Variables;
|
||||
object1.GetVariables().SerializeTo(originalSerializedObject1Variables);
|
||||
|
||||
project.GetVariables().Rename("MyGlobalVariable",
|
||||
"MyRenamedGlobalVariable");
|
||||
project.GetVariables().Rename("MyGlobalStructureVariable",
|
||||
"MyRenamedGlobalStructureVariable");
|
||||
project.GetVariables().Rename("SharedVariableName",
|
||||
"RenamedGlobalVariableFromASharedName");
|
||||
auto changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedProjectVariables,
|
||||
project.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
project.GetVariables(),
|
||||
changeset);
|
||||
|
||||
layout1.GetVariables().Rename("MySceneVariable", "MyRenamedSceneVariable");
|
||||
layout1.GetVariables().Rename("MySceneStructureVariable",
|
||||
"MyRenamedSceneStructureVariable");
|
||||
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedLayoutVariables,
|
||||
layout1.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
layout1.GetVariables(),
|
||||
changeset);
|
||||
|
||||
object1.GetVariables().Rename("MyObjectVariable",
|
||||
"MyRenamedObjectVariable");
|
||||
object1.GetVariables().Rename("MyObjectStructureVariable",
|
||||
"MyRenamedObjectStructureVariable");
|
||||
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedObject1Variables,
|
||||
object1.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
object1.GetVariables(),
|
||||
changeset);
|
||||
|
||||
// Check the first layout is updated.
|
||||
// clang-format off
|
||||
{
|
||||
// Updated direct access to variables:
|
||||
REQUIRE(event.GetActions()[0].GetParameter(0).GetPlainString() ==
|
||||
"1 + "
|
||||
"MyRenamedSceneVariable + "
|
||||
"MySceneVariable2 + "
|
||||
"Object1.MyRenamedObjectVariable + "
|
||||
"Object2.MyObjectVariable + "
|
||||
"Object1.MyRenamedObjectStructureVariable.MyChild + "
|
||||
"Object2.MyObjectStructureVariable.MyChild + "
|
||||
"MyRenamedSceneStructureVariable.MyChild + "
|
||||
"MySceneStructureVariable2.MyChild + "
|
||||
"MyRenamedGlobalVariable + "
|
||||
"MyGlobalVariable2 + "
|
||||
"MyRenamedGlobalStructureVariable.MyChild + "
|
||||
"MyGlobalStructureVariable2.MyChild"
|
||||
);
|
||||
|
||||
// Updated access to variables using the legacy "pre-scoped" "scenevar",
|
||||
// "globalvar" and "objectvar" parameters in expressions:
|
||||
REQUIRE(event.GetActions()[1].GetParameter(0).GetPlainString() ==
|
||||
"1 + "
|
||||
// Multiple "objectvar" parameters in a free function:
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyRenamedObjectVariable, Object2, MyObjectVariable) + "
|
||||
// Multiple "objectvar" parameters in a free function, with child
|
||||
// variable:
|
||||
"MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyRenamedObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild) + "
|
||||
// Single "objectvar" from the object being accessed:
|
||||
"Object1.GetObjectVariableAsNumber(MyRenamedObjectVariable) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectVariable) + "
|
||||
// Single "objectvar" from the object being accessed, with child
|
||||
// variales:
|
||||
"Object1.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild) + "
|
||||
"Object1.GetObjectVariableAsNumber(MyRenamedObjectStructureVariable.MyChild.GrandChild) + "
|
||||
"Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild) + "
|
||||
// "globalvar" and "scenevar" in a free function:
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyRenamedGlobalVariable) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable2) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(RenamedGlobalVariableFromASharedName) + "
|
||||
"MyExtension::GetVariableAsNumber(MyRenamedSceneVariable) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneVariable2) + "
|
||||
"MyExtension::GetVariableAsNumber(SharedVariableName) + "
|
||||
// "globalvar" and "scenevar" in a free function, with child
|
||||
// variables:
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyRenamedGlobalStructureVariable.MyChild) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MyRenamedSceneStructureVariable.MyChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild) + "
|
||||
// "globalvar" and "scenevar" in a free function, with grand child
|
||||
// variables:
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyRenamedGlobalStructureVariable.MyChild.GrandChild) + "
|
||||
"MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild.GrandChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MyRenamedSceneStructureVariable.MyChild.GrandChild) + "
|
||||
"MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild.GrandChild)");
|
||||
|
||||
// Updated "scenevar", "globalvar" and "object" parameters of an
|
||||
// instruction:
|
||||
REQUIRE(event.GetActions()[2].GetParameter(0).GetPlainString() ==
|
||||
"MyRenamedSceneVariable");
|
||||
REQUIRE(event.GetActions()[2].GetParameter(1).GetPlainString() ==
|
||||
"MyRenamedGlobalVariable");
|
||||
REQUIRE(event.GetActions()[2].GetParameter(2).GetPlainString() ==
|
||||
"Object1");
|
||||
REQUIRE(event.GetActions()[2].GetParameter(3).GetPlainString() ==
|
||||
"MyRenamedObjectVariable");
|
||||
|
||||
// Updated "scenevar" and "globalvar" parameters of an instruction:
|
||||
REQUIRE(event.GetActions()[3].GetParameter(0).GetPlainString() ==
|
||||
"SharedVariableName");
|
||||
REQUIRE(event.GetActions()[3].GetParameter(1).GetPlainString() ==
|
||||
"RenamedGlobalVariableFromASharedName");
|
||||
|
||||
// Unchanged "object" and "objectvar" parameters:
|
||||
REQUIRE(event.GetActions()[3].GetParameter(2).GetPlainString() ==
|
||||
"Object2");
|
||||
REQUIRE(event.GetActions()[3].GetParameter(3).GetPlainString() ==
|
||||
"MyObjectVariable");
|
||||
}
|
||||
|
||||
REQUIRE(forEachChildVariableEvent.GetIterableVariableName() == "MyRenamedSceneStructureVariable");
|
||||
REQUIRE(repeatEvent.GetRepeatExpression() == "1 + MyRenamedSceneVariable + Object1.MyRenamedObjectVariable + Object2.MyObjectVariable");
|
||||
// clang-format on
|
||||
|
||||
// Check the other layout is untouched.
|
||||
{
|
||||
gd::SerializerElement serializedLayout2;
|
||||
layout2.SerializeTo(serializedLayout2);
|
||||
REQUIRE(gd::Serializer::ToJSON(serializedLayout2) ==
|
||||
gd::Serializer::ToJSON(originalSerializedLayout2));
|
||||
}
|
||||
}
|
||||
SECTION("Variable removed (project, layout, object)") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
SetupProjectWithDummyPlatform(project, platform);
|
||||
auto &layout1 = project.InsertNewLayout("Layout1", 0);
|
||||
gd::StandardEvent &event =
|
||||
dynamic_cast<gd::StandardEvent &>(layout1.GetEvents().InsertNewEvent(
|
||||
project, "BuiltinCommonInstructions::Standard"));
|
||||
|
||||
// Declare global variables.
|
||||
project.GetVariables().InsertNew("MyGlobalVariable", 0).SetValue(123);
|
||||
project.GetVariables().InsertNew("MyGlobalVariable2", 0).SetValue(123);
|
||||
project.GetVariables().InsertNew("SharedVariableName", 0).SetValue(123);
|
||||
project.GetVariables()
|
||||
.InsertNew("MyGlobalStructureVariable", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
project.GetVariables()
|
||||
.InsertNew("MyGlobalStructureVariable2", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
// Declare variables in the scene.
|
||||
layout1.GetVariables().InsertNew("MySceneVariable", 0).SetValue(123);
|
||||
layout1.GetVariables().InsertNew("MySceneVariable2", 0).SetValue(123);
|
||||
layout1.GetVariables().InsertNew("SharedVariableName", 0).SetValue(123);
|
||||
layout1.GetVariables()
|
||||
.InsertNew("MySceneStructureVariable", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
layout1.GetVariables()
|
||||
.InsertNew("MySceneStructureVariable2", 0)
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
// Declare variables in objects.
|
||||
auto &object1 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
|
||||
object1.GetVariables().InsertNew("MyObjectVariable");
|
||||
object1.GetVariables()
|
||||
.InsertNew("MyObjectStructureVariable")
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
auto &object2 =
|
||||
layout1.InsertNewObject(project, "MyExtension::Sprite", "Object2", 0);
|
||||
object2.GetVariables().InsertNew("MyObjectVariable");
|
||||
object2.GetVariables()
|
||||
.InsertNew("MyObjectStructureVariable")
|
||||
.GetChild("MyChild")
|
||||
.SetValue(123);
|
||||
|
||||
auto makeTestAction = [](const gd::String &expression) {
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomething");
|
||||
action.SetParametersCount(1);
|
||||
action.SetParameter(0, expression);
|
||||
return action;
|
||||
};
|
||||
|
||||
// Create an event using the variables.
|
||||
// clang-format off
|
||||
event.GetActions().Insert(makeTestAction("1 + MySceneVariable"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MySceneVariable2"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object1.MyObjectVariable"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object2.MyObjectVariable"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object1.MyObjectStructureVariable.MyChild"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object2.MyObjectStructureVariable.MyChild"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MySceneStructureVariable.MyChild"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MySceneStructureVariable2.MyChild"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyGlobalVariable"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyGlobalVariable2"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyGlobalStructureVariable.MyChild"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyGlobalStructureVariable2.MyChild"));
|
||||
|
||||
// Expressions with "old" "scenevar", "globalvar", "objectvar":
|
||||
// "objectvar" (in a free expression):
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectVariable, Object2, MyObjectVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object2, MyObjectVariable, Object2, MyObjectVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object1, MyObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object2, MyObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild)"));
|
||||
// "objectvar" (using the name of the object being called):
|
||||
event.GetActions().Insert(makeTestAction("1 + Object1.GetObjectVariableAsNumber(MyObjectVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object2.GetObjectVariableAsNumber(MyObjectVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object1.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable2)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(SharedVariableName)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneVariable)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneVariable2)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(SharedVariableName)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable.MyChild.GrandChild)"));
|
||||
event.GetActions().Insert(makeTestAction("1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild.GrandChild)"));
|
||||
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MySceneVariable")); // To remove
|
||||
action.SetParameter(1, gd::Expression("MyGlobalVariable2"));
|
||||
action.SetParameter(2, gd::Expression("Object2"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MySceneVariable2"));
|
||||
action.SetParameter(1, gd::Expression("MyGlobalVariable")); // To remove
|
||||
action.SetParameter(2, gd::Expression("Object2"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MySceneVariable2"));
|
||||
action.SetParameter(1, gd::Expression("MyGlobalVariable2"));
|
||||
action.SetParameter(2, gd::Expression("Object1"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable")); // To remove
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
{
|
||||
gd::Instruction action;
|
||||
action.SetType("MyExtension::DoSomethingWithLegacyPreScopedVariables");
|
||||
action.SetParametersCount(4);
|
||||
action.SetParameter(0, gd::Expression("MySceneVariable2"));
|
||||
action.SetParameter(1, gd::Expression("MyGlobalVariable2"));
|
||||
action.SetParameter(2, gd::Expression("Object2"));
|
||||
action.SetParameter(3, gd::Expression("MyObjectVariable"));
|
||||
event.GetActions().Insert(action);
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// Do a copy of layout1 to ensure other scene is unchanged after the
|
||||
// refactoring.
|
||||
gd::Layout layout2 = layout1;
|
||||
layout2.SetName("Layout2");
|
||||
project.InsertLayout(layout2, 1);
|
||||
gd::SerializerElement originalSerializedLayout2;
|
||||
layout2.SerializeTo(originalSerializedLayout2);
|
||||
|
||||
// Do the changes and launch the refactoring.
|
||||
project.GetVariables().ResetPersistentUuid();
|
||||
layout1.GetVariables().ResetPersistentUuid();
|
||||
object1.ResetPersistentUuid();
|
||||
gd::SerializerElement originalSerializedProjectVariables;
|
||||
project.GetVariables().SerializeTo(originalSerializedProjectVariables);
|
||||
gd::SerializerElement originalSerializedLayoutVariables;
|
||||
layout1.GetVariables().SerializeTo(originalSerializedLayoutVariables);
|
||||
gd::SerializerElement originalSerializedObject1Variables;
|
||||
object1.GetVariables().SerializeTo(originalSerializedObject1Variables);
|
||||
|
||||
project.GetVariables().Remove("MyGlobalVariable");
|
||||
project.GetVariables().Remove("MyGlobalStructureVariable");
|
||||
project.GetVariables().Remove("SharedVariableName");
|
||||
auto changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedProjectVariables,
|
||||
project.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
project.GetVariables(),
|
||||
changeset);
|
||||
|
||||
layout1.GetVariables().Remove("MySceneVariable");
|
||||
layout1.GetVariables().Remove("MySceneStructureVariable");
|
||||
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedLayoutVariables,
|
||||
layout1.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
layout1.GetVariables(),
|
||||
changeset);
|
||||
|
||||
object1.GetVariables().Remove("MyObjectVariable");
|
||||
object1.GetVariables().Remove("MyObjectStructureVariable");
|
||||
changeset = gd::WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
project,
|
||||
originalSerializedObject1Variables,
|
||||
object1.GetVariables());
|
||||
gd::WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
project,
|
||||
object1.GetVariables(),
|
||||
changeset);
|
||||
|
||||
// Check the first layout is updated.
|
||||
{
|
||||
REQUIRE(event.GetActions().GetCount() == 19);
|
||||
|
||||
// clang-format off
|
||||
// All the actions using the removed variables are gone.
|
||||
REQUIRE(event.GetActions()[0].GetParameter(0).GetPlainString() == "1 + MySceneVariable2");
|
||||
REQUIRE(event.GetActions()[1].GetParameter(0).GetPlainString() == "1 + Object2.MyObjectVariable");
|
||||
REQUIRE(event.GetActions()[2].GetParameter(0).GetPlainString() == "1 + Object2.MyObjectStructureVariable.MyChild");
|
||||
REQUIRE(event.GetActions()[3].GetParameter(0).GetPlainString() == "1 + MySceneStructureVariable2.MyChild");
|
||||
REQUIRE(event.GetActions()[4].GetParameter(0).GetPlainString() == "1 + MyGlobalVariable2");
|
||||
REQUIRE(event.GetActions()[5].GetParameter(0).GetPlainString() == "1 + MyGlobalStructureVariable2.MyChild");
|
||||
REQUIRE(event.GetActions()[6].GetParameter(0).GetPlainString() == "1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object2, MyObjectVariable, Object2, MyObjectVariable)");
|
||||
REQUIRE(event.GetActions()[7].GetParameter(0).GetPlainString() == "1 + MyExtension::GetStringWith2ObjectParamAnd2ObjectVarParam(Object2, MyObjectStructureVariable.MyChild, Object2, MyObjectStructureVariable.MyChild)");
|
||||
REQUIRE(event.GetActions()[8].GetParameter(0).GetPlainString() == "1 + Object2.GetObjectVariableAsNumber(MyObjectVariable)");
|
||||
REQUIRE(event.GetActions()[9].GetParameter(0).GetPlainString() == "1 + Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild)");
|
||||
REQUIRE(event.GetActions()[10].GetParameter(0).GetPlainString() == "1 + Object2.GetObjectVariableAsNumber(MyObjectStructureVariable.MyChild.GrandChild)");
|
||||
REQUIRE(event.GetActions()[11].GetParameter(0).GetPlainString() == "1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalVariable2)");
|
||||
REQUIRE(event.GetActions()[12].GetParameter(0).GetPlainString() == "1 + MyExtension::GetVariableAsNumber(MySceneVariable2)");
|
||||
REQUIRE(event.GetActions()[13].GetParameter(0).GetPlainString() == "1 + MyExtension::GetVariableAsNumber(SharedVariableName)");
|
||||
REQUIRE(event.GetActions()[14].GetParameter(0).GetPlainString() == "1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild)");
|
||||
REQUIRE(event.GetActions()[15].GetParameter(0).GetPlainString() == "1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild)");
|
||||
REQUIRE(event.GetActions()[16].GetParameter(0).GetPlainString() == "1 + MyExtension::GetGlobalVariableAsNumber(MyGlobalStructureVariable2.MyChild.GrandChild)");
|
||||
REQUIRE(event.GetActions()[17].GetParameter(0).GetPlainString() == "1 + MyExtension::GetVariableAsNumber(MySceneStructureVariable2.MyChild.GrandChild)");
|
||||
|
||||
REQUIRE(event.GetActions()[18].GetParameter(0).GetPlainString() == "MySceneVariable2");
|
||||
REQUIRE(event.GetActions()[18].GetParameter(1).GetPlainString() == "MyGlobalVariable2");
|
||||
REQUIRE(event.GetActions()[18].GetParameter(2).GetPlainString() == "Object2");
|
||||
REQUIRE(event.GetActions()[18].GetParameter(3).GetPlainString() == "MyObjectVariable");
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// Check the other layout is untouched.
|
||||
{
|
||||
gd::SerializerElement serializedLayout2;
|
||||
layout2.SerializeTo(serializedLayout2);
|
||||
REQUIRE(gd::Serializer::ToJSON(serializedLayout2) ==
|
||||
gd::Serializer::ToJSON(originalSerializedLayout2));
|
||||
}
|
||||
}
|
||||
}
|
@@ -306,7 +306,7 @@ const void SetupEvents(gd::EventsList &eventList) {
|
||||
action.SetParameter(
|
||||
0,
|
||||
gd::Expression(
|
||||
"ObjectWithMyBehavior.GetObjectNumber()"));
|
||||
"ObjectWithMyBehavior.GetObjectNumber() + ObjectWithMyBehavior.MyVariable + ObjectWithMyBehavior.MyStructureVariable.Child"));
|
||||
event.GetActions().Insert(action);
|
||||
eventList.InsertEvent(event);
|
||||
}
|
||||
@@ -415,13 +415,13 @@ const void SetupEvents(gd::EventsList &eventList) {
|
||||
if (eventList.GetEventsCount() != BehaviorSharedPropertyAction) {
|
||||
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
|
||||
}
|
||||
// Create an event in the layout using "MyProperty" action
|
||||
// Create an event in the layout using "MySharedProperty" action
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType(
|
||||
"MyEventsExtension::MyEventsBasedBehavior::" +
|
||||
gd::EventsBasedBehavior::GetSharedPropertyActionName("MyProperty"));
|
||||
gd::EventsBasedBehavior::GetSharedPropertyActionName("MySharedProperty"));
|
||||
event.GetActions().Insert(instruction);
|
||||
eventList.InsertEvent(event);
|
||||
}
|
||||
@@ -429,13 +429,13 @@ const void SetupEvents(gd::EventsList &eventList) {
|
||||
if (eventList.GetEventsCount() != BehaviorSharedPropertyCondition) {
|
||||
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
|
||||
}
|
||||
// Create an event in the layout using "MyProperty" condition
|
||||
// Create an event in the layout using "MySharedProperty" condition
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
instruction.SetType(
|
||||
"MyEventsExtension::MyEventsBasedBehavior::" +
|
||||
gd::EventsBasedBehavior::GetSharedPropertyConditionName("MyProperty"));
|
||||
gd::EventsBasedBehavior::GetSharedPropertyConditionName("MySharedProperty"));
|
||||
event.GetConditions().Insert(instruction);
|
||||
eventList.InsertEvent(event);
|
||||
}
|
||||
@@ -443,7 +443,7 @@ const void SetupEvents(gd::EventsList &eventList) {
|
||||
if (eventList.GetEventsCount() != BehaviorSharedPropertyExpression) {
|
||||
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
|
||||
}
|
||||
// Create an event in the layout using "MyProperty" expression
|
||||
// Create an event in the layout using "MySharedProperty" expression
|
||||
{
|
||||
gd::StandardEvent event;
|
||||
gd::Instruction instruction;
|
||||
@@ -452,7 +452,7 @@ const void SetupEvents(gd::EventsList &eventList) {
|
||||
instruction.SetParameter(
|
||||
0, gd::Expression("ObjectWithMyBehavior.MyBehavior::" +
|
||||
gd::EventsBasedBehavior::GetSharedPropertyExpressionName(
|
||||
"MyProperty") +
|
||||
"MySharedProperty") +
|
||||
"()"));
|
||||
event.GetActions().Insert(instruction);
|
||||
eventList.InsertEvent(event);
|
||||
@@ -929,11 +929,15 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
.SetFunctionType(gd::EventsFunction::ActionWithOperator)
|
||||
.SetGetterName("MyBehaviorEventsFunctionExpressionAndCondition");
|
||||
|
||||
// Add property
|
||||
// Add a property:
|
||||
eventsBasedBehavior.GetPropertyDescriptors()
|
||||
.InsertNew("MyProperty", 0)
|
||||
.SetType("Number");
|
||||
// The same name is used for the shared property to ensure there is no name
|
||||
// Add a shared property:
|
||||
eventsBasedBehavior.GetSharedPropertyDescriptors()
|
||||
.InsertNew("MySharedProperty", 0)
|
||||
.SetType("Number");
|
||||
// The same name is used for another shared property to ensure there is no name
|
||||
// collision.
|
||||
eventsBasedBehavior.GetSharedPropertyDescriptors()
|
||||
.InsertNew("MyProperty", 0)
|
||||
@@ -1068,6 +1072,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &childObject = eventsBasedObject.InsertNewObject(
|
||||
project, "MyExtension::Sprite", "ObjectWithMyBehavior", 0);
|
||||
childObject.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyBehavior");
|
||||
childObject.GetVariables().InsertNew("MyVariable");
|
||||
childObject.GetVariables().InsertNew("MyStructureVariable").CastTo(gd::Variable::Structure);
|
||||
auto &group = eventsBasedObject.GetObjectGroups().InsertNew("GroupWithMyBehavior");
|
||||
group.AddObject(childObject.GetName());
|
||||
|
||||
@@ -1154,6 +1160,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
|
||||
auto &object = layout.InsertNewObject(project, "MyExtension::Sprite",
|
||||
"ObjectWithMyBehavior", 0);
|
||||
object.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyBehavior");
|
||||
object.GetVariables().InsertNew("MyVariable");
|
||||
object.GetVariables().InsertNew("MyStructureVariable").CastTo(gd::Variable::Structure);
|
||||
auto &group = layout.GetObjectGroups().InsertNew("GroupWithMyBehavior", 0);
|
||||
group.AddObject("ObjectWithMyBehavior");
|
||||
|
||||
@@ -1455,10 +1463,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(eventsList->GetEvent(
|
||||
FreeFunctionWithObjects)) == "RenamedObjectWithMyBehavior");
|
||||
|
||||
// Check object name has been renamed in expressions.
|
||||
// Check object name has been renamed in expressions and in object variables.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
eventsList->GetEvent(FreeFunctionWithObjectExpression)) ==
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber()");
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber() + RenamedObjectWithMyBehavior.MyVariable + RenamedObjectWithMyBehavior.MyStructureVariable.Child");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1567,6 +1575,12 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
gd::ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
project, eventsFunction.GetParameters(), objectsContainer);
|
||||
|
||||
// Simulate a variable in ObjectWithMyBehavior, even if this is not
|
||||
// supported by the editor.
|
||||
auto& objectWithMyBehavior = objectsContainer.GetObject("ObjectWithMyBehavior");
|
||||
objectWithMyBehavior.GetVariables().InsertNew("MyVariable");
|
||||
objectWithMyBehavior.GetVariables().InsertNew("MyStructureVariable").CastTo(gd::Variable::Structure);
|
||||
|
||||
// Trigger the refactoring after the renaming of an object
|
||||
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
project, eventsFunction, globalObjectsContainer, objectsContainer,
|
||||
@@ -1579,11 +1593,11 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsFunction.GetEvents().GetEvent(FreeFunctionWithObjects)) ==
|
||||
"RenamedObjectWithMyBehavior");
|
||||
|
||||
// Check object name has been renamed in expressions.
|
||||
// Check object name has been renamed in expressions and object variables.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
eventsFunction.GetEvents().GetEvent(
|
||||
FreeFunctionWithObjectExpression)) ==
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber()");
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber() + RenamedObjectWithMyBehavior.MyVariable + RenamedObjectWithMyBehavior.MyStructureVariable.Child");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1618,10 +1632,10 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
objectFunctionEvents.GetEvent(FreeFunctionWithObjects)) ==
|
||||
"RenamedObjectWithMyBehavior");
|
||||
|
||||
// Check object name has been renamed in expressions.
|
||||
// Check object name has been renamed in expressions and object variables.
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
objectFunctionEvents.GetEvent(FreeFunctionWithObjectExpression)) ==
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber()");
|
||||
"RenamedObjectWithMyBehavior.GetObjectNumber() + RenamedObjectWithMyBehavior.MyVariable + RenamedObjectWithMyBehavior.MyStructureVariable.Child");
|
||||
}
|
||||
|
||||
SECTION("Object deleted (in events function)") {
|
||||
@@ -1742,7 +1756,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
eventsList->GetEvent(BehaviorActionWithOperator)) ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"MyBehaviorEventsFunctionActionWithOperator");
|
||||
|
||||
|
||||
// Check if events-based behaviors properties have been renamed in
|
||||
// instructions
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
@@ -1752,7 +1766,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
|
||||
"MyRenamedExtension::MyEventsBasedBehavior::"
|
||||
"SetSharedPropertyMyProperty");
|
||||
"SetSharedPropertyMySharedProperty");
|
||||
|
||||
// Check events-based behavior methods have *not* been renamed in
|
||||
// expressions
|
||||
@@ -2114,7 +2128,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
|
||||
"MyEventsExtension::MyRenamedEventsBasedBehavior::"
|
||||
"SetSharedPropertyMyProperty");
|
||||
"SetSharedPropertyMySharedProperty");
|
||||
|
||||
// Check events-based behavior methods have *not* been renamed in
|
||||
// expressions
|
||||
@@ -2729,16 +2743,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SetSharedPropertyMyProperty");
|
||||
"SetSharedPropertyMySharedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstConditionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyCondition)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SharedPropertyMyProperty");
|
||||
"SharedPropertyMySharedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyExpression)) ==
|
||||
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMyProperty()");
|
||||
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMySharedProperty()");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2750,6 +2764,11 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
auto &eventsBasedBehavior =
|
||||
eventsExtension.GetEventsBasedBehaviors().Get("MyEventsBasedBehavior");
|
||||
|
||||
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
|
||||
project, eventsExtension, eventsBasedBehavior, "MySharedProperty",
|
||||
"MyRenamedSharedProperty");
|
||||
|
||||
// Also wrongly try to rename a property that is not a shared property.
|
||||
gd::WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
|
||||
project, eventsExtension, eventsBasedBehavior, "MyProperty",
|
||||
"MyRenamedProperty");
|
||||
@@ -2760,16 +2779,16 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyAction)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SetSharedPropertyMyRenamedProperty");
|
||||
"SetSharedPropertyMyRenamedSharedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstConditionType(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyCondition)) ==
|
||||
"MyEventsExtension::MyEventsBasedBehavior::"
|
||||
"SharedPropertyMyRenamedProperty");
|
||||
"SharedPropertyMyRenamedSharedProperty");
|
||||
|
||||
REQUIRE(GetEventFirstActionFirstParameterString(
|
||||
eventsList->GetEvent(BehaviorSharedPropertyExpression)) ==
|
||||
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMyRenamedProperty()");
|
||||
"ObjectWithMyBehavior.MyBehavior::SharedPropertyMyRenamedSharedProperty()");
|
||||
|
||||
// Ensure that the property was NOT renamed.
|
||||
REQUIRE(GetEventFirstActionType(
|
||||
@@ -2951,7 +2970,7 @@ TEST_CASE("WholeProjectRefactorer (FixInvalidRequiredBehaviorProperties)",
|
||||
// - add the behavior "A" to an object
|
||||
// Check that no behavior is added on the object for it and that there is no
|
||||
// crash.
|
||||
|
||||
|
||||
SECTION("Fix nothing if there are no missing required behavior") {
|
||||
gd::Project project;
|
||||
gd::Platform platform;
|
||||
@@ -3732,7 +3751,7 @@ TEST_CASE("RemoveLayer", "[common]") {
|
||||
|
||||
layout.InsertNewLayer("My layer", 0);
|
||||
otherLayout.InsertNewLayer("My layer", 0);
|
||||
|
||||
|
||||
auto &externalLayout =
|
||||
project.InsertNewExternalLayout("My external layout", 0);
|
||||
auto &otherExternalLayout =
|
||||
@@ -3746,7 +3765,7 @@ TEST_CASE("RemoveLayer", "[common]") {
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("My layer");
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("");
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("");
|
||||
|
||||
|
||||
auto &externalInitialInstances = externalLayout.GetInitialInstances();
|
||||
externalInitialInstances.InsertNewInitialInstance().SetLayer("My layer");
|
||||
externalInitialInstances.InsertNewInitialInstance().SetLayer("My layer");
|
||||
@@ -3789,7 +3808,7 @@ TEST_CASE("MergeLayers", "[common]") {
|
||||
|
||||
layout.InsertNewLayer("My layer", 0);
|
||||
otherLayout.InsertNewLayer("My layer", 0);
|
||||
|
||||
|
||||
auto &externalLayout =
|
||||
project.InsertNewExternalLayout("My external layout", 0);
|
||||
auto &otherExternalLayout =
|
||||
@@ -3804,7 +3823,7 @@ TEST_CASE("MergeLayers", "[common]") {
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("");
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("");
|
||||
initialInstances.InsertNewInitialInstance().SetLayer("My other layer");
|
||||
|
||||
|
||||
auto &externalInitialInstances = externalLayout.GetInitialInstances();
|
||||
externalInitialInstances.InsertNewInitialInstance().SetLayer("My layer");
|
||||
externalInitialInstances.InsertNewInitialInstance().SetLayer("My layer");
|
||||
|
@@ -144,6 +144,40 @@ namespace gdjs {
|
||||
return this._z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Z position of the rendered object.
|
||||
*
|
||||
* For most objects, this will returns the same value as getZ(). But if the
|
||||
* object has an origin that is not the same as the point (0,0,0) of the
|
||||
* object displayed, getDrawableZ will differ.
|
||||
*
|
||||
* @return The Z position of the rendered object.
|
||||
*/
|
||||
getDrawableZ(): float {
|
||||
return this.getZ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Z position of the object center, **relative to the object Z
|
||||
* position** (`getDrawableX`).
|
||||
*
|
||||
* Use `getCenterZInScene` to get the position of the center in the scene.
|
||||
*
|
||||
* @return the Z position of the object center, relative to
|
||||
* `getDrawableZ()`.
|
||||
*/
|
||||
getCenterZ(): float {
|
||||
return this.getDepth() / 2;
|
||||
}
|
||||
|
||||
getCenterZInScene(): float {
|
||||
return this.getDrawableZ() + this.getCenterZ();
|
||||
}
|
||||
|
||||
setCenterZInScene(z: float): void {
|
||||
this.setZ(z + this._z - (this.getDrawableZ() + this.getCenterZ()));
|
||||
}
|
||||
|
||||
setAngle(angle: float): void {
|
||||
super.setAngle(angle);
|
||||
this.getRenderer().updateRotation();
|
||||
|
@@ -15,6 +15,17 @@ namespace gdjs {
|
||||
*/
|
||||
getZ(): float;
|
||||
|
||||
/**
|
||||
* Return the Z position of the object center, **relative to the scene origin**.
|
||||
*/
|
||||
getCenterZInScene(): float;
|
||||
|
||||
/**
|
||||
* Change the object center Z position in the scene.
|
||||
* @param z The new Z position of the center in the scene.
|
||||
*/
|
||||
setCenterZInScene(z: float): void;
|
||||
|
||||
/**
|
||||
* Set the object rotation on the X axis.
|
||||
*
|
||||
@@ -132,6 +143,14 @@ namespace gdjs {
|
||||
return this.object.getZ();
|
||||
}
|
||||
|
||||
getCenterZInScene(): number {
|
||||
return this.object.getCenterZInScene();
|
||||
}
|
||||
|
||||
setCenterZInScene(z: number): void {
|
||||
this.object.setCenterZInScene(z);
|
||||
}
|
||||
|
||||
setRotationX(angle: float): void {
|
||||
this.object.setRotationX(angle);
|
||||
}
|
||||
|
@@ -67,6 +67,22 @@ module.exports = {
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setZ')
|
||||
.setGetter('getZ');
|
||||
|
||||
base3D
|
||||
.addExpressionAndConditionAndAction(
|
||||
'number',
|
||||
'CenterZ',
|
||||
_('Center Z position'),
|
||||
_('the Z position of the center of rotation'),
|
||||
_('the Z position of the center'),
|
||||
_('Position/Center'),
|
||||
'res/conditions/3d_box.svg'
|
||||
)
|
||||
.addParameter('object', _('3D object'))
|
||||
.addParameter('behavior', _('Behavior'), 'Base3DBehavior')
|
||||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||||
.setFunctionName('setCenterZInScene')
|
||||
.setGetter('getCenterZInScene');
|
||||
|
||||
base3D
|
||||
.addExpressionAndConditionAndAction(
|
||||
|
@@ -298,6 +298,11 @@ namespace gdjs {
|
||||
return this.getHeight() * centerPoint[1];
|
||||
}
|
||||
|
||||
getCenterZ(): float {
|
||||
const centerPoint = this._renderer.getCenterPoint();
|
||||
return this.getDepth() * centerPoint[2];
|
||||
}
|
||||
|
||||
getDrawableX(): float {
|
||||
const originPoint = this._renderer.getOriginPoint();
|
||||
return this.getX() - this.getWidth() * originPoint[0];
|
||||
@@ -307,6 +312,11 @@ namespace gdjs {
|
||||
const originPoint = this._renderer.getOriginPoint();
|
||||
return this.getY() - this.getHeight() * originPoint[1];
|
||||
}
|
||||
|
||||
getDrawableZ(): float {
|
||||
const originPoint = this._renderer.getOriginPoint();
|
||||
return this.getZ() - this.getDepth() * originPoint[2];
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Model3DRuntimeObject {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user