mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
314 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
13cf9b1d0b | ||
![]() |
6fc0198298 | ||
![]() |
0999ba611d | ||
![]() |
119d1af76a | ||
![]() |
d6b4dacb1e | ||
![]() |
9edb3cfe91 | ||
![]() |
d44a1c3537 | ||
![]() |
8cc84e5728 | ||
![]() |
76130b43d4 | ||
![]() |
40b0823b91 | ||
![]() |
9b447e08e2 | ||
![]() |
10314a1911 | ||
![]() |
d0195719c2 | ||
![]() |
dd8c040381 | ||
![]() |
5e0c8a92aa | ||
![]() |
977c102ddc | ||
![]() |
849d79d5d7 | ||
![]() |
28f7c9ae0b | ||
![]() |
38e58327fa | ||
![]() |
3cc29efaa2 | ||
![]() |
65eb76fb57 | ||
![]() |
9eb721662f | ||
![]() |
b10b131010 | ||
![]() |
b5fd1bb351 | ||
![]() |
f1c9521625 | ||
![]() |
6ceb3c2c10 | ||
![]() |
43827876cd | ||
![]() |
62b746300a | ||
![]() |
769ebcd91c | ||
![]() |
70d5de16bf | ||
![]() |
7fbe1bd23d | ||
![]() |
de8a679e31 | ||
![]() |
a1a4029b35 | ||
![]() |
a377467031 | ||
![]() |
dd090fd1d7 | ||
![]() |
26ee9b3891 | ||
![]() |
ad18eab4ae | ||
![]() |
007fc36a2e | ||
![]() |
c8ebfde85b | ||
![]() |
d0005ba2cb | ||
![]() |
f623b352ee | ||
![]() |
16e2d8a005 | ||
![]() |
7654883cb1 | ||
![]() |
3ae5db2a49 | ||
![]() |
9ed002c879 | ||
![]() |
d4283c2350 | ||
![]() |
5a176d21e7 | ||
![]() |
bfdfd7f0fb | ||
![]() |
aae75f2232 | ||
![]() |
977092c0a3 | ||
![]() |
556688cedb | ||
![]() |
ce93dc5310 | ||
![]() |
d6d425db4f | ||
![]() |
2737e75639 | ||
![]() |
36eab18133 | ||
![]() |
48acbb12ee | ||
![]() |
21904e46f1 | ||
![]() |
94753ac053 | ||
![]() |
b160ee9b27 | ||
![]() |
f76e8a72b6 | ||
![]() |
5bc342688d | ||
![]() |
3e6204c0eb | ||
![]() |
7b1c340ad0 | ||
![]() |
fac724dc3f | ||
![]() |
9b4151f64c | ||
![]() |
6b5ab6c811 | ||
![]() |
5943092b0c | ||
![]() |
deb0c5ffc3 | ||
![]() |
c56fa03bf6 | ||
![]() |
0d49d449db | ||
![]() |
fdd702cd09 | ||
![]() |
5a8e4a7ca9 | ||
![]() |
2e4e91c21e | ||
![]() |
6ece930809 | ||
![]() |
c5baa81977 | ||
![]() |
d24be38874 | ||
![]() |
1efffbbb78 | ||
![]() |
090d76a368 | ||
![]() |
d44a9375de | ||
![]() |
5a2a3893f9 | ||
![]() |
0a6b0dc785 | ||
![]() |
02e99726dc | ||
![]() |
843055d8df | ||
![]() |
f7fda5cb5e | ||
![]() |
e529642aec | ||
![]() |
c8e10d7043 | ||
![]() |
5f0de0e9a7 | ||
![]() |
e51638ce4b | ||
![]() |
0fbd6a606a | ||
![]() |
2fa543c3db | ||
![]() |
38e35c9695 | ||
![]() |
ad13a1a101 | ||
![]() |
cb9d98d027 | ||
![]() |
064c3f1572 | ||
![]() |
9e5320f9d4 | ||
![]() |
a1826d355d | ||
![]() |
32a3a094d1 | ||
![]() |
ee7dc2654b | ||
![]() |
64ffad3c0a | ||
![]() |
dcc62f078f | ||
![]() |
d920f05dbc | ||
![]() |
ac82be800b | ||
![]() |
2c92ae4042 | ||
![]() |
465e934605 | ||
![]() |
248ba7675e | ||
![]() |
fd2b59ba45 | ||
![]() |
be54236ece | ||
![]() |
6edf63e98f | ||
![]() |
034f1ad9cc | ||
![]() |
94b8c31ac2 | ||
![]() |
57d1241e2d | ||
![]() |
eb4708ca87 | ||
![]() |
fac710780b | ||
![]() |
d28aac325a | ||
![]() |
306b341ee5 | ||
![]() |
1d8e04cb78 | ||
![]() |
3b2855de59 | ||
![]() |
d5c2982b2d | ||
![]() |
315b7387b3 | ||
![]() |
f4f92566f4 | ||
![]() |
7b72d4e080 | ||
![]() |
ae96ebf79c | ||
![]() |
be813a0271 | ||
![]() |
b306d80915 | ||
![]() |
9baed02aa1 | ||
![]() |
1b4c5c1b1c | ||
![]() |
4f04190614 | ||
![]() |
d3134ecde9 | ||
![]() |
61ed7ffa16 | ||
![]() |
8be1961d3f | ||
![]() |
112c306610 | ||
![]() |
423f15b513 | ||
![]() |
a9c89b14c3 | ||
![]() |
bd898463f5 | ||
![]() |
ed4635664c | ||
![]() |
b6f25db40c | ||
![]() |
f78662be5f | ||
![]() |
7aae35a029 | ||
![]() |
6c323614ef | ||
![]() |
eb4170df20 | ||
![]() |
8830bb93ae | ||
![]() |
87fa0a39ac | ||
![]() |
a9cc911ca8 | ||
![]() |
d3fe6cf532 | ||
![]() |
e2c40ff205 | ||
![]() |
970d04b0df | ||
![]() |
8c5076443c | ||
![]() |
3744e98065 | ||
![]() |
b6a1332124 | ||
![]() |
0251997703 | ||
![]() |
57faa9fb4a | ||
![]() |
ba95f66ccd | ||
![]() |
9465873dbd | ||
![]() |
b5a9fe4fe1 | ||
![]() |
6531e2e970 | ||
![]() |
4f900c9451 | ||
![]() |
f97f267a96 | ||
![]() |
6cd8f54869 | ||
![]() |
9718fb788e | ||
![]() |
38651edf3e | ||
![]() |
d34f1a654f | ||
![]() |
5abc74b66b | ||
![]() |
a848764318 | ||
![]() |
c0c6fddcbb | ||
![]() |
95ac26f05d | ||
![]() |
1f852648ef | ||
![]() |
b7da4361c3 | ||
![]() |
71b369d40e | ||
![]() |
4d8cf56922 | ||
![]() |
1a6e0ba5a1 | ||
![]() |
ec1ebcbf5b | ||
![]() |
4ee9ccd7a9 | ||
![]() |
1ac248bfa4 | ||
![]() |
65b78d4db7 | ||
![]() |
639d90d743 | ||
![]() |
45d0a78656 | ||
![]() |
0a0811e355 | ||
![]() |
a8f9df3dac | ||
![]() |
35db56d778 | ||
![]() |
27efe8e3dd | ||
![]() |
3584ee2aaf | ||
![]() |
19a762fb60 | ||
![]() |
8a6dd8b940 | ||
![]() |
2173b49b19 | ||
![]() |
caf2752acb | ||
![]() |
e3291b515d | ||
![]() |
c1627b5ab2 | ||
![]() |
7db5b97c45 | ||
![]() |
425a0e92ca | ||
![]() |
c2dfe579af | ||
![]() |
4ea6fb78bd | ||
![]() |
8e668db6ea | ||
![]() |
88063a4cad | ||
![]() |
b180f5032e | ||
![]() |
29e7f7d7a4 | ||
![]() |
547f1e4bce | ||
![]() |
9e176d91ec | ||
![]() |
cac59d4727 | ||
![]() |
4f98ffa9ab | ||
![]() |
7229406cfb | ||
![]() |
d39cfd16a2 | ||
![]() |
2965f374bb | ||
![]() |
2d5b2a49e7 | ||
![]() |
05df9cb599 | ||
![]() |
7b28d2525e | ||
![]() |
533e61cdbc | ||
![]() |
3a823113f1 | ||
![]() |
17bc8b584d | ||
![]() |
dbaf8a6569 | ||
![]() |
fcbc538c30 | ||
![]() |
decd96c52f | ||
![]() |
8dbf9c99ce | ||
![]() |
1318523804 | ||
![]() |
8ef5e2c1ed | ||
![]() |
d945426068 | ||
![]() |
8eb07a492f | ||
![]() |
37c9dd95ce | ||
![]() |
a6d837cbe6 | ||
![]() |
f6539626d3 | ||
![]() |
e0df9b0ff3 | ||
![]() |
0b50231027 | ||
![]() |
94c9924a88 | ||
![]() |
581cc5837a | ||
![]() |
ff77d107b2 | ||
![]() |
3a5198cb34 | ||
![]() |
c92025549d | ||
![]() |
600b307e7e | ||
![]() |
297fade0bd | ||
![]() |
84d7500eab | ||
![]() |
f99c4ab948 | ||
![]() |
5fe4f23c83 | ||
![]() |
8820bd45e4 | ||
![]() |
5e16968f37 | ||
![]() |
6b6179ff22 | ||
![]() |
94bcd87a9f | ||
![]() |
356a1974ef | ||
![]() |
345bad9876 | ||
![]() |
0b8d843a73 | ||
![]() |
9f0c987ec7 | ||
![]() |
0e79209d2b | ||
![]() |
f991c09c39 | ||
![]() |
6e24dfa9b8 | ||
![]() |
a3cd00dc94 | ||
![]() |
300c011151 | ||
![]() |
230d410469 | ||
![]() |
402e54f706 | ||
![]() |
dc29c27272 | ||
![]() |
462bb84c51 | ||
![]() |
a775e79bae | ||
![]() |
bc8c867f23 | ||
![]() |
f34bf2b7b4 | ||
![]() |
e2d482e1aa | ||
![]() |
0724ae34d9 | ||
![]() |
2fc1c2fd86 | ||
![]() |
9a42ae11ad | ||
![]() |
32773e06f6 | ||
![]() |
00508df014 | ||
![]() |
c3cf5b7002 | ||
![]() |
df7fab84b8 | ||
![]() |
95a0012c5e | ||
![]() |
4bf644f9f2 | ||
![]() |
79cad1da3a | ||
![]() |
ffc6f5786b | ||
![]() |
07df65f8c2 | ||
![]() |
61f8f0ed66 | ||
![]() |
c24e2e5ace | ||
![]() |
25c72a6d1f | ||
![]() |
6153b23e7d | ||
![]() |
1b7ccfded3 | ||
![]() |
ec57a0a80d | ||
![]() |
95a9e37aba | ||
![]() |
787274f7fa | ||
![]() |
ceb24f0edb | ||
![]() |
028bf7a70d | ||
![]() |
08471d0356 | ||
![]() |
921add6665 | ||
![]() |
dabff06891 | ||
![]() |
967033c407 | ||
![]() |
0809749ddc | ||
![]() |
8a27801355 | ||
![]() |
20cba9d7ed | ||
![]() |
3878b93c94 | ||
![]() |
645d5331cb | ||
![]() |
91db750632 | ||
![]() |
740485b54a | ||
![]() |
e960fe7c5b | ||
![]() |
0633c7b474 | ||
![]() |
704bfd40cb | ||
![]() |
310abe7a13 | ||
![]() |
b0f9bed273 | ||
![]() |
766a62ad9b | ||
![]() |
ec6f669d94 | ||
![]() |
36a2408be0 | ||
![]() |
1343210a2b | ||
![]() |
1e76fedd7b | ||
![]() |
3923641988 | ||
![]() |
a7d488626c | ||
![]() |
33f20b9bde | ||
![]() |
6c4cd6343f | ||
![]() |
f1c04df0ee | ||
![]() |
9b466ff574 | ||
![]() |
2b0969600e | ||
![]() |
7ed1eee8f9 | ||
![]() |
9b9b729b42 | ||
![]() |
530969d4b0 | ||
![]() |
11ef87b9ee | ||
![]() |
e1857e2e1e | ||
![]() |
d1385e5fc2 | ||
![]() |
9298026179 | ||
![]() |
913e367f6f | ||
![]() |
1bc27bdd8b | ||
![]() |
4d8b39df95 | ||
![]() |
e6970319b9 | ||
![]() |
cdf21ebd4c |
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
- run:
|
||||
name: Install system dependencies for Electron builder
|
||||
@@ -127,7 +127,8 @@ jobs:
|
||||
# Build GDevelop.js (and run tests to ensure it works)
|
||||
- run:
|
||||
name: Build GDevelop.js
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
|
||||
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
|
||||
|
||||
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
|
||||
- run:
|
||||
@@ -184,7 +185,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
@@ -200,7 +201,8 @@ jobs:
|
||||
# Build GDevelop.js (and run tests to ensure it works)
|
||||
- run:
|
||||
name: Build GDevelop.js
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
|
||||
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
|
37
.github/ISSUE_TEMPLATE/--automatic-crash.yml
vendored
Normal file
37
.github/ISSUE_TEMPLATE/--automatic-crash.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: 💥 Automatic crash report
|
||||
description: Do not use this template for bug reports. This template is only for automatic crash reports.
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe what you were doing when the crash happened
|
||||
description: If applicable, add screenshots to help explain your problem.
|
||||
placeholder: |
|
||||
1. Went to '...'
|
||||
2. Clicked on '...'
|
||||
3. Scrolled down to '...'
|
||||
4. Saw error
|
||||
- type: input
|
||||
id: gdevelop_version
|
||||
attributes:
|
||||
label: GDevelop version
|
||||
description: |
|
||||
The version of GDevelop used. Leave the prefilled value.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: platform_info
|
||||
attributes:
|
||||
label: Platform info
|
||||
description: |
|
||||
The platform you are using GDevelop on. Leave the prefilled value.
|
||||
- type: textarea
|
||||
id: error_stack
|
||||
attributes:
|
||||
label: Additional error context
|
||||
description: Additonal context about the problem. Leave the prefilled value.
|
||||
- type: textarea
|
||||
id: component_stack
|
||||
attributes:
|
||||
label: Additional component context
|
||||
description: Additonal context about the problem. Leave the prefilled value.
|
11
.github/ISSUE_TEMPLATE/--bug-report.yml
vendored
11
.github/ISSUE_TEMPLATE/--bug-report.yml
vendored
@@ -2,18 +2,21 @@ name: 🐛Bug report
|
||||
description: Create a bug report about GDevelop or the game engine
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: searched_issues
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
options:
|
||||
- label: I have searched the [existing issues](https://github.com/4ian/GDevelop/issues)
|
||||
required: true
|
||||
- label: I have searched the [existing issues](https://github.com/4ian/GDevelop/issues)
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction_steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: |
|
||||
@@ -27,6 +30,7 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: GDevelop platform
|
||||
description: Which platform of GDevelop are you using?
|
||||
@@ -38,6 +42,7 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: gdevelop_version
|
||||
attributes:
|
||||
label: GDevelop version
|
||||
description: |
|
||||
@@ -47,6 +52,7 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: platform_info
|
||||
attributes:
|
||||
label: Platform info
|
||||
value: |
|
||||
@@ -66,6 +72,7 @@ body:
|
||||
|
||||
</details>
|
||||
- type: textarea
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the problem here.
|
||||
|
@@ -14,7 +14,7 @@ tasks:
|
||||
init: |
|
||||
sudo apt-get update
|
||||
sudo apt install cmake python-is-python3 python3-distutils -y
|
||||
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
cd GDevelop.js
|
||||
npm install
|
||||
source ../emsdk/emsdk_env.sh && npm run build -- --dev
|
||||
|
@@ -39,7 +39,7 @@ install:
|
||||
- cd ..
|
||||
# Install Emscripten (for GDevelop.js)
|
||||
- git clone https://github.com/juj/emsdk.git
|
||||
- cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
- cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
# Install GDevelop.js dependencies
|
||||
- cd GDevelop.js && npm install && cd ..
|
||||
# Build GDevelop.js
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -113,7 +113,8 @@
|
||||
"memory_resource": "cpp",
|
||||
"__bits": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"variant": "cpp"
|
||||
"variant": "cpp",
|
||||
"charconv": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -164,7 +164,7 @@ void LinkEvent::UnserializeFrom(gd::Project& project,
|
||||
}
|
||||
|
||||
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
|
||||
return BaseEvent::AcceptVisitor(eventVisitor) |
|
||||
return BaseEvent::AcceptVisitor(eventVisitor) ||
|
||||
eventVisitor.VisitLinkEvent(*this);
|
||||
}
|
||||
|
||||
|
@@ -46,10 +46,6 @@ void EffectsCodeGenerator::GenerateEffectsIncludeFiles(
|
||||
// TODO Add unit tests on this function.
|
||||
|
||||
// TODO Merge with UsedExtensionsFinder.
|
||||
// Default lights rely on the fact that UsedExtensionsFinder doesn't find
|
||||
// extension usages for effects. This has the happy side effect of not
|
||||
// including Three.js when no 3D object are in the scenes.
|
||||
// We need to make something explicit to avoid future bugs.
|
||||
|
||||
// See also gd::Project::ExposeResources for a method that traverse the whole
|
||||
// project (this time for resources) and
|
||||
|
@@ -69,8 +69,36 @@ gd::String EventsCodeGenerator::GenerateRelationalOperatorCall(
|
||||
}
|
||||
}
|
||||
|
||||
return callStartString + "(" + argumentsStr + ") " + relationalOperator +
|
||||
" " + rhs;
|
||||
auto lhs = callStartString + "(" + argumentsStr + ")";
|
||||
return GenerateRelationalOperation(relationalOperator, lhs, rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a relational operation
|
||||
*
|
||||
* @param relationalOperator the operator
|
||||
* @param lhs the left hand operand
|
||||
* @param rhs the right hand operand
|
||||
* @return gd::String
|
||||
*/
|
||||
gd::String EventsCodeGenerator::GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs) {
|
||||
return lhs + " " + GenerateRelationalOperatorCodes(relationalOperator) + " " + rhs;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=" &&
|
||||
operatorString != "startsWith" && operatorString != "endsWith" && operatorString != "contains") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,18 +666,6 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
const gd::String EventsCodeGenerator::GenerateRelationalOperatorCodes(const gd::String &operatorString) {
|
||||
if (operatorString == "=") {
|
||||
return "==";
|
||||
}
|
||||
if (operatorString != "<" && operatorString != ">" &&
|
||||
operatorString != "<=" && operatorString != ">=" && operatorString != "!=") {
|
||||
cout << "Warning: Bad relational operator: Set to == by default." << endl;
|
||||
return "==";
|
||||
}
|
||||
return operatorString;
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
const gd::Expression& parameter,
|
||||
const gd::ParameterMetadata& metadata,
|
||||
@@ -674,7 +690,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
argOutput =
|
||||
GenerateObject(parameter.GetPlainString(), metadata.GetType(), context);
|
||||
} else if (metadata.GetType() == "relationalOperator") {
|
||||
argOutput += GenerateRelationalOperatorCodes(parameter.GetPlainString());
|
||||
argOutput += parameter.GetPlainString();
|
||||
argOutput = "\"" + argOutput + "\"";
|
||||
} else if (metadata.GetType() == "operator") {
|
||||
argOutput += parameter.GetPlainString();
|
||||
@@ -698,6 +714,8 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
|
||||
metadata.GetType() == "tilesetResource" ||
|
||||
metadata.GetType() == "videoResource" ||
|
||||
metadata.GetType() == "model3DResource" ||
|
||||
metadata.GetType() == "atlasResource" ||
|
||||
metadata.GetType() == "spineResource" ||
|
||||
// Deprecated, old parameter names:
|
||||
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
|
||||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
|
||||
@@ -1212,13 +1230,13 @@ gd::String EventsCodeGenerator::GeneratePropertyGetter(const gd::PropertiesConta
|
||||
const gd::NamedPropertyDescriptor& property,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getProperty" + property.GetName() + "()";
|
||||
return "getProperty" + property.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
gd::String EventsCodeGenerator::GenerateParameterGetter(const gd::ParameterMetadata& parameter,
|
||||
const gd::String& type,
|
||||
gd::EventsCodeGenerationContext& context) {
|
||||
return "getParameter" + parameter.GetName() + "()";
|
||||
return "getParameter" + parameter.GetName() + "As" + type + "()";
|
||||
}
|
||||
|
||||
EventsCodeGenerator::EventsCodeGenerator(const gd::Project& project_,
|
||||
|
@@ -12,8 +12,8 @@
|
||||
|
||||
#include "GDCore/Events/Event.h"
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class EventsList;
|
||||
class Expression;
|
||||
@@ -58,8 +58,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* \brief Construct a code generator for the specified
|
||||
* objects/groups and platform
|
||||
*/
|
||||
EventsCodeGenerator(const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
EventsCodeGenerator(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_);
|
||||
virtual ~EventsCodeGenerator(){};
|
||||
|
||||
/**
|
||||
@@ -472,10 +473,15 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
*/
|
||||
size_t GenerateSingleUsageUniqueIdForEventsList();
|
||||
|
||||
virtual gd::String GenerateRelationalOperation(
|
||||
const gd::String& relationalOperator,
|
||||
const gd::String& lhs,
|
||||
const gd::String& rhs);
|
||||
|
||||
protected:
|
||||
virtual const gd::String GenerateRelationalOperatorCodes(
|
||||
const gd::String& operatorString);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Generate the code for a single parameter.
|
||||
*
|
||||
@@ -546,7 +552,9 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
};
|
||||
|
||||
virtual gd::String GenerateVariableValueAs(const gd::String& type) {
|
||||
return type == "string" ? ".getAsString()" : ".getAsNumber()";
|
||||
return type == "number|string" ? ".getAsNumberOrString()"
|
||||
: type == "string" ? ".getAsString()"
|
||||
: ".getAsNumber()";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -577,14 +585,16 @@ 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 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);
|
||||
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
|
||||
@@ -665,7 +675,8 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
* The default implementation generates C-style code : It wraps the predicate
|
||||
* inside parenthesis and add a !.
|
||||
*/
|
||||
virtual gd::String GenerateNegatedPredicate(const gd::String& predicate) const {
|
||||
virtual gd::String GenerateNegatedPredicate(
|
||||
const gd::String& predicate) const {
|
||||
return "!(" + predicate + ")";
|
||||
};
|
||||
|
||||
@@ -726,6 +737,7 @@ class GD_CORE_API EventsCodeGenerator {
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
std::size_t startFromArgument = 0);
|
||||
|
||||
gd::String GenerateOperatorCall(const gd::InstructionMetadata& instrInfos,
|
||||
const std::vector<gd::String>& arguments,
|
||||
const gd::String& callStartString,
|
||||
|
@@ -103,7 +103,7 @@ 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.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -191,7 +191,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
return;
|
||||
}
|
||||
|
||||
ExpressionCodeGenerator generator("string", "", codeGenerator, context);
|
||||
ExpressionCodeGenerator generator("number|string", "", codeGenerator, context);
|
||||
node.expression->Visit(generator);
|
||||
output +=
|
||||
codeGenerator.GenerateVariableBracketAccessor(generator.GetOutput());
|
||||
@@ -200,7 +200,7 @@ void ExpressionCodeGenerator::OnVisitVariableBracketAccessorNode(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -271,7 +271,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitFunctionCallNode(FunctionCallNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
|
||||
@@ -502,7 +502,7 @@ gd::String ExpressionCodeGenerator::GenerateDefaultValue(
|
||||
|
||||
void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
@@ -511,7 +511,7 @@ void ExpressionCodeGenerator::OnVisitEmptyNode(EmptyNode& node) {
|
||||
void ExpressionCodeGenerator::OnVisitObjectFunctionNameNode(
|
||||
ObjectFunctionNameNode& node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(codeGenerator.GetPlatform(),
|
||||
codeGenerator.GetObjectsContainersList(),
|
||||
codeGenerator.GetProjectScopedContainers(),
|
||||
rootType,
|
||||
node);
|
||||
output += GenerateDefaultValue(type);
|
||||
|
@@ -277,8 +277,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
|
||||
std::unique_ptr<VariableNode> Variable(const gd::String &name, gd::ExpressionParserLocation nameLocation) {
|
||||
auto variable = gd::make_unique<VariableNode>(name);
|
||||
variable->child = VariableAccessorOrVariableBracketAccessor();
|
||||
variable->child->parent = variable.get();
|
||||
|
||||
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
|
||||
variable->child = VariableAccessorOrVariableBracketAccessor();
|
||||
variable->child->parent = variable.get();
|
||||
}
|
||||
|
||||
variable->location = ExpressionParserLocation(
|
||||
nameLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -302,8 +305,12 @@ class GD_CORE_API ExpressionParser2 {
|
||||
"bracket for each opening bracket."));
|
||||
}
|
||||
SkipIfChar(IsClosingSquareBracket);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
|
||||
SkipAllWhitespaces();
|
||||
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
}
|
||||
child->location =
|
||||
ExpressionParserLocation(childStartPosition, GetCurrentPosition());
|
||||
|
||||
@@ -315,8 +322,15 @@ class GD_CORE_API ExpressionParser2 {
|
||||
auto identifierAndLocation = ReadIdentifierName(/*allowDeprecatedSpacesInName=*/ false);
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(identifierAndLocation.name);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
if (identifierAndLocation.name.empty()) {
|
||||
child->diagnostic = RaiseSyntaxError(_("A name should be entered after the dot."));
|
||||
}
|
||||
|
||||
SkipAllWhitespaces();
|
||||
if (CheckIfChar(IsOpeningSquareBracket) || CheckIfChar(IsDot)) {
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
child->child->parent = child.get();
|
||||
}
|
||||
child->nameLocation = identifierAndLocation.location;
|
||||
child->dotLocation = dotLocation;
|
||||
child->location =
|
||||
@@ -325,7 +339,11 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(child);
|
||||
}
|
||||
|
||||
return std::move(gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>());
|
||||
// Should never happen, unless a node called this function without checking if the current character
|
||||
// was a dot or an opening bracket - this means there is an error in the grammar.
|
||||
auto unrecognisedNode = gd::make_unique<VariableAccessorOrVariableBracketAccessorNode>();
|
||||
unrecognisedNode->diagnostic = RaiseSyntaxError(_("A dot or bracket was expected here."));
|
||||
return std::move(unrecognisedNode);
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctionCallNode> FreeFunction(
|
||||
@@ -361,18 +379,24 @@ class GD_CORE_API ExpressionParser2 {
|
||||
const auto &childIdentifierNameLocation =
|
||||
childIdentifierAndLocation.location;
|
||||
|
||||
std::unique_ptr<gd::ExpressionParserError> emptyNameError = childIdentifierName.empty() ?
|
||||
RaiseSyntaxError(_("A name should be entered after the dot.")) : nullptr;
|
||||
|
||||
SkipAllWhitespaces();
|
||||
|
||||
if (IsNamespaceSeparator()) {
|
||||
ExpressionParserLocation namespaceSeparatorLocation =
|
||||
SkipNamespaceSeparator();
|
||||
SkipAllWhitespaces();
|
||||
return BehaviorFunction(parentIdentifier,
|
||||
auto behaviorFunction = BehaviorFunction(parentIdentifier,
|
||||
childIdentifierName,
|
||||
parentIdentifierLocation,
|
||||
parentIdentifierDotLocation,
|
||||
childIdentifierNameLocation,
|
||||
namespaceSeparatorLocation);
|
||||
|
||||
if (emptyNameError) behaviorFunction->diagnostic = std::move(emptyNameError);
|
||||
return std::move(behaviorFunction);
|
||||
} else if (CheckIfChar(IsOpeningParenthesis)) {
|
||||
ExpressionParserLocation openingParenthesisLocation = SkipChar();
|
||||
|
||||
@@ -381,7 +405,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
childIdentifierName);
|
||||
auto parametersNode = Parameters(function.get(), parentIdentifier);
|
||||
function->parameters = std::move(parametersNode.parameters),
|
||||
function->diagnostic = std::move(parametersNode.diagnostic);
|
||||
function->diagnostic = emptyNameError ? std::move(emptyNameError) : std::move(parametersNode.diagnostic);
|
||||
|
||||
function->location = ExpressionParserLocation(
|
||||
parentIdentifierLocation.GetStartPosition(), GetCurrentPosition());
|
||||
@@ -394,6 +418,8 @@ class GD_CORE_API ExpressionParser2 {
|
||||
return std::move(function);
|
||||
} else if (CheckIfChar(IsDot) || CheckIfChar(IsOpeningSquareBracket)) {
|
||||
auto variable = gd::make_unique<VariableNode>(parentIdentifier);
|
||||
variable->diagnostic = std::move(emptyNameError);
|
||||
|
||||
auto child =
|
||||
gd::make_unique<VariableAccessorNode>(childIdentifierName);
|
||||
child->child = VariableAccessorOrVariableBracketAccessor();
|
||||
@@ -419,6 +445,7 @@ class GD_CORE_API ExpressionParser2 {
|
||||
node->identifierNameLocation = parentIdentifierLocation;
|
||||
node->identifierNameDotLocation = parentIdentifierDotLocation;
|
||||
node->childIdentifierNameLocation = childIdentifierNameLocation;
|
||||
node->diagnostic = std::move(emptyNameError);
|
||||
return std::move(node);
|
||||
}
|
||||
|
||||
@@ -491,11 +518,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
gd::String lastObjectName = "";
|
||||
|
||||
// By convention, object is always the first parameter, and behavior the
|
||||
// second one.
|
||||
size_t parameterIndex =
|
||||
WrittenParametersFirstIndex(objectName, behaviorName);
|
||||
|
||||
bool previousCharacterIsParameterSeparator = false;
|
||||
while (!IsEndReached()) {
|
||||
SkipAllWhitespaces();
|
||||
@@ -514,7 +536,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
SkipAllWhitespaces();
|
||||
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
|
||||
SkipIfChar(IsParameterSeparator);
|
||||
parameterIndex++;
|
||||
}
|
||||
|
||||
ExpressionParserLocation invalidClosingParenthesisLocation;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
namespace gd {
|
||||
@@ -43,6 +44,11 @@ class GD_CORE_API ExpressionParser2NodePrinter
|
||||
*/
|
||||
const gd::String& GetOutput() { return output; };
|
||||
|
||||
static gd::String PrintStringLiteral(const gd::String& str) {
|
||||
return "\"" +
|
||||
str.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") + "\"";
|
||||
}
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
output += "(";
|
||||
@@ -69,10 +75,7 @@ class GD_CORE_API ExpressionParser2NodePrinter
|
||||
}
|
||||
void OnVisitNumberNode(NumberNode& node) override { output += node.number; }
|
||||
void OnVisitTextNode(TextNode& node) override {
|
||||
output +=
|
||||
"\"" +
|
||||
node.text.FindAndReplace("\\", "\\\\").FindAndReplace("\"", "\\\"") +
|
||||
"\"";
|
||||
output += PrintStringLiteral(node.text);
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
output += node.name;
|
||||
@@ -97,8 +100,8 @@ class GD_CORE_API ExpressionParser2NodePrinter
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
if (!node.behaviorFunctionName.empty()) {
|
||||
output +=
|
||||
node.objectName + "." + node.objectFunctionOrBehaviorName + "::" + node.behaviorFunctionName;
|
||||
output += node.objectName + "." + node.objectFunctionOrBehaviorName +
|
||||
"::" + node.behaviorFunctionName;
|
||||
} else {
|
||||
output += node.objectName + "." + node.objectFunctionOrBehaviorName;
|
||||
}
|
||||
|
@@ -115,21 +115,23 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
|
||||
.AddExpression(
|
||||
"GetArgumentAsNumber",
|
||||
_("Get function parameter value"),
|
||||
_("Get function parameter (also called \"argument\") value."),
|
||||
_("Get function parameter (also called \"argument\") value. You don't need this most of the time as you can simply write the parameter name in an expression."),
|
||||
"",
|
||||
"res/function16.png")
|
||||
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
|
||||
.SetRelevantForFunctionEventsOnly();
|
||||
.SetRelevantForFunctionEventsOnly()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddStrExpression(
|
||||
"GetArgumentAsString",
|
||||
_("Get function parameter text"),
|
||||
_("Get function parameter (also called \"argument\") text."),
|
||||
_("Get function parameter (also called \"argument\") text. You don't need this most of the time as you can simply write the parameter name in an expression."),
|
||||
"",
|
||||
"res/function16.png")
|
||||
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
|
||||
.SetRelevantForFunctionEventsOnly();
|
||||
.SetRelevantForFunctionEventsOnly()
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
|
@@ -49,6 +49,7 @@ class GD_CORE_API BuiltinExtensionsImplementer {
|
||||
static void ImplementsAnimatableExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsEffectExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsOpacityExtension(gd::PlatformExtension& extension);
|
||||
static void ImplementsTextContainerExtension(gd::PlatformExtension& extension);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -396,7 +396,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Z order"),
|
||||
_("Modify the Z-order of an object"),
|
||||
_("the z-order"),
|
||||
_("Z order"),
|
||||
_("Layers and cameras"),
|
||||
"res/actions/planicon24.png",
|
||||
"res/actions/planicon.png")
|
||||
|
||||
@@ -550,7 +550,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Z-order"),
|
||||
_("Compare the Z-order of the specified object."),
|
||||
_("the Z-order"),
|
||||
_("Z-order"),
|
||||
_("Layer"),
|
||||
"res/conditions/planicon24.png",
|
||||
"res/conditions/planicon.png")
|
||||
|
||||
@@ -617,6 +617,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"number", ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("AngleOfDisplacement",
|
||||
_("Angle of movement (using forces)"),
|
||||
_("Compare the angle of movement of an object according to "
|
||||
@@ -626,7 +627,20 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Movement using forces"),
|
||||
"res/conditions/vitesse24.png",
|
||||
"res/conditions/vitesse.png")
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Tolerance, in degrees"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
obj.AddCondition("IsTotalForceAngleAround",
|
||||
_("Angle of movement (using forces)"),
|
||||
_("Compare the angle of movement of an object according to "
|
||||
"the forces applied on it."),
|
||||
_("Angle of movement of _PARAM0_ is _PARAM1_ ± _PARAM2_°"),
|
||||
_("Movement using forces"),
|
||||
"res/conditions/vitesse24.png",
|
||||
"res/conditions/vitesse.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Tolerance, in degrees"))
|
||||
@@ -1137,7 +1151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
obj.AddExpression("ZOrder",
|
||||
_("Z-order"),
|
||||
_("Z-order of an object"),
|
||||
_("Visibility"),
|
||||
"",
|
||||
"res/actions/planicon.png")
|
||||
.AddParameter("object", _("Object"));
|
||||
|
||||
@@ -1267,8 +1281,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Enable an effect on the object"),
|
||||
_("Enable effect _PARAM1_ on _PARAM0_: _PARAM2_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("yesorno", _("Enable?"))
|
||||
@@ -1283,8 +1297,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
@@ -1301,8 +1315,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM2_ to _PARAM3_ for effect _PARAM1_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
@@ -1318,8 +1332,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM2_ for effect _PARAM1_ of _PARAM0_: _PARAM3_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.AddParameter("objectEffectParameterName", _("Property name"))
|
||||
@@ -1333,8 +1347,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Check if the effect on an object is enabled."),
|
||||
_("Effect _PARAM1_ of _PARAM0_ is enabled"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
.MarkAsSimple()
|
||||
@@ -1610,7 +1624,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Cast a ray from _PARAM1_;_PARAM2_, angle: _PARAM3_ and max "
|
||||
"distance: _PARAM4_px, against _PARAM0_, and save the "
|
||||
"result in _PARAM5_, _PARAM6_"),
|
||||
"",
|
||||
_("Collision"),
|
||||
"res/conditions/raycast24.png",
|
||||
"res/conditions/raycast.png")
|
||||
.AddParameter("objectList", _("Objects to test against the ray"))
|
||||
@@ -1641,7 +1655,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
_("Cast a ray from _PARAM1_;_PARAM2_ to _PARAM3_;_PARAM4_ "
|
||||
"against _PARAM0_, and save the "
|
||||
"result in _PARAM5_, _PARAM6_"),
|
||||
"",
|
||||
_("Collision"),
|
||||
"res/conditions/raycast24.png",
|
||||
"res/conditions/raycast.png")
|
||||
.AddParameter("objectList", _("Objects to test against the ray"))
|
||||
|
@@ -27,7 +27,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Layers and cameras"))
|
||||
.SetIcon("res/conditions/camera24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
|
||||
.SetIcon("res/actions/effect24.png");
|
||||
.SetIcon("res/actions/effect_black.svg");
|
||||
|
||||
extension
|
||||
.AddExpressionAndConditionAndAction(
|
||||
@@ -327,7 +327,6 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.AddParameter("expression", _("Camera number (default : 0)"), "", true)
|
||||
.SetDefaultValue("0");
|
||||
|
||||
// TODO Deprecated: hide this action in a future release.
|
||||
extension
|
||||
.AddAction(
|
||||
"FixCamera",
|
||||
@@ -339,6 +338,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"",
|
||||
"res/actions/camera24.png",
|
||||
"res/actions/camera.png")
|
||||
.SetHidden()
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("objectPtr", _("Object"))
|
||||
.AddParameter("expression",
|
||||
@@ -386,7 +386,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
_("Center the camera on an object"),
|
||||
_("Center the camera on the specified object."),
|
||||
_("Center camera on _PARAM1_ (layer: _PARAM3_)"),
|
||||
"",
|
||||
_("Layers and cameras"),
|
||||
"res/actions/camera24.png",
|
||||
"res/actions/camera.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
@@ -450,8 +450,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
@@ -469,8 +469,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of layer _PARAM1_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
@@ -488,8 +488,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM3_ for effect _PARAM2_ of layer _PARAM1_: _PARAM4_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
@@ -504,8 +504,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
_("The effect on a layer is enabled"),
|
||||
_("Effect _PARAM2_ on layer _PARAM1_ is enabled"),
|
||||
_(""),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
@@ -518,8 +518,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
_("Enable an effect on a layer"),
|
||||
_("Enable effect _PARAM2_ on layer _PARAM1_: _PARAM3_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
@@ -548,7 +548,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"ChangeLayerTimeScale",
|
||||
_("Change layer time scale"),
|
||||
_("Layer time scale"),
|
||||
_("Change the time scale applied to the objects of the layer."),
|
||||
_("Set the time scale of layer _PARAM1_ to _PARAM2_"),
|
||||
"",
|
||||
@@ -594,7 +594,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerAmbientLightColor",
|
||||
_("Set the ambient light color"),
|
||||
_("Ambient light color"),
|
||||
_("Set the ambient light color of the lighting layer in format "
|
||||
"\"R;G;B\" string."),
|
||||
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animatable capability"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Animations and images"))
|
||||
.SetIcon("res/actions/animation24.png");
|
||||
|
||||
@@ -42,7 +44,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number",
|
||||
"Index",
|
||||
_("Animation (by number)"),
|
||||
_("the number of the animation played by the object (the number from "
|
||||
_("the animation played by the object using the animation number (from "
|
||||
"the animations list)"),
|
||||
_("the number of the animation"),
|
||||
_("Animations and images"),
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation index")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["Index"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"string",
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"objectAnimationName", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Animation name")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Name"].SetGroup("");
|
||||
|
||||
aut.AddScopedAction("PauseAnimation",
|
||||
_("Pause the animation"),
|
||||
@@ -107,6 +111,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Speed scale")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllExpressions()["SpeedScale"].SetGroup("");
|
||||
|
||||
aut.AddScopedCondition("IsAnimationPaused",
|
||||
_("Animation paused"),
|
||||
@@ -130,6 +135,31 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.MarkAsSimple();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
"ElapsedTime",
|
||||
_("Animation elapsed time"),
|
||||
_("the elapsed time from the beginning of the animation (in seconds)"),
|
||||
_("the animation elapsed time"),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior")
|
||||
.UseStandardParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Elapsed time (in seconds)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["ElapsedTime"].SetGroup("");
|
||||
|
||||
aut.AddExpression(
|
||||
"Duration",
|
||||
_("Animation duration"),
|
||||
_("Return the current animation duration (in seconds)."),
|
||||
_("Animations and images"),
|
||||
"res/actions/animation24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "AnimatableBehavior");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -24,7 +24,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
|
||||
.SetIcon("res/actions/effect24.png");
|
||||
.SetIcon("res/actions/effect_black.svg");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"EffectBehavior",
|
||||
@@ -32,7 +32,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
"Effect",
|
||||
_("Apply visual effects to objects."),
|
||||
"",
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect_black.svg",
|
||||
"EffectBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
@@ -43,8 +43,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
_("Enable an effect on the object"),
|
||||
_("Enable effect _PARAM2_ on _PARAM0_: _PARAM3_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
@@ -58,8 +58,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
@@ -75,8 +75,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Set _PARAM3_ to _PARAM4_ for effect _PARAM2_ of _PARAM0_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
@@ -91,8 +91,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
"names) in the effects window."),
|
||||
_("Enable _PARAM3_ for effect _PARAM2_ of _PARAM0_: _PARAM4_"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
@@ -105,8 +105,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsEffectExtension(
|
||||
_("Check if the effect on an object is enabled."),
|
||||
_("Effect _PARAM2_ of _PARAM0_ is enabled"),
|
||||
_("Effects"),
|
||||
"res/actions/effect24.png",
|
||||
"res/actions/effect.png")
|
||||
"res/actions/effect_black.svg",
|
||||
"res/actions/effect_black.svg")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "EffectBehavior")
|
||||
.AddParameter("objectEffectName", _("Effect name"))
|
||||
|
@@ -24,7 +24,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFlippableExtension(
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Effects"))
|
||||
.SetIcon("res/actions/effect24.png");
|
||||
.SetIcon("res/actions/effect_black.svg");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"FlippableBehavior",
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Opacity capability"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Visibility"))
|
||||
.SetIcon("res/actions/opacity24.png");
|
||||
|
||||
@@ -54,6 +56,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsOpacityExtension(
|
||||
_("Opacity (0-255)")))
|
||||
.SetFunctionName("setOpacity")
|
||||
.SetGetter("getOpacity");
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -23,6 +23,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Scalable capability"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
|
||||
@@ -44,7 +46,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale"),
|
||||
_("the scale of the object (default scale is 1)"),
|
||||
_("the scale"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scale24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -53,6 +55,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Value"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -60,7 +63,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale on X axis"),
|
||||
_("the scale on X axis of the object (default scale is 1)"),
|
||||
_("the scale on X axis"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scaleWidth24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -69,6 +72,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["X"].SetGroup("");
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"number",
|
||||
@@ -76,7 +80,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
_("Scale on Y axis"),
|
||||
_("the scale on Y axis of the object (default scale is 1)"),
|
||||
_("the scale on Y axis"),
|
||||
_("Scale"),
|
||||
_("Size"),
|
||||
"res/actions/scaleHeight24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "ScalableBehavior")
|
||||
@@ -85,6 +89,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsScalableExtension(
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
aut.GetAllExpressions()["Y"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the GNU Lesser General Public
|
||||
* License.
|
||||
*/
|
||||
#include "GDCore/Extensions/Builtin/AllBuiltinExtensions.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/BehaviorsSharedData.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
|
||||
void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTextContainerExtension(
|
||||
gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("TextContainerCapability",
|
||||
_("Text capability"),
|
||||
_("Animate objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Text capability"))
|
||||
.SetIcon("res/conditions/text24_black.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"TextContainerBehavior",
|
||||
_("Text capability"),
|
||||
"Text",
|
||||
_("Access objects text."),
|
||||
"",
|
||||
"res/conditions/text24_black.png",
|
||||
"TextContainerBehavior",
|
||||
std::make_shared<gd::Behavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>())
|
||||
.SetHidden();
|
||||
|
||||
aut.AddExpressionAndConditionAndAction(
|
||||
"string",
|
||||
"Value",
|
||||
_("Text"),
|
||||
_("the text"),
|
||||
_("the text"),
|
||||
"",
|
||||
"res/conditions/text24_black.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TextContainerBehavior")
|
||||
.UseStandardParameters(
|
||||
"string", gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Text")))
|
||||
.MarkAsSimple();
|
||||
aut.GetAllStrExpressions()["Value"].SetGroup("");
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -38,6 +38,8 @@ BuiltinExtensionsImplementer::ImplementsExternalLayoutsExtension(
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter("expression", _("Y position of the origin"), "", true)
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter("expression", _("Z position of the origin"), "", true)
|
||||
.SetDefaultValue("0")
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include "AllBuiltinExtensions.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
@@ -57,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
extension
|
||||
.AddCondition("DoesSceneExist",
|
||||
_("Does scene exist"),
|
||||
_("Check if scene exists."),
|
||||
_("Check if a scene exists."),
|
||||
_("Scene _PARAM1_ exists"),
|
||||
"",
|
||||
"res/actions/texte.png",
|
||||
@@ -163,6 +164,45 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
"res/actions/window.png")
|
||||
.SetHelpPath("/interface/scene-editor/events")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Name of the new scene"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in background for a scene (between 0 and 1)."),
|
||||
_("_PARAM0_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/hourglass_black.svg",
|
||||
"res/actions/hourglass_black.svg")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -30,7 +30,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.AddObject<SpriteObject>("Sprite",
|
||||
_("Sprite"),
|
||||
_("Animated object which can be used for "
|
||||
"most elements of a game"),
|
||||
"most elements of a game."),
|
||||
"CppPlatform/Extensions/spriteicon.png")
|
||||
.SetCategoryFullName(_("General"))
|
||||
.AddDefaultBehavior("EffectCapability::EffectBehavior")
|
||||
@@ -408,6 +408,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
.SetHidden()
|
||||
.MarkAsSimple();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("ScaleWidth",
|
||||
_("Scale on X axis"),
|
||||
_("Compare the scale of the width of an object."),
|
||||
@@ -415,7 +416,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Size"),
|
||||
"res/conditions/scaleWidth24_black.png",
|
||||
"res/conditions/scaleWidth_black.png")
|
||||
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number",
|
||||
@@ -423,6 +424,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Scale (1 by default)")))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
// Deprecated
|
||||
obj.AddCondition("ScaleHeight",
|
||||
_("Scale on Y axis"),
|
||||
_("Compare the scale of the height of an object."),
|
||||
@@ -430,7 +432,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
_("Size"),
|
||||
"res/conditions/scaleHeight24_black.png",
|
||||
"res/conditions/scaleHeight_black.png")
|
||||
|
||||
.SetHidden()
|
||||
.AddParameter("object", _("Object"), "Sprite")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number",
|
||||
|
@@ -87,8 +87,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"SetWindowSize",
|
||||
_("Change the size of the game window"),
|
||||
_("This action changes the size of the game window. Note that this "
|
||||
_("Game window size"),
|
||||
_("Changes the size of the game window. Note that this "
|
||||
"will only work on platform supporting this operation: games "
|
||||
"running in browsers or on mobile phones can not update their "
|
||||
"window size. Game resolution can still be updated."),
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include "ExpressionMetadata.h"
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
@@ -46,16 +47,14 @@ gd::ExpressionMetadata& ExpressionMetadata::AddParameter(
|
||||
// For objects/behavior, the supplementary information
|
||||
// parameter is an object/behavior type...
|
||||
((gd::ParameterMetadata::IsObject(type) ||
|
||||
gd::ParameterMetadata::IsBehavior(type))
|
||||
// Prefix with the namespace if it's not already there.
|
||||
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
|
||||
? (supplementaryInformation.empty()
|
||||
? ""
|
||||
: extensionNamespace +
|
||||
supplementaryInformation //... so prefix it with the extension
|
||||
// namespace.
|
||||
)
|
||||
: supplementaryInformation); // Otherwise don't change anything
|
||||
gd::ParameterMetadata::IsBehavior(type))
|
||||
// Prefix with the namespace if it's not already there.
|
||||
&& (supplementaryInformation.find(
|
||||
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
|
||||
? (supplementaryInformation.empty()
|
||||
? ""
|
||||
: extensionNamespace + supplementaryInformation)
|
||||
: supplementaryInformation));
|
||||
|
||||
// TODO: Assert against supplementaryInformation === "emsc" (when running with
|
||||
// Emscripten), and warn about a missing argument when calling addParameter.
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
@@ -64,17 +65,14 @@ InstructionMetadata& InstructionMetadata::AddParameter(
|
||||
// For objects/behavior, the supplementary information
|
||||
// parameter is an object/behavior type...
|
||||
((gd::ParameterMetadata::IsObject(type) ||
|
||||
gd::ParameterMetadata::IsBehavior(type))
|
||||
// Prefix with the namespace if it's not already there.
|
||||
&& !(supplementaryInformation.rfind(extensionNamespace, 0) == 0))
|
||||
? (supplementaryInformation.empty()
|
||||
? ""
|
||||
: extensionNamespace +
|
||||
supplementaryInformation //... so prefix it with the
|
||||
// extension
|
||||
// namespace.
|
||||
)
|
||||
: supplementaryInformation); // Otherwise don't change anything
|
||||
gd::ParameterMetadata::IsBehavior(type))
|
||||
// Prefix with the namespace if it's not already there.
|
||||
&& (supplementaryInformation.find(
|
||||
PlatformExtension::GetNamespaceSeparator()) == gd::String::npos)
|
||||
? (supplementaryInformation.empty()
|
||||
? ""
|
||||
: extensionNamespace + supplementaryInformation)
|
||||
: supplementaryInformation));
|
||||
|
||||
// TODO: Assert against supplementaryInformation === "emsc" (when running with
|
||||
// Emscripten), and warn about a missing argument when calling addParameter.
|
||||
@@ -190,7 +188,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
|
||||
gd::String templateSentence = _("<subject> of _PARAM0_ <operator> <value>");
|
||||
|
||||
sentence =
|
||||
templateSentence.FindAndReplace("<subject>", sentence)
|
||||
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
|
||||
.FindAndReplace(
|
||||
"<operator>",
|
||||
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
|
||||
@@ -200,7 +198,7 @@ InstructionMetadata::UseStandardRelationalOperatorParameters(
|
||||
gd::String templateSentence = _("<subject> <operator> <value>");
|
||||
|
||||
sentence =
|
||||
templateSentence.FindAndReplace("<subject>", sentence)
|
||||
templateSentence.FindAndReplace("<subject>", sentence.CapitalizeFirstLetter())
|
||||
.FindAndReplace(
|
||||
"<operator>",
|
||||
"_PARAM" + gd::String::From(operatorParamIndex) + "_")
|
||||
|
@@ -191,6 +191,16 @@ class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetHelpPath
|
||||
*/
|
||||
MultipleInstructionMetadata &SetHelpPath(const gd::String &path) {
|
||||
if (expression) expression->SetHelpPath(path);
|
||||
if (condition) condition->SetHelpPath(path);
|
||||
if (action) action->SetHelpPath(path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::MarkAsSimple
|
||||
*/
|
||||
|
@@ -303,12 +303,24 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Return true if the instruction must be hidden in the IDE.
|
||||
* \brief Return true if the object must be hidden in the IDE.
|
||||
*/
|
||||
bool IsHidden() const { return hidden; }
|
||||
|
||||
/**
|
||||
* \brief Declare a usage of the 3D renderer.
|
||||
*/
|
||||
ObjectMetadata &MarkAsRenderedIn3D() {
|
||||
isRenderedIn3D = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the object uses the 3D renderer.
|
||||
*/
|
||||
bool IsRenderedIn3D() const { return isRenderedIn3D; }
|
||||
|
||||
std::map<gd::String, gd::InstructionMetadata> conditionsInfos;
|
||||
std::map<gd::String, gd::InstructionMetadata> actionsInfos;
|
||||
std::map<gd::String, gd::ExpressionMetadata> expressionsInfos;
|
||||
@@ -329,6 +341,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada
|
||||
gd::String categoryFullName;
|
||||
std::set<gd::String> defaultBehaviorTypes;
|
||||
bool hidden = false;
|
||||
bool isRenderedIn3D = false;
|
||||
|
||||
std::shared_ptr<gd::ObjectConfiguration>
|
||||
blueprintObject; ///< The "blueprint" object to be copied when a new
|
||||
|
@@ -60,10 +60,10 @@ void ParameterMetadataTools::ParametersToObjectsContainer(
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
void ParameterMetadataTools::ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>&
|
||||
parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb) {
|
||||
for (auto it = parametersVectorsList.rbegin();
|
||||
it != parametersVectorsList.rend();
|
||||
@@ -71,7 +71,7 @@ void ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
const std::vector<gd::ParameterMetadata>* parametersVector = *it;
|
||||
|
||||
for (const auto& parameterMetadata: *parametersVector) {
|
||||
if (parameterMetadata.GetName().find(prefix) == 0) cb(parameterMetadata);
|
||||
if (parameterMetadata.GetName().FindCaseInsensitive(search) != gd::String::npos) cb(parameterMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -27,9 +27,9 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
const std::vector<gd::ParameterMetadata>& parameters,
|
||||
gd::ObjectsContainer& outputObjectsContainer);
|
||||
|
||||
static void ForEachParameterWithPrefix(
|
||||
static void ForEachParameterMatchingSearch(
|
||||
const std::vector<const std::vector<gd::ParameterMetadata>*>& parametersVectorsList,
|
||||
const gd::String& prefix,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::ParameterMetadata&)> cb);
|
||||
|
||||
static bool Has(
|
||||
|
@@ -217,7 +217,9 @@ class GD_CORE_API ValueTypeMetadata {
|
||||
parameterType == "jsonResource" ||
|
||||
parameterType == "tilemapResource" ||
|
||||
parameterType == "tilesetResource" ||
|
||||
parameterType == "model3DResource";
|
||||
parameterType == "model3DResource" ||
|
||||
parameterType == "atlasResource" ||
|
||||
parameterType == "spineResource";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -264,8 +264,11 @@ class GD_CORE_API PlatformExtension {
|
||||
*
|
||||
* \param name The name of the behavior
|
||||
* \param fullname The user friendly name of the behavior
|
||||
* \param defaultName The default name of behavior instances
|
||||
* \param description The user friendly description of the behavior
|
||||
* \param group The behavior category label
|
||||
* \param icon The icon of the behavior.
|
||||
* \param className The name of the class implementing the behavior
|
||||
* \param instance An instance of the behavior that
|
||||
* will be used to create the behavior
|
||||
* \param sharedDatasInstance Optional
|
||||
@@ -288,6 +291,7 @@ class GD_CORE_API PlatformExtension {
|
||||
* \param name The name of the behavior
|
||||
* \param fullname The user friendly name of the behavior
|
||||
* \param description The user friendly description of the behavior
|
||||
* \param group The behavior category label
|
||||
* \param icon The icon of the behavior.
|
||||
*/
|
||||
gd::BehaviorMetadata& AddEventsBasedBehavior(
|
||||
|
@@ -4,7 +4,6 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "DependenciesAnalyzer.h"
|
||||
#include <algorithm>
|
||||
#include "GDCore/Events/Builtin/LinkEvent.h"
|
||||
@@ -29,9 +28,9 @@ DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
|
||||
|
||||
bool DependenciesAnalyzer::Analyze() {
|
||||
if (layout)
|
||||
return Analyze(layout->GetEvents(), true);
|
||||
return Analyze(layout->GetEvents());
|
||||
else if (externalEvents)
|
||||
return Analyze(externalEvents->GetEvents(), true);
|
||||
return Analyze(externalEvents->GetEvents());
|
||||
|
||||
std::cout << "ERROR: DependenciesAnalyzer called without any layout or "
|
||||
"external events.";
|
||||
@@ -40,63 +39,38 @@ bool DependenciesAnalyzer::Analyze() {
|
||||
|
||||
DependenciesAnalyzer::~DependenciesAnalyzer() {}
|
||||
|
||||
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLevel) {
|
||||
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
|
||||
for (unsigned int i = 0; i < events.size(); ++i) {
|
||||
const gd::LinkEvent* linkEvent = dynamic_cast<const gd::LinkEvent*>(&events[i]);
|
||||
if (linkEvent) {
|
||||
DependenciesAnalyzer analyzer(*this);
|
||||
|
||||
gd::String linked = linkEvent->GetTarget();
|
||||
if (project.HasExternalEventsNamed(linked)) {
|
||||
if (std::find(parentExternalEvents.begin(),
|
||||
parentExternalEvents.end(),
|
||||
linked) != parentExternalEvents.end())
|
||||
return false; // Circular dependency!
|
||||
|
||||
externalEventsDependencies.insert(
|
||||
linked); // There is a direct dependency
|
||||
if (!isOnTopLevel) notTopLevelExternalEventsDependencies.insert(linked);
|
||||
analyzer.AddParentExternalEvents(linked);
|
||||
if (!analyzer.Analyze(project.GetExternalEvents(linked).GetEvents(),
|
||||
isOnTopLevel))
|
||||
linked) != parentExternalEvents.end()) {
|
||||
// Circular dependency!
|
||||
return false;
|
||||
|
||||
}
|
||||
bool wasDependencyJustAdded = externalEventsDependencies.insert(linked).second;
|
||||
if (wasDependencyJustAdded) {
|
||||
parentExternalEvents.push_back(linked);
|
||||
if (!Analyze(project.GetExternalEvents(linked).GetEvents()))
|
||||
return false;
|
||||
parentExternalEvents.pop_back();
|
||||
}
|
||||
} else if (project.HasLayoutNamed(linked)) {
|
||||
if (std::find(parentScenes.begin(), parentScenes.end(), linked) !=
|
||||
parentScenes.end())
|
||||
return false; // Circular dependency!
|
||||
|
||||
scenesDependencies.insert(linked); // There is a direct dependency
|
||||
if (!isOnTopLevel) notTopLevelScenesDependencies.insert(linked);
|
||||
analyzer.AddParentScene(linked);
|
||||
if (!analyzer.Analyze(project.GetLayout(linked).GetEvents(),
|
||||
isOnTopLevel))
|
||||
parentScenes.end()) {
|
||||
// Circular dependency!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update with indirect dependencies.
|
||||
scenesDependencies.insert(analyzer.GetScenesDependencies().begin(),
|
||||
analyzer.GetScenesDependencies().end());
|
||||
externalEventsDependencies.insert(
|
||||
analyzer.GetExternalEventsDependencies().begin(),
|
||||
analyzer.GetExternalEventsDependencies().end());
|
||||
sourceFilesDependencies.insert(
|
||||
analyzer.GetSourceFilesDependencies().begin(),
|
||||
analyzer.GetSourceFilesDependencies().end());
|
||||
notTopLevelScenesDependencies.insert(
|
||||
analyzer.GetNotTopLevelScenesDependencies().begin(),
|
||||
analyzer.GetNotTopLevelScenesDependencies().end());
|
||||
notTopLevelExternalEventsDependencies.insert(
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies().begin(),
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies().end());
|
||||
|
||||
if (!isOnTopLevel) {
|
||||
notTopLevelScenesDependencies.insert(
|
||||
analyzer.GetScenesDependencies().begin(),
|
||||
analyzer.GetScenesDependencies().end());
|
||||
notTopLevelExternalEventsDependencies.insert(
|
||||
analyzer.GetExternalEventsDependencies().begin(),
|
||||
analyzer.GetExternalEventsDependencies().end());
|
||||
}
|
||||
bool wasDependencyJustAdded = scenesDependencies.insert(linked).second;
|
||||
if (wasDependencyJustAdded) {
|
||||
parentScenes.push_back(linked);
|
||||
if (!Analyze(project.GetLayout(linked).GetEvents()))
|
||||
return false;
|
||||
parentScenes.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,45 +86,9 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLev
|
||||
|
||||
// Analyze sub events dependencies
|
||||
if (events[i].CanHaveSubEvents()) {
|
||||
if (!Analyze(events[i].GetSubEvents(), false)) return false;
|
||||
if (!Analyze(events[i].GetSubEvents())) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene() {
|
||||
if (!externalEvents) {
|
||||
std::cout << "ERROR: ExternalEventsCanBeCompiledForAScene called without "
|
||||
"external events set!"
|
||||
<< std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
gd::String sceneName;
|
||||
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
// For each layout, compute the dependencies and the dependencies which are
|
||||
// not coming from a top level event.
|
||||
DependenciesAnalyzer analyzer(project, project.GetLayout(i));
|
||||
if (!analyzer.Analyze()) continue; // Analyze failed -> Cyclic dependencies
|
||||
const std::set<gd::String>& dependencies =
|
||||
analyzer.GetExternalEventsDependencies();
|
||||
const std::set<gd::String>& notTopLevelDependencies =
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies();
|
||||
|
||||
// Check if the external events is a dependency, and that is is only present
|
||||
// as a link on the top level.
|
||||
if (dependencies.find(externalEvents->GetName()) != dependencies.end() &&
|
||||
notTopLevelDependencies.find(externalEvents->GetName()) ==
|
||||
notTopLevelDependencies.end()) {
|
||||
if (!sceneName.empty())
|
||||
return ""; // External events can be compiled only if one scene is
|
||||
// including them.
|
||||
else
|
||||
sceneName = project.GetLayout(i).GetName();
|
||||
}
|
||||
}
|
||||
|
||||
return sceneName; // External events can be compiled and used for the scene.
|
||||
}
|
||||
#endif
|
||||
|
@@ -39,11 +39,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
|
||||
/**
|
||||
* \brief Constructor for analyzing the dependencies of external events.
|
||||
*
|
||||
* You can also call then
|
||||
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene to check if the
|
||||
* external events can be compiled separately and called by a scene. \see
|
||||
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
|
||||
*/
|
||||
DependenciesAnalyzer(const gd::Project& project_,
|
||||
const gd::ExternalEvents& externalEvents);
|
||||
@@ -60,18 +55,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
*/
|
||||
bool Analyze();
|
||||
|
||||
/**
|
||||
* Check if the external events (passed in the constructor) can be compiled
|
||||
* and called by a single scene:<br> This is possible when the link calling
|
||||
* the external events does not have any parent event and when this situation
|
||||
* occurs only in a single scene and not in another.
|
||||
*
|
||||
* \return The name of the scene which is able to call the compiled external
|
||||
* events. If empty, no scene is able to call them. (So external events have
|
||||
* to be included directly by links).
|
||||
*/
|
||||
gd::String ExternalEventsCanBeCompiledForAScene();
|
||||
|
||||
/**
|
||||
* \brief Return the scenes being dependencies of the scene or external events
|
||||
* passed in the constructor.
|
||||
@@ -96,25 +79,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
return sourceFilesDependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return the scenes being dependencies of the scene or external events
|
||||
* passed in the constructor, but being not top level dependencies: The links
|
||||
* including them are not a top level events (i.e: They have a parent event).
|
||||
*/
|
||||
const std::set<gd::String>& GetNotTopLevelScenesDependencies() const {
|
||||
return notTopLevelScenesDependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return the external events being dependencies of the scene or
|
||||
* external events passed in the constructor, but being not top level
|
||||
* dependencies: The links including them are not a top level events (i.e:
|
||||
* They have a parent event).
|
||||
*/
|
||||
const std::set<gd::String>& GetNotTopLevelExternalEventsDependencies() const {
|
||||
return notTopLevelExternalEventsDependencies;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Analyze the dependencies of the events.
|
||||
@@ -124,32 +88,11 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
* (they have no parents). \return false if a circular dependency exists, true
|
||||
* otherwise.
|
||||
*/
|
||||
bool Analyze(const gd::EventsList& events, bool isOnTopLevel);
|
||||
|
||||
void AddParentScene(gd::String parentScene) {
|
||||
parentScenes.push_back(parentScene);
|
||||
};
|
||||
void AddParentExternalEvents(gd::String parentExternalEvents_) {
|
||||
parentExternalEvents.push_back(parentExternalEvents_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if all links pointing to external events called \a
|
||||
* externalEventsName are only at the top level of \a events. The function
|
||||
* return false as soon as it discover a link to external events which is not
|
||||
* at the top level ( i.e: It has a parent event ).
|
||||
*
|
||||
* \warning The function assumes that there are not cyclic dependencies.
|
||||
*/
|
||||
bool CheckIfExternalEventsIsLinkedOnlyAtTopLevel(
|
||||
const gd::String& externalEventsName,
|
||||
std::vector<std::shared_ptr<gd::BaseEvent> >& events);
|
||||
bool Analyze(const gd::EventsList& events);
|
||||
|
||||
std::set<gd::String> scenesDependencies;
|
||||
std::set<gd::String> externalEventsDependencies;
|
||||
std::set<gd::String> sourceFilesDependencies;
|
||||
std::set<gd::String> notTopLevelScenesDependencies;
|
||||
std::set<gd::String> notTopLevelExternalEventsDependencies;
|
||||
std::vector<gd::String>
|
||||
parentScenes; ///< Used to check for circular dependencies.
|
||||
std::vector<gd::String>
|
||||
|
@@ -58,7 +58,7 @@ 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);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -88,7 +88,7 @@ class GD_CORE_API ExpressionObjectsAnalyzer
|
||||
if (node.child) node.child->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
context.AddObjectName(projectScopedContainers, node.identifierName);
|
||||
} else if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
|
@@ -226,7 +226,7 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
@@ -81,8 +81,7 @@ 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);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -115,7 +114,7 @@ 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, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == objectName) {
|
||||
hasDoneRenaming = true;
|
||||
@@ -217,8 +216,7 @@ 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);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -250,7 +248,7 @@ 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, projectScopedContainers.GetObjectsContainersList(), rootType, node);
|
||||
auto type = gd::ExpressionTypeFinder::GetType(platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) &&
|
||||
node.identifierName == searchedObjectName) {
|
||||
hasObject = true;
|
||||
|
@@ -136,7 +136,7 @@ class GD_CORE_API ExpressionVariableReplacer
|
||||
auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
if (!objectNameToUseForVariableAccessor.empty()) {
|
||||
if (objectsContainersList.HasVariablesContainer(
|
||||
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
|
||||
objectNameToUseForVariableAccessor, targetVariablesContainer)) {
|
||||
// The node represents an object variable, and this object variables are
|
||||
// the target. Do the replacement or removals:
|
||||
@@ -177,7 +177,7 @@ class GD_CORE_API ExpressionVariableReplacer
|
||||
GetPotentialNewName(node.identifierName),
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
if (objectsContainersList.HasVariablesContainer(
|
||||
if (objectsContainersList.HasObjectOrGroupVariablesContainer(
|
||||
node.identifierName, targetVariablesContainer)) {
|
||||
// The node represents an object variable, and this object variables
|
||||
// are the target. Do the replacement or removals:
|
||||
|
@@ -258,7 +258,7 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
@@ -11,13 +11,16 @@
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Events/Parsers/GrammarTerminals.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ValueTypeMetadata.h"
|
||||
#include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
|
||||
#include "GDCore/IDE/Events/ExpressionVariableParentFinder.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
|
||||
@@ -395,12 +398,10 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
|
||||
protected:
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch("", type);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
@@ -409,12 +410,10 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
// No completions.
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix("", type);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch("", type);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type, "", searchedPosition + 1, searchedPosition + 1));
|
||||
@@ -488,90 +487,143 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
// Only attempt to complete with the children of the variable
|
||||
// if it's the last child (no more `.AnotherVariable` written after).
|
||||
bool eagerlyCompleteIfExactMatch = node.child == nullptr;
|
||||
|
||||
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
const gd::VariablesContainer* variablesContainer = nullptr;
|
||||
if (type == "globalvar") {
|
||||
variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
} else if (type == "scenevar") {
|
||||
variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (type == "globalvar" || type == "scenevar") {
|
||||
const auto* variablesContainer =
|
||||
type == "globalvar"
|
||||
? projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer()
|
||||
: projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(*variablesContainer,
|
||||
node.name,
|
||||
node.nameLocation,
|
||||
eagerlyCompleteIfExactMatch);
|
||||
}
|
||||
} else if (type == "objectvar") {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
// Variable fields doesn't use expression completion,
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
variablesContainer =
|
||||
objectsContainersList.GetObjectOrGroupVariablesContainer(
|
||||
objectName);
|
||||
}
|
||||
platform, objectsContainersList, rootObjectName, node);
|
||||
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(
|
||||
*variablesContainer, node.name, node.nameLocation);
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList,
|
||||
objectName,
|
||||
node.name,
|
||||
node.nameLocation,
|
||||
eagerlyCompleteIfExactMatch);
|
||||
}
|
||||
} else {
|
||||
AddCompletionsForObjectsAndVariablesWithPrefix(
|
||||
node.name, type, node.nameLocation);
|
||||
AddCompletionsForObjectsAndVariablesMatchingSearch(
|
||||
node.name, type, node.nameLocation, eagerlyCompleteIfExactMatch);
|
||||
}
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
// No completions
|
||||
VariableAndItsParent variableAndItsParent =
|
||||
gd::ExpressionVariableParentFinder::GetLastParentOfNode(
|
||||
platform, projectScopedContainers, node);
|
||||
|
||||
// If no child, we're at the end of a variable (like `GrandChild` in
|
||||
// `Something.Child.GrandChild`) so we can complete eagerly children if we
|
||||
// can.
|
||||
gd::String eagerlyCompleteForVariableName =
|
||||
node.child == nullptr ? node.name : "";
|
||||
AddCompletionsForChildrenVariablesOf(variableAndItsParent,
|
||||
node.nameLocation,
|
||||
eagerlyCompleteForVariableName);
|
||||
}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
// No completions
|
||||
}
|
||||
VariableBracketAccessorNode& node) override {}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type)) {
|
||||
// Only show completions of objects if an object is required.
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.identifierName, type, node.location);
|
||||
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
|
||||
const gd::VariablesContainer* variablesContainer = nullptr;
|
||||
if (type == "globalvar") {
|
||||
variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
} else if (type == "scenevar") {
|
||||
variablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (type == "globalvar" || type == "scenevar") {
|
||||
const auto* variablesContainer =
|
||||
type == "globalvar"
|
||||
? projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer()
|
||||
: projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
if (variablesContainer) {
|
||||
if (IsCaretOn(node.identifierNameDotLocation) ||
|
||||
IsCaretOn(node.childIdentifierNameLocation)) {
|
||||
// Complete a potential child variable:
|
||||
if (variablesContainer->Has(node.identifierName)) {
|
||||
AddCompletionsForChildrenVariablesOf(
|
||||
&variablesContainer->Get(node.identifierName),
|
||||
node.childIdentifierNameLocation,
|
||||
node.childIdentifierName);
|
||||
}
|
||||
} else {
|
||||
// Complete a root variable of the scene or project.
|
||||
|
||||
// Don't attempt to complete children variables if there is
|
||||
// already a dot written (`MyVariable.`).
|
||||
bool eagerlyCompleteIfPossible =
|
||||
!node.identifierNameDotLocation.IsValid();
|
||||
AddCompletionsForVariablesMatchingSearch(
|
||||
*variablesContainer,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation,
|
||||
eagerlyCompleteIfPossible);
|
||||
}
|
||||
}
|
||||
} else if (type == "objectvar") {
|
||||
auto objectName = gd::ExpressionVariableOwnerFinder::GetObjectName(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
// Variable fields doesn't use expression completion,
|
||||
// so the object will be found inside the expression itself.
|
||||
"",
|
||||
node);
|
||||
variablesContainer =
|
||||
objectsContainersList.GetObjectOrGroupVariablesContainer(
|
||||
objectName);
|
||||
}
|
||||
platform, objectsContainersList, rootObjectName, node);
|
||||
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(*variablesContainer,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation);
|
||||
if (IsCaretOn(node.identifierNameDotLocation) ||
|
||||
IsCaretOn(node.childIdentifierNameLocation)) {
|
||||
// Complete a potential child variable:
|
||||
const auto* variablesContainer =
|
||||
objectsContainersList.GetObjectOrGroupVariablesContainer(
|
||||
objectName);
|
||||
if (variablesContainer &&
|
||||
variablesContainer->Has(node.identifierName)) {
|
||||
AddCompletionsForChildrenVariablesOf(
|
||||
&variablesContainer->Get(node.identifierName),
|
||||
node.childIdentifierNameLocation,
|
||||
node.childIdentifierName);
|
||||
}
|
||||
} else {
|
||||
// Complete a root variable of the object.
|
||||
|
||||
// Don't attempt to complete children variables if there is
|
||||
// already a dot written (`MyVariable.`).
|
||||
bool eagerlyCompleteIfPossible =
|
||||
!node.identifierNameDotLocation.IsValid();
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList,
|
||||
objectName,
|
||||
node.identifierName,
|
||||
node.identifierNameLocation,
|
||||
eagerlyCompleteIfPossible);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Object function, behavior name, variable, object variable.
|
||||
if (IsCaretOn(node.identifierNameLocation)) {
|
||||
// Is this the proper position?
|
||||
AddCompletionsForAllIdentifiersWithPrefix(
|
||||
node.identifierName, type, node.identifierNameLocation);
|
||||
// Don't attempt to complete children variables if there is
|
||||
// already a dot written (`MyVariable.`).
|
||||
bool eagerlyCompleteIfPossible =
|
||||
!node.identifierNameDotLocation.IsValid();
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
node.identifierName,
|
||||
type,
|
||||
node.identifierNameLocation,
|
||||
eagerlyCompleteIfPossible);
|
||||
if (!node.identifierNameDotLocation.IsValid()) {
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
@@ -582,46 +634,69 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
} else if (IsCaretOn(node.identifierNameDotLocation) ||
|
||||
IsCaretOn(node.childIdentifierNameLocation)) {
|
||||
const gd::String& objectName = node.identifierName;
|
||||
// Might be:
|
||||
// - An object variable, object behavior or object expression.
|
||||
// - Or a variable with a child.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
node.identifierName,
|
||||
[&]() {
|
||||
// This is an object.
|
||||
const gd::String& objectName = node.identifierName;
|
||||
AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
objectsContainersList,
|
||||
objectName,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation,
|
||||
true);
|
||||
|
||||
// Might be an object variable, object behavior or object expression:
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(objectName);
|
||||
if (variablesContainer) {
|
||||
AddCompletionsForVariablesWithPrefix(
|
||||
*variablesContainer,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation);
|
||||
}
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForBehaviorWithPrefix(
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
objectName));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
objectName));
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
VariableAndItsParent variableAndItsParent =
|
||||
gd::ExpressionVariableParentFinder::GetLastParentOfNode(
|
||||
platform, projectScopedContainers, node);
|
||||
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForBehaviorWithPrefix(
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
objectName));
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type,
|
||||
node.childIdentifierName,
|
||||
node.childIdentifierNameLocation.GetStartPosition(),
|
||||
node.childIdentifierNameLocation.GetEndPosition(),
|
||||
objectName));
|
||||
AddCompletionsForChildrenVariablesOf(
|
||||
variableAndItsParent,
|
||||
node.childIdentifierNameLocation,
|
||||
node.childIdentifierName);
|
||||
},
|
||||
[&]() {
|
||||
// Ignore properties here.
|
||||
// There is no support for "children" of properties.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore parameters here.
|
||||
// There is no support for "children" of parameters.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore unrecognised identifiers here.
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
if (!node.behaviorFunctionName.empty() ||
|
||||
node.behaviorNameNamespaceSeparatorLocation.IsValid()) {
|
||||
// Behavior function (or behavior function being written, with the
|
||||
// function name missing)
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
@@ -645,7 +720,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else {
|
||||
// Object function or behavior name
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.objectFunctionOrBehaviorNameLocation)) {
|
||||
@@ -666,17 +741,15 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
bool isCaretOnParenthesis = IsCaretOn(node.openingParenthesisLocation) ||
|
||||
IsCaretOn(node.closingParenthesisLocation);
|
||||
|
||||
if (!node.behaviorName.empty()) {
|
||||
// Behavior function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else if (IsCaretOn(node.objectNameDotLocation) ||
|
||||
IsCaretOn(node.behaviorNameLocation)) {
|
||||
@@ -700,7 +773,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
} else if (!node.objectName.empty()) {
|
||||
// Object function
|
||||
if (IsCaretOn(node.objectNameLocation)) {
|
||||
AddCompletionsForObjectWithPrefix(
|
||||
AddCompletionsForObjectMatchingSearch(
|
||||
node.objectName, type, node.objectNameLocation);
|
||||
} else {
|
||||
// Add completions for behaviors, because we could imagine that the user
|
||||
@@ -738,12 +811,11 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
platform, objectsContainersList, rootType, node);
|
||||
platform, projectScopedContainers, rootType, node);
|
||||
|
||||
AddCompletionsForAllIdentifiersWithPrefix(node.text, type, node.location);
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
node.text, type, node.location);
|
||||
completions.push_back(
|
||||
ExpressionCompletionDescription::ForExpressionWithPrefix(
|
||||
type,
|
||||
@@ -762,12 +834,98 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
(inclusive && searchedPosition <= location.GetEndPosition())));
|
||||
}
|
||||
|
||||
void AddCompletionsForVariablesWithPrefix(
|
||||
const gd::VariablesContainer& variablesContainer,
|
||||
const gd::String& prefix,
|
||||
/**
|
||||
* A slightly less strict check than `gd::Project::IsNameSafe` as child
|
||||
* variables can be completed even if they start with a number.
|
||||
*/
|
||||
bool IsIdentifierSafe(const gd::String& name) {
|
||||
if (name.empty()) return false;
|
||||
|
||||
for (auto character : name) {
|
||||
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddCompletionsForChildrenVariablesOf(
|
||||
VariableAndItsParent variableAndItsParent,
|
||||
const ExpressionParserLocation& location,
|
||||
gd::String eagerlyCompleteForVariableName = "") {
|
||||
if (variableAndItsParent.parentVariable) {
|
||||
AddCompletionsForChildrenVariablesOf(variableAndItsParent.parentVariable,
|
||||
location,
|
||||
eagerlyCompleteForVariableName);
|
||||
} else if (variableAndItsParent.parentVariablesContainer) {
|
||||
AddCompletionsForVariablesMatchingSearch(
|
||||
*variableAndItsParent.parentVariablesContainer, "", location);
|
||||
}
|
||||
}
|
||||
|
||||
void AddCompletionsForChildrenVariablesOf(
|
||||
const gd::Variable* variable,
|
||||
const ExpressionParserLocation& location,
|
||||
gd::String eagerlyCompleteForVariableName = "") {
|
||||
if (!variable) return;
|
||||
|
||||
if (variable->GetType() == gd::Variable::Structure) {
|
||||
for (const auto& name : variable->GetAllChildrenNames()) {
|
||||
if (!IsIdentifierSafe(name)) continue;
|
||||
|
||||
const auto& childVariable = variable->GetChild(name);
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Variable,
|
||||
location.GetStartPosition(),
|
||||
location.GetEndPosition());
|
||||
description.SetCompletion(name);
|
||||
description.SetVariableType(childVariable.GetType());
|
||||
completions.push_back(description);
|
||||
|
||||
if (name == eagerlyCompleteForVariableName) {
|
||||
AddEagerCompletionForVariableChildren(childVariable, name, location);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: we could do a "comment only completion" to indicate that nothing
|
||||
// can/should be completed?
|
||||
}
|
||||
}
|
||||
|
||||
void AddEagerCompletionForVariableChildren(
|
||||
const gd::Variable& variable,
|
||||
const gd::String& variableName,
|
||||
const ExpressionParserLocation& location) {
|
||||
variablesContainer.ForEachVariableWithPrefix(
|
||||
prefix,
|
||||
if (variable.GetType() == gd::Variable::Structure) {
|
||||
gd::String prefix = variableName + ".";
|
||||
for (const auto& name : variable.GetAllChildrenNames()) {
|
||||
gd::String completion =
|
||||
IsIdentifierSafe(name)
|
||||
? (prefix + name)
|
||||
: (variableName + "[" +
|
||||
gd::ExpressionParser2NodePrinter::PrintStringLiteral(name) +
|
||||
"]");
|
||||
|
||||
const auto& childVariable = variable.GetChild(name);
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Variable,
|
||||
location.GetStartPosition(),
|
||||
location.GetEndPosition());
|
||||
description.SetCompletion(completion);
|
||||
description.SetVariableType(childVariable.GetType());
|
||||
completions.push_back(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddCompletionsForVariablesMatchingSearch(
|
||||
const gd::VariablesContainer& variablesContainer,
|
||||
const gd::String& search,
|
||||
const ExpressionParserLocation& location,
|
||||
bool eagerlyCompleteIfExactMatch = false) {
|
||||
variablesContainer.ForEachVariableMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Variable,
|
||||
@@ -776,34 +934,66 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
description.SetCompletion(variableName);
|
||||
description.SetVariableType(variable.GetType());
|
||||
completions.push_back(description);
|
||||
|
||||
if (eagerlyCompleteIfExactMatch && variableName == search) {
|
||||
AddEagerCompletionForVariableChildren(
|
||||
variable, variableName, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectWithPrefix(
|
||||
const gd::String& prefix,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.GetObjectsContainersList().ForEachNameWithPrefix(
|
||||
prefix,
|
||||
[&](const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration) {
|
||||
void AddCompletionsForObjectOrGroupVariablesMatchingSearch(
|
||||
const gd::ObjectsContainersList& objectsContainersList,
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
const ExpressionParserLocation& location,
|
||||
bool eagerlyCompleteIfExactMatch) {
|
||||
objectsContainersList.ForEachObjectOrGroupVariableMatchingSearch(
|
||||
objectOrGroupName,
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Object,
|
||||
ExpressionCompletionDescription::Variable,
|
||||
location.GetStartPosition(),
|
||||
location.GetEndPosition());
|
||||
description.SetObjectConfiguration(objectConfiguration);
|
||||
description.SetCompletion(name);
|
||||
description.SetType(type);
|
||||
description.SetCompletion(variableName);
|
||||
description.SetVariableType(variable.GetType());
|
||||
completions.push_back(description);
|
||||
|
||||
if (eagerlyCompleteIfExactMatch && variableName == search) {
|
||||
AddEagerCompletionForVariableChildren(
|
||||
variable, variableName, location);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectsAndVariablesWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void AddCompletionsForObjectMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.ForEachIdentifierWithPrefix(
|
||||
prefix,
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.ForEachNameMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
ExpressionCompletionDescription::Object,
|
||||
location.GetStartPosition(),
|
||||
location.GetEndPosition());
|
||||
description.SetObjectConfiguration(objectConfiguration);
|
||||
description.SetCompletion(name);
|
||||
description.SetType(type);
|
||||
completions.push_back(description);
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForObjectsAndVariablesMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location,
|
||||
bool eagerlyCompleteIfExactMatch) {
|
||||
projectScopedContainers.ForEachIdentifierMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& objectName,
|
||||
const ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
@@ -823,6 +1013,11 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
description.SetCompletion(variableName);
|
||||
description.SetVariableType(variable.GetType());
|
||||
completions.push_back(description);
|
||||
|
||||
if (eagerlyCompleteIfExactMatch && variableName == search) {
|
||||
AddEagerCompletionForVariableChildren(
|
||||
variable, variableName, location);
|
||||
}
|
||||
},
|
||||
[&](const gd::NamedPropertyDescriptor& property) {
|
||||
// Ignore properties here.
|
||||
@@ -832,20 +1027,21 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
});
|
||||
}
|
||||
|
||||
void AddCompletionsForAllIdentifiersWithPrefix(const gd::String& prefix,
|
||||
const gd::String& type) {
|
||||
AddCompletionsForAllIdentifiersWithPrefix(
|
||||
prefix,
|
||||
void AddCompletionsForAllIdentifiersMatchingSearch(const gd::String& search,
|
||||
const gd::String& type) {
|
||||
AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
search,
|
||||
type,
|
||||
ExpressionParserLocation(searchedPosition + 1, searchedPosition + 1));
|
||||
}
|
||||
|
||||
void AddCompletionsForAllIdentifiersWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void AddCompletionsForAllIdentifiersMatchingSearch(
|
||||
const gd::String& search,
|
||||
const gd::String& type,
|
||||
const ExpressionParserLocation& location) {
|
||||
projectScopedContainers.ForEachIdentifierWithPrefix(
|
||||
prefix,
|
||||
const ExpressionParserLocation& location,
|
||||
bool eagerlyCompleteIfExactMatch = false) {
|
||||
projectScopedContainers.ForEachIdentifierMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String& objectName,
|
||||
const ObjectConfiguration* objectConfiguration) {
|
||||
ExpressionCompletionDescription description(
|
||||
@@ -865,6 +1061,11 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
description.SetCompletion(variableName);
|
||||
description.SetVariableType(variable.GetType());
|
||||
completions.push_back(description);
|
||||
|
||||
if (eagerlyCompleteIfExactMatch && variableName == search) {
|
||||
AddEagerCompletionForVariableChildren(
|
||||
variable, variableName, location);
|
||||
}
|
||||
},
|
||||
[&](const gd::NamedPropertyDescriptor& property) {
|
||||
ExpressionCompletionDescription description(
|
||||
@@ -892,11 +1093,14 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const gd::String& rootType_,
|
||||
size_t searchedPosition_,
|
||||
gd::ExpressionNode* maybeParentNodeAtLocation_)
|
||||
: platform(platform_),
|
||||
: searchedPosition(searchedPosition_),
|
||||
maybeParentNodeAtLocation(maybeParentNodeAtLocation_),
|
||||
platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
searchedPosition(searchedPosition_),
|
||||
maybeParentNodeAtLocation(maybeParentNodeAtLocation_){};
|
||||
rootObjectName("") // Always empty, might be changed if variable fields
|
||||
// in the editor are changed to use completion.
|
||||
{};
|
||||
|
||||
std::vector<ExpressionCompletionDescription> completions;
|
||||
size_t searchedPosition;
|
||||
@@ -905,6 +1109,7 @@ class GD_CORE_API ExpressionCompletionFinder
|
||||
const gd::Platform& platform;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
const gd::String rootObjectName;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -14,13 +14,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -41,10 +40,10 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
* operations.
|
||||
*/
|
||||
static const gd::String GetType(const gd::Platform &platform,
|
||||
const gd::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionLeftSideTypeFinder typeFinder(
|
||||
platform, objectsContainersList);
|
||||
platform, projectScopedContainers);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -53,9 +52,9 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
|
||||
protected:
|
||||
ExpressionLeftSideTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_)
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
type("unknown") {};
|
||||
|
||||
const gd::String &GetType() {
|
||||
@@ -67,6 +66,14 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {
|
||||
node.leftHandSide->Visit(*this);
|
||||
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
if (type == "unknown" || type == "number|string") {
|
||||
node.rightHandSide->Visit(*this);
|
||||
}
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
node.factor->Visit(*this);
|
||||
@@ -83,7 +90,7 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
type = "unknown";
|
||||
}
|
||||
@@ -93,12 +100,99 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
type = "unknown";
|
||||
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.name,
|
||||
[&]() {
|
||||
// This represents an object.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
// We could store it to explore the type of the variable, but in practice this
|
||||
// is only called for structures/arrays with 2 levels, and we don't support structure
|
||||
// type identification for now.
|
||||
}, [&]() {
|
||||
// This is a property with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is a parameter with more than one child - this is unsupported.
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
type = "unknown";
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
type = "unknown";
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(node.identifierName,
|
||||
[&]() {
|
||||
// It's an object variable.
|
||||
if (projectScopedContainers.GetObjectsContainersList()
|
||||
.HasObjectOrGroupWithVariableNamed(
|
||||
node.identifierName, node.childIdentifierName)
|
||||
== ObjectsContainersList::VariableExistence::DoesNotExist) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
auto variableType =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetTypeOfObjectOrGroupVariable(node.identifierName,
|
||||
node.childIdentifierName);
|
||||
ReadTypeFromVariable(variableType);
|
||||
},
|
||||
[&]() {
|
||||
// It's a variable.
|
||||
const auto& variable =
|
||||
projectScopedContainers.GetVariablesContainersList().Get(
|
||||
node.identifierName);
|
||||
|
||||
if (node.childIdentifierName.empty()) {
|
||||
ReadTypeFromVariable(variable.GetType());
|
||||
} else {
|
||||
if (!variable.HasChild(node.childIdentifierName)) {
|
||||
type = "unknown";
|
||||
return;
|
||||
}
|
||||
|
||||
ReadTypeFromVariable(
|
||||
variable.GetChild(node.childIdentifierName).GetType());
|
||||
}
|
||||
}, [&]() {
|
||||
// This is a property.
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(node.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
type = "number";
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
type = "string";
|
||||
}
|
||||
}, [&]() {
|
||||
// It's a parameter.
|
||||
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
const auto& parameter = gd::ParameterMetadataTools::Get(parametersVectorsList, node.identifierName);
|
||||
const auto& valueTypeMetadata = parameter.GetValueTypeMetadata();
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
type = "number";
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
type = "string";
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
type = "unknown";
|
||||
}
|
||||
}, [&]() {
|
||||
// This is something else.
|
||||
type = "unknown";
|
||||
});
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {
|
||||
type = "unknown";
|
||||
@@ -108,10 +202,18 @@ class GD_CORE_API ExpressionLeftSideTypeFinder : public ExpressionParser2NodeWor
|
||||
}
|
||||
|
||||
private:
|
||||
void ReadTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
type = "number";
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
type = "string";
|
||||
}
|
||||
}
|
||||
|
||||
gd::String type;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -15,13 +15,12 @@
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/Layout.h" // For GetTypeOfObject and GetTypeOfBehavior
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class ObjectsContainersList;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
@@ -51,11 +50,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::ObjectsContainersList &objectsContainersList,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers,
|
||||
const gd::String &rootType,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionTypeFinder typeFinder(
|
||||
platform, objectsContainersList, rootType);
|
||||
platform, projectScopedContainers, rootType);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.GetType();
|
||||
}
|
||||
@@ -64,10 +63,10 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
|
||||
protected:
|
||||
ExpressionTypeFinder(const gd::Platform &platform_,
|
||||
const gd::ObjectsContainersList &objectsContainersList_,
|
||||
const gd::ProjectScopedContainers &projectScopedContainers_,
|
||||
const gd::String &rootType_)
|
||||
: platform(platform_),
|
||||
objectsContainersList(objectsContainersList_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
rootType(rootType_),
|
||||
type(ExpressionTypeFinder::unknownType),
|
||||
child(nullptr) {};
|
||||
@@ -113,8 +112,12 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
}
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
|
||||
// If we can infer a definitive number or string type, use it.
|
||||
// Otherwise, we only know that it's number or string, and this can even
|
||||
// be used as is at runtime.
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
type = leftSideType;
|
||||
@@ -126,7 +129,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& node) override {
|
||||
if (child == nullptr) {
|
||||
const gd::ExpressionMetadata &metadata = MetadataProvider::GetFunctionCallMetadata(
|
||||
platform, objectsContainersList, node);
|
||||
platform, projectScopedContainers.GetObjectsContainersList(), node);
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
VisitParent(node);
|
||||
}
|
||||
@@ -138,7 +141,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
gd::MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers.GetObjectsContainersList(),
|
||||
node,
|
||||
*child);
|
||||
if (parameterMetadata == nullptr || parameterMetadata->GetType().empty()) {
|
||||
@@ -159,7 +162,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
else if (rootType == ExpressionTypeFinder::numberOrStringType) {
|
||||
auto leftSideType = gd::ExpressionLeftSideTypeFinder::GetType(
|
||||
platform,
|
||||
objectsContainersList,
|
||||
projectScopedContainers,
|
||||
node);
|
||||
if (leftSideType == ExpressionTypeFinder::numberType
|
||||
|| leftSideType == ExpressionTypeFinder::stringType) {
|
||||
@@ -183,7 +186,7 @@ class GD_CORE_API ExpressionTypeFinder : public ExpressionParser2NodeWorker {
|
||||
ExpressionNode *child;
|
||||
|
||||
const gd::Platform &platform;
|
||||
const gd::ObjectsContainersList &objectsContainersList;
|
||||
const gd::ProjectScopedContainers &projectScopedContainers;
|
||||
const gd::String rootType;
|
||||
};
|
||||
|
||||
|
@@ -89,20 +89,43 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
|
||||
const auto& parametersVectorsList = projectScopedContainers.GetParametersVectorsList();
|
||||
|
||||
// Unless we find something precise (like a variable or property or parameter with a known type),
|
||||
// we consider this node will be of the type required by the parent.
|
||||
childType = parentType;
|
||||
|
||||
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)) {
|
||||
auto variableExistence = objectsContainersList.HasObjectOrGroupWithVariableNamed(identifier.identifierName, identifier.childIdentifierName);
|
||||
|
||||
if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
|
||||
RaiseTypeError(_("This variable does not exist on this object or group."),
|
||||
identifier.identifierNameLocation);
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
|
||||
RaiseTypeError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
|
||||
identifier.childIdentifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
|
||||
RaiseTypeError(_("This group is empty. Add an object to this group first."),
|
||||
identifier.identifierNameLocation);
|
||||
|
||||
return true; // We should have found a variable.
|
||||
}
|
||||
|
||||
auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(identifier.identifierName, identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(variableType);
|
||||
|
||||
return true; // We found a variable.
|
||||
}, [&]() {
|
||||
@@ -110,7 +133,6 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
|
||||
// Try to identify a declared variable with the name (and maybe the child
|
||||
// variable).
|
||||
|
||||
const gd::Variable& variable =
|
||||
variablesContainersList.Get(identifier.identifierName);
|
||||
|
||||
@@ -118,6 +140,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
// Just the root variable is accessed, check it can be used in an
|
||||
// expression.
|
||||
validateVariableTypeForExpression(variable.GetType());
|
||||
ReadChildTypeFromVariable(variable.GetType());
|
||||
|
||||
return true; // We found a variable.
|
||||
} else {
|
||||
@@ -131,6 +154,7 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
|
||||
const gd::Variable& childVariable =
|
||||
variable.GetChild(identifier.childIdentifierName);
|
||||
ReadChildTypeFromVariable(childVariable.GetType());
|
||||
return true; // We found a variable.
|
||||
}
|
||||
}, [&]() {
|
||||
@@ -138,23 +162,44 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
|
||||
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.
|
||||
}
|
||||
|
||||
const gd::NamedPropertyDescriptor& property = projectScopedContainers
|
||||
.GetPropertiesContainersList().Get(identifier.identifierName).second;
|
||||
|
||||
if (property.GetType() == "Number") {
|
||||
childType = Type::Number;
|
||||
} else if (property.GetType() == "Boolean") {
|
||||
// Nothing - we don't know the precise type (this could be used a string or as a number)
|
||||
} else {
|
||||
// Assume type is String or equivalent.
|
||||
childType = Type::String;
|
||||
}
|
||||
|
||||
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()) {
|
||||
if (valueTypeMetadata.IsNumber()) {
|
||||
childType = Type::Number;
|
||||
} else if (valueTypeMetadata.IsString()) {
|
||||
childType = Type::String;
|
||||
} else if (valueTypeMetadata.IsBoolean()) {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
} else {
|
||||
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.
|
||||
}
|
||||
|
||||
@@ -218,11 +263,17 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
|
||||
}
|
||||
|
||||
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
|
||||
RaiseError("invalid_function_name",
|
||||
if (function.functionName.empty()) {
|
||||
RaiseError("invalid_function_name",
|
||||
_("Enter the name of the function to call."),
|
||||
function.location);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -103,9 +103,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
"example: \"Your name: \" + VariableString(PlayerName).", node.rightHandSide->location);
|
||||
}
|
||||
else if (node.op != '+') {
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
RaiseOperatorError(
|
||||
_("You've used an operator that is not supported. Only + can be used "
|
||||
"to concatenate texts."),
|
||||
ExpressionParserLocation(node.leftHandSide->location.GetEndPosition() + 1, node.location.GetEndPosition()));
|
||||
}
|
||||
} else if (leftType == Type::Object) {
|
||||
@@ -124,7 +124,11 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
node.rightHandSide->Visit(*this);
|
||||
const Type rightType = childType;
|
||||
|
||||
childType = leftType;
|
||||
// The type is decided by the first operand, unless it can (`number|string`)
|
||||
// or should (`unknown`) be refined, in which case we go for the right
|
||||
// operand (which got visited knowing the type of the first operand, so it's
|
||||
// equal or strictly more precise than the left operand).
|
||||
childType = (leftType == Type::Unknown || leftType == Type::NumberOrString) ? leftType : rightType;
|
||||
}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -309,8 +313,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseTypeError(
|
||||
_("You've entered a name, but this type was expected:") + " " + TypeToString(parentType),
|
||||
node.location);
|
||||
childType = parentType;
|
||||
} else {
|
||||
childType = parentType;
|
||||
}
|
||||
childType = parentType;
|
||||
}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {
|
||||
ReportAnyError(node);
|
||||
@@ -379,6 +385,16 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
|
||||
RaiseError("invalid_operator", message, location);
|
||||
}
|
||||
|
||||
void ReadChildTypeFromVariable(gd::Variable::Type variableType) {
|
||||
if (variableType == gd::Variable::Number) {
|
||||
childType = Type::Number;
|
||||
} else if (variableType == gd::Variable::String) {
|
||||
childType = Type::String;
|
||||
} else {
|
||||
// Nothing - we don't know the precise type (this could be used as a string or as a number).
|
||||
}
|
||||
}
|
||||
|
||||
static Type StringToType(const gd::String &type);
|
||||
static const gd::String &TypeToString(Type type);
|
||||
static const gd::String unknownTypeString;
|
||||
|
374
Core/GDCore/IDE/Events/ExpressionVariableParentFinder.h
Normal file
374
Core/GDCore/IDE/Events/ExpressionVariableParentFinder.h
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2Node.h"
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/ObjectMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
|
||||
namespace gd {
|
||||
class Expression;
|
||||
class ObjectsContainer;
|
||||
class Platform;
|
||||
class ParameterMetadata;
|
||||
class ExpressionMetadata;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Contains a variables container or a variable. Useful
|
||||
* to refer to the parent of a variable (which can be a VariablesContainer
|
||||
* or another Variable).
|
||||
*/
|
||||
struct VariableAndItsParent {
|
||||
const gd::VariablesContainer* parentVariablesContainer;
|
||||
const gd::Variable* parentVariable;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Find the last parent (i.e: the variables container) of a node
|
||||
* representing a variable.
|
||||
*
|
||||
* Useful for completions, to know which variables can be entered in a node.
|
||||
*
|
||||
* \see gd::ExpressionParser2
|
||||
*/
|
||||
class GD_CORE_API ExpressionVariableParentFinder
|
||||
: public ExpressionParser2NodeWorker {
|
||||
public:
|
||||
static VariableAndItsParent GetLastParentOfNode(
|
||||
const gd::Platform& platform,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers,
|
||||
gd::ExpressionNode& node) {
|
||||
gd::ExpressionVariableParentFinder typeFinder(platform,
|
||||
projectScopedContainers);
|
||||
node.Visit(typeFinder);
|
||||
return typeFinder.variableAndItsParent;
|
||||
}
|
||||
|
||||
virtual ~ExpressionVariableParentFinder(){};
|
||||
|
||||
protected:
|
||||
ExpressionVariableParentFinder(
|
||||
const gd::Platform& platform_,
|
||||
const gd::ProjectScopedContainers& projectScopedContainers_)
|
||||
: platform(platform_),
|
||||
projectScopedContainers(projectScopedContainers_),
|
||||
variableNode(nullptr),
|
||||
thisIsALegacyPrescopedVariable(false),
|
||||
bailOutBecauseEmptyVariableName(false),
|
||||
legacyPrescopedVariablesContainer(nullptr),
|
||||
variableAndItsParent{} {};
|
||||
|
||||
void OnVisitSubExpressionNode(SubExpressionNode& node) override {}
|
||||
void OnVisitOperatorNode(OperatorNode& node) override {}
|
||||
void OnVisitUnaryOperatorNode(UnaryOperatorNode& node) override {}
|
||||
void OnVisitNumberNode(NumberNode& node) override {}
|
||||
void OnVisitTextNode(TextNode& node) override {}
|
||||
void OnVisitVariableNode(VariableNode& node) override {
|
||||
if (variableNode != nullptr) {
|
||||
// This is not possible
|
||||
return;
|
||||
}
|
||||
variableNode = &node;
|
||||
|
||||
// Check if the parent is a function call, in which we might be dealing
|
||||
// with a legacy pre-scoped variable parameter:
|
||||
if (node.parent) node.parent->Visit(*this);
|
||||
|
||||
if (thisIsALegacyPrescopedVariable) {
|
||||
// The node represents a variable name, and the variables container
|
||||
// containing it was identified in the FunctionCallNode.
|
||||
childVariableNames.insert(childVariableNames.begin(), node.name);
|
||||
if (legacyPrescopedVariablesContainer)
|
||||
variableAndItsParent = WalkUntilLastParent(
|
||||
*legacyPrescopedVariablesContainer, childVariableNames);
|
||||
} else {
|
||||
// Otherwise, the identifier is to be interpreted as usual:
|
||||
// it can be an object (on which a variable is accessed),
|
||||
// or a variable.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
node.name,
|
||||
[&]() {
|
||||
// This is an object.
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(node.name);
|
||||
if (variablesContainer)
|
||||
variableAndItsParent =
|
||||
WalkUntilLastParent(*variablesContainer, childVariableNames);
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
if (projectScopedContainers.GetVariablesContainersList().Has(
|
||||
node.name)) {
|
||||
variableAndItsParent = WalkUntilLastParent(
|
||||
projectScopedContainers.GetVariablesContainersList().Get(
|
||||
node.name),
|
||||
childVariableNames);
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
// Ignore properties here.
|
||||
// There is no support for "children" of properties.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore parameters here.
|
||||
// There is no support for "children" of parameters.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore unrecognised identifiers here.
|
||||
});
|
||||
}
|
||||
}
|
||||
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
|
||||
if (node.name.empty() && node.child) {
|
||||
// A variable accessor should always have a name if it has a child (i.e:
|
||||
// another accessor). While the parser may have generated an empty name,
|
||||
// flag this so we avoid finding a wrong parent (and so, run the risk of
|
||||
// giving wrong autocompletions).
|
||||
bailOutBecauseEmptyVariableName = true;
|
||||
}
|
||||
childVariableNames.insert(childVariableNames.begin(), node.name);
|
||||
if (node.parent) node.parent->Visit(*this);
|
||||
}
|
||||
void OnVisitIdentifierNode(IdentifierNode& node) override {
|
||||
if (variableNode != nullptr) {
|
||||
// This is not possible
|
||||
return;
|
||||
}
|
||||
// This node is not necessarily a variable node.
|
||||
// It will be checked when visiting the FunctionCallNode, just after.
|
||||
variableNode = &node;
|
||||
|
||||
// Check if the parent is a function call, in which we might be dealing
|
||||
// with a legacy pre-scoped variable parameter:
|
||||
if (node.parent) node.parent->Visit(*this);
|
||||
|
||||
if (thisIsALegacyPrescopedVariable) {
|
||||
// The identifier represents a variable name, and the variables container
|
||||
// containing it was identified in the FunctionCallNode.
|
||||
if (!node.childIdentifierName.empty())
|
||||
childVariableNames.insert(childVariableNames.begin(),
|
||||
node.childIdentifierName);
|
||||
childVariableNames.insert(childVariableNames.begin(),
|
||||
node.identifierName);
|
||||
|
||||
if (legacyPrescopedVariablesContainer)
|
||||
variableAndItsParent = WalkUntilLastParent(
|
||||
*legacyPrescopedVariablesContainer, childVariableNames);
|
||||
|
||||
} else {
|
||||
// Otherwise, the identifier is to be interpreted as usual:
|
||||
// it can be an object (on which a variable is accessed),
|
||||
// or a variable.
|
||||
projectScopedContainers.MatchIdentifierWithName<void>(
|
||||
node.identifierName,
|
||||
[&]() {
|
||||
// This is an object.
|
||||
if (!node.childIdentifierName.empty())
|
||||
childVariableNames.insert(childVariableNames.begin(),
|
||||
node.childIdentifierName);
|
||||
|
||||
const auto* variablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(node.identifierName);
|
||||
if (variablesContainer)
|
||||
variableAndItsParent =
|
||||
WalkUntilLastParent(*variablesContainer, childVariableNames);
|
||||
},
|
||||
[&]() {
|
||||
// This is a variable.
|
||||
if (!node.childIdentifierName.empty())
|
||||
childVariableNames.insert(childVariableNames.begin(),
|
||||
node.childIdentifierName);
|
||||
|
||||
if (projectScopedContainers.GetVariablesContainersList().Has(
|
||||
node.identifierName)) {
|
||||
variableAndItsParent = WalkUntilLastParent(
|
||||
projectScopedContainers.GetVariablesContainersList().Get(
|
||||
node.identifierName),
|
||||
childVariableNames);
|
||||
}
|
||||
},
|
||||
[&]() {
|
||||
// Ignore properties here.
|
||||
// There is no support for "children" of properties.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore parameters here.
|
||||
// There is no support for "children" of properties.
|
||||
},
|
||||
[&]() {
|
||||
// Ignore unrecognised identifiers here.
|
||||
});
|
||||
}
|
||||
}
|
||||
void OnVisitEmptyNode(EmptyNode& node) override {}
|
||||
void OnVisitObjectFunctionNameNode(ObjectFunctionNameNode& node) override {}
|
||||
void OnVisitVariableBracketAccessorNode(
|
||||
VariableBracketAccessorNode& node) override {
|
||||
// Add a child with an empty name, which will be interpreted as
|
||||
// "take the first child/item of the structure/array".
|
||||
childVariableNames.insert(childVariableNames.begin(), "");
|
||||
if (node.parent) node.parent->Visit(*this);
|
||||
}
|
||||
void OnVisitFunctionCallNode(FunctionCallNode& functionCall) override {
|
||||
if (variableNode == nullptr) {
|
||||
return;
|
||||
}
|
||||
int parameterIndex = -1;
|
||||
for (int i = 0; i < functionCall.parameters.size(); i++) {
|
||||
if (functionCall.parameters.at(i).get() == variableNode) {
|
||||
parameterIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameterIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& objectsContainersList =
|
||||
projectScopedContainers.GetObjectsContainersList();
|
||||
|
||||
const gd::ParameterMetadata* parameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform, objectsContainersList, functionCall, parameterIndex);
|
||||
if (parameterMetadata == nullptr) return; // Unexpected
|
||||
|
||||
// Support for legacy pre-scoped variables:
|
||||
if (parameterMetadata->GetValueTypeMetadata().IsLegacyPreScopedVariable()) {
|
||||
if (parameterMetadata->GetType() == "objectvar") {
|
||||
// Legacy convention where a "objectvar"
|
||||
// parameter represents a variable of the object represented by the
|
||||
// previous "object" parameter. The object on which the function is
|
||||
// called is returned if no previous parameters are objects.
|
||||
gd::String objectName = functionCall.objectName;
|
||||
for (int previousIndex = parameterIndex - 1; previousIndex >= 0;
|
||||
previousIndex--) {
|
||||
const gd::ParameterMetadata* previousParameterMetadata =
|
||||
MetadataProvider::GetFunctionCallParameterMetadata(
|
||||
platform, objectsContainersList, functionCall, previousIndex);
|
||||
if (previousParameterMetadata != nullptr &&
|
||||
gd::ParameterMetadata::IsObject(
|
||||
previousParameterMetadata->GetType())) {
|
||||
auto previousParameterNode =
|
||||
functionCall.parameters[previousIndex].get();
|
||||
IdentifierNode* objectNode =
|
||||
dynamic_cast<IdentifierNode*>(previousParameterNode);
|
||||
objectName = objectNode->identifierName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
legacyPrescopedVariablesContainer =
|
||||
projectScopedContainers.GetObjectsContainersList()
|
||||
.GetObjectOrGroupVariablesContainer(objectName);
|
||||
thisIsALegacyPrescopedVariable = true;
|
||||
} else if (parameterMetadata->GetType() == "scenevar") {
|
||||
legacyPrescopedVariablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetBottomMostVariablesContainer();
|
||||
thisIsALegacyPrescopedVariable = true;
|
||||
} else if (parameterMetadata->GetType() == "globalvar") {
|
||||
legacyPrescopedVariablesContainer =
|
||||
projectScopedContainers.GetVariablesContainersList()
|
||||
.GetTopMostVariablesContainer();
|
||||
thisIsALegacyPrescopedVariable = true;
|
||||
}
|
||||
} else {
|
||||
thisIsALegacyPrescopedVariable = false;
|
||||
legacyPrescopedVariablesContainer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VariableAndItsParent WalkUntilLastParent(
|
||||
const gd::Variable& variable,
|
||||
const std::vector<gd::String>& childVariableNames,
|
||||
size_t startIndex = 0) {
|
||||
if (bailOutBecauseEmptyVariableName)
|
||||
return {}; // Do not even attempt to find the parent if we had an issue
|
||||
// when visiting nodes.
|
||||
|
||||
const gd::Variable* currentVariable = &variable;
|
||||
|
||||
// Walk until size - 1 as we want the last parent.
|
||||
for (size_t index = startIndex; index + 1 < childVariableNames.size();
|
||||
++index) {
|
||||
const gd::String& childName = childVariableNames[index];
|
||||
|
||||
if (childName.empty()) {
|
||||
if (currentVariable->GetChildrenCount() == 0) {
|
||||
// The array or structure is empty, we can't walk through it - there
|
||||
// is no "parent".
|
||||
return {};
|
||||
}
|
||||
|
||||
if (currentVariable->GetType() == gd::Variable::Array) {
|
||||
currentVariable = ¤tVariable->GetAtIndex(0);
|
||||
} else {
|
||||
currentVariable =
|
||||
currentVariable->GetAllChildren().begin()->second.get();
|
||||
}
|
||||
} else {
|
||||
if (!currentVariable->HasChild(childName)) {
|
||||
// Non existing child - there is no "parent".
|
||||
return {};
|
||||
}
|
||||
|
||||
currentVariable = ¤tVariable->GetChild(childName);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the last parent of the chain of variables (so not the last
|
||||
// variable but the one before it).
|
||||
return {.parentVariable = currentVariable};
|
||||
}
|
||||
|
||||
VariableAndItsParent WalkUntilLastParent(
|
||||
const gd::VariablesContainer& variablesContainer,
|
||||
const std::vector<gd::String>& childVariableNames) {
|
||||
if (bailOutBecauseEmptyVariableName)
|
||||
return {}; // Do not even attempt to find the parent if we had an issue
|
||||
// when visiting nodes.
|
||||
if (childVariableNames.empty())
|
||||
return {}; // There is no "parent" to the variables container itself.
|
||||
|
||||
const gd::String& firstChildName = *childVariableNames.begin();
|
||||
|
||||
const gd::Variable* variable = variablesContainer.Has(firstChildName)
|
||||
? &variablesContainer.Get(firstChildName)
|
||||
: nullptr;
|
||||
if (childVariableNames.size() == 1 || !variable)
|
||||
return {// Only one child: the parent is the variables container itself.
|
||||
.parentVariablesContainer = &variablesContainer};
|
||||
|
||||
return WalkUntilLastParent(*variable, childVariableNames, 1);
|
||||
}
|
||||
|
||||
gd::ExpressionNode* variableNode;
|
||||
std::vector<gd::String> childVariableNames;
|
||||
bool thisIsALegacyPrescopedVariable;
|
||||
bool bailOutBecauseEmptyVariableName;
|
||||
const gd::VariablesContainer* legacyPrescopedVariablesContainer;
|
||||
VariableAndItsParent variableAndItsParent;
|
||||
|
||||
const gd::Platform& platform;
|
||||
const gd::ProjectScopedContainers& projectScopedContainers;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -25,6 +25,9 @@ const UsedExtensionsResult UsedExtensionsFinder::ScanProject(gd::Project& projec
|
||||
void UsedExtensionsFinder::DoVisitObject(gd::Object &object) {
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
project.GetCurrentPlatform(), object.GetType());
|
||||
if (metadata.GetMetadata().IsRenderedIn3D()) {
|
||||
result.MarkAsHaving3DObjects();
|
||||
}
|
||||
result.GetUsedExtensions().insert(metadata.GetExtension().GetName());
|
||||
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
|
||||
result.GetUsedIncludeFiles().insert(includeFile);
|
||||
@@ -110,7 +113,7 @@ void UsedExtensionsFinder::OnVisitVariableNode(VariableNode& node) {
|
||||
result.GetUsedExtensions().insert("BuiltinVariables");
|
||||
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
|
||||
if (gd::ParameterMetadata::IsExpression("variable", type)) {
|
||||
// Nothing to do (this can't reference an object)
|
||||
@@ -154,7 +157,7 @@ void UsedExtensionsFinder::OnVisitVariableBracketAccessorNode(
|
||||
// Add extensions bound to Objects/Behaviors/Functions
|
||||
void UsedExtensionsFinder::OnVisitIdentifierNode(IdentifierNode &node) {
|
||||
auto type = gd::ExpressionTypeFinder::GetType(
|
||||
project.GetCurrentPlatform(), GetObjectsContainersList(), rootType, node);
|
||||
project.GetCurrentPlatform(), GetProjectScopedContainers(), rootType, node);
|
||||
if (gd::ParameterMetadata::IsObject(type) ||
|
||||
GetObjectsContainersList().HasObjectOrGroupNamed(node.identifierName)) {
|
||||
// An object or object variable is used.
|
||||
|
@@ -44,6 +44,13 @@ public:
|
||||
return usedRequiredFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true when at least 1 object uses the 3D renderer.
|
||||
*/
|
||||
bool Has3DObjects() const {
|
||||
return has3DObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extensions used by the project (or part of it).
|
||||
*/
|
||||
@@ -59,10 +66,15 @@ public:
|
||||
*/
|
||||
std::set<gd::String> &GetUsedRequiredFiles() { return usedRequiredFiles; }
|
||||
|
||||
void MarkAsHaving3DObjects() {
|
||||
has3DObjects = true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<gd::String> usedExtensions;
|
||||
std::set<gd::String> usedIncludeFiles;
|
||||
std::set<gd::String> usedRequiredFiles;
|
||||
bool has3DObjects = false;
|
||||
};
|
||||
|
||||
class GD_CORE_API UsedExtensionsFinder
|
||||
|
@@ -43,7 +43,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
|
||||
struct dirent *lecture;
|
||||
DIR *rep;
|
||||
rep = opendir(directory.c_str());
|
||||
int l = 0;
|
||||
|
||||
if (rep == NULL) {
|
||||
cout << "Unable to open Extensions (" << directory << ") directory."
|
||||
@@ -63,8 +62,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
|
||||
|
||||
LoadExtension(directory + "/" + lec, platform, forgiving);
|
||||
librariesLoaded.push_back(directory + "/" + lec);
|
||||
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +100,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
|
||||
struct dirent *lecture;
|
||||
DIR *rep;
|
||||
rep = opendir(directory.c_str());
|
||||
int l = 0;
|
||||
|
||||
if (rep == NULL) {
|
||||
cout << "Unable to open Extensions (" << directory << ") directory."
|
||||
@@ -118,7 +114,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
|
||||
lec.find(".xgd" + suffix, lec.length() - 4 - suffix.length()) !=
|
||||
string::npos) {
|
||||
librariesLoaded.push_back(directory + "/" + lec);
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
||||
|
246
Core/GDCore/IDE/ObjectAssetSerializer.cpp
Normal file
246
Core/GDCore/IDE/ObjectAssetSerializer.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "ObjectAssetSerializer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
|
||||
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/Project/AssetResourcePathCleaner.h"
|
||||
#include "GDCore/IDE/Project/ResourcesInUseHelper.h"
|
||||
#include "GDCore/IDE/Project/ResourcesRenamer.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/CustomBehavior.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/PropertyDescriptor.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String
|
||||
ObjectAssetSerializer::GetObjectExtensionName(const gd::Object &object) {
|
||||
const gd::String &type = object.GetType();
|
||||
const auto separatorIndex =
|
||||
type.find(PlatformExtension::GetNamespaceSeparator());
|
||||
return separatorIndex != std::string::npos ? type.substr(0, separatorIndex)
|
||||
: "";
|
||||
}
|
||||
|
||||
void ObjectAssetSerializer::SerializeTo(
|
||||
gd::Project &project, const gd::Object &object,
|
||||
const gd::String &objectFullName, SerializerElement &element,
|
||||
std::map<gd::String, std::vector<gd::String>> &resourcesFileNameMap) {
|
||||
auto cleanObject = object.Clone();
|
||||
cleanObject->GetVariables().Clear();
|
||||
cleanObject->GetEffects().Clear();
|
||||
for (auto &&behaviorName : cleanObject->GetAllBehaviorNames()) {
|
||||
cleanObject->RemoveBehavior(behaviorName);
|
||||
}
|
||||
|
||||
gd::String extensionName = GetObjectExtensionName(*cleanObject);
|
||||
|
||||
std::map<gd::String, gd::String> resourcesNameReverseMap;
|
||||
gd::ObjectAssetSerializer::RenameObjectResourceFiles(
|
||||
project, *cleanObject, "", objectFullName, resourcesFileNameMap,
|
||||
resourcesNameReverseMap);
|
||||
|
||||
element.SetAttribute("id", "");
|
||||
element.SetAttribute("name", "");
|
||||
element.SetAttribute("license", "");
|
||||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
auto &extension = project.GetEventsFunctionsExtension(extensionName);
|
||||
element.SetAttribute("description", extension.GetShortDescription());
|
||||
}
|
||||
element.SetAttribute("gdevelopVersion", "");
|
||||
element.SetAttribute("version", "");
|
||||
element.SetIntAttribute("animationsCount", 1);
|
||||
element.SetIntAttribute("maxFramesCount", 1);
|
||||
// TODO Find the right object dimensions.
|
||||
element.SetIntAttribute("width", 0);
|
||||
element.SetIntAttribute("height", 0);
|
||||
SerializerElement &authorsElement = element.AddChild("authors");
|
||||
authorsElement.ConsiderAsArrayOf("author");
|
||||
SerializerElement &tagsElement = element.AddChild("tags");
|
||||
tagsElement.ConsiderAsArrayOf("tag");
|
||||
|
||||
SerializerElement &objectAssetsElement = element.AddChild("objectAssets");
|
||||
objectAssetsElement.ConsiderAsArrayOf("objectAsset");
|
||||
SerializerElement &objectAssetElement =
|
||||
objectAssetsElement.AddChild("objectAsset");
|
||||
|
||||
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
|
||||
|
||||
SerializerElement &resourcesElement =
|
||||
objectAssetElement.AddChild("resources");
|
||||
resourcesElement.ConsiderAsArrayOf("resource");
|
||||
auto &resourcesManager = project.GetResourcesManager();
|
||||
gd::ResourcesInUseHelper resourcesInUse(resourcesManager);
|
||||
cleanObject->GetConfiguration().ExposeResources(resourcesInUse);
|
||||
for (auto &&newResourceName : resourcesInUse.GetAllResources()) {
|
||||
if (newResourceName.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
auto &resource = resourcesManager.GetResource(
|
||||
resourcesNameReverseMap.find(newResourceName) !=
|
||||
resourcesNameReverseMap.end()
|
||||
? resourcesNameReverseMap[newResourceName]
|
||||
: newResourceName);
|
||||
SerializerElement &resourceElement = resourcesElement.AddChild("resource");
|
||||
resource.SerializeTo(resourceElement);
|
||||
// Override name and file because the project and the asset don't use the
|
||||
// same one.
|
||||
resourceElement.SetAttribute("kind", resource.GetKind());
|
||||
resourceElement.SetAttribute("name", newResourceName);
|
||||
auto &oldFilePath = resource.GetFile();
|
||||
resourceElement.SetAttribute("file", newResourceName);
|
||||
}
|
||||
|
||||
SerializerElement &requiredExtensionsElement =
|
||||
objectAssetElement.AddChild("requiredExtensions");
|
||||
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
|
||||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
SerializerElement &requiredExtensionElement =
|
||||
requiredExtensionsElement.AddChild("requiredExtension");
|
||||
requiredExtensionElement.SetAttribute("extensionName", extensionName);
|
||||
requiredExtensionElement.SetAttribute("extensionVersion", "1.0.0");
|
||||
}
|
||||
|
||||
// TODO This can be removed when the asset script no longer require it.
|
||||
SerializerElement &customizationElement =
|
||||
objectAssetElement.AddChild("customization");
|
||||
customizationElement.ConsiderAsArrayOf("empty");
|
||||
}
|
||||
|
||||
void ObjectAssetSerializer::RenameObjectResourceFiles(
|
||||
gd::Project &project, gd::Object &object,
|
||||
const gd::String &destinationDirectory, const gd::String &objectFullName,
|
||||
std::map<gd::String, std::vector<gd::String>> &resourcesFileNameMap,
|
||||
std::map<gd::String, gd::String> &resourcesNameReverseMap) {
|
||||
std::map<gd::String, gd::String> cleanedResourcesFileNameMap;
|
||||
gd::AssetResourcePathCleaner assetResourcePathCleaner(
|
||||
project.GetResourcesManager(), cleanedResourcesFileNameMap,
|
||||
resourcesNameReverseMap);
|
||||
object.GetConfiguration().ExposeResources(assetResourcePathCleaner);
|
||||
|
||||
// Use asset store script naming conventions for sprite resource files.
|
||||
if (object.GetConfiguration().GetType() == "Sprite") {
|
||||
gd::SpriteObject &spriteConfiguration =
|
||||
dynamic_cast<gd::SpriteObject &>(object.GetConfiguration());
|
||||
/// Resource files may be duplicated because the files names allow the
|
||||
/// asset store script to rebuild the animations.
|
||||
std::map<gd::String, std::vector<gd::String>> normalizedFileNames;
|
||||
|
||||
for (std::size_t animationIndex = 0;
|
||||
animationIndex < spriteConfiguration.GetAnimationsCount();
|
||||
animationIndex++) {
|
||||
auto &animation = spriteConfiguration.GetAnimation(animationIndex);
|
||||
auto &direction = animation.GetDirection(0);
|
||||
|
||||
const gd::String &animationName =
|
||||
animation.GetName().empty()
|
||||
? gd::String::From(animationIndex)
|
||||
: animation.GetName().FindAndReplace("_", " ", true);
|
||||
|
||||
// Search frames that share the same resource.
|
||||
std::map<gd::String, std::vector<int>> frameIndexes;
|
||||
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount();
|
||||
frameIndex++) {
|
||||
auto &frame = direction.GetSprite(frameIndex);
|
||||
|
||||
if (frameIndexes.find(frame.GetImageName()) == frameIndexes.end()) {
|
||||
std::vector<int> emptyVector;
|
||||
frameIndexes[frame.GetImageName()] = emptyVector;
|
||||
}
|
||||
auto &indexes = frameIndexes[frame.GetImageName()];
|
||||
indexes.push_back(frameIndex);
|
||||
}
|
||||
|
||||
for (std::size_t frameIndex = 0; frameIndex < direction.GetSpritesCount();
|
||||
frameIndex++) {
|
||||
auto &frame = direction.GetSprite(frameIndex);
|
||||
auto oldName = frame.GetImageName();
|
||||
|
||||
if (normalizedFileNames.find(oldName) == normalizedFileNames.end()) {
|
||||
std::vector<gd::String> value;
|
||||
normalizedFileNames[oldName] = value;
|
||||
}
|
||||
|
||||
gd::String newName = objectFullName;
|
||||
if (spriteConfiguration.GetAnimationsCount() > 1) {
|
||||
newName += "_" + animationName;
|
||||
}
|
||||
if (direction.GetSpritesCount() > 1) {
|
||||
newName += "_";
|
||||
auto &indexes = frameIndexes[frame.GetImageName()];
|
||||
for (size_t i = 0; i < indexes.size(); i++) {
|
||||
newName += gd::String::From(indexes.at(i) + 1);
|
||||
if (i < indexes.size() - 1) {
|
||||
newName += ";";
|
||||
}
|
||||
}
|
||||
}
|
||||
gd::String extension = oldName.substr(oldName.find_last_of("."));
|
||||
newName += extension;
|
||||
|
||||
frame.SetImageName(newName);
|
||||
auto &newNames = normalizedFileNames[oldName];
|
||||
if (find(newNames.begin(), newNames.end(), newName) == newNames.end()) {
|
||||
newNames.push_back(newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::map<gd::String, gd::String>::const_iterator it =
|
||||
cleanedResourcesFileNameMap.begin();
|
||||
it != cleanedResourcesFileNameMap.end(); ++it) {
|
||||
if (!it->first.empty()) {
|
||||
const gd::String &originFile = it->first;
|
||||
const gd::String &destinationFile = it->second;
|
||||
|
||||
std::vector<gd::String> value;
|
||||
for (auto &&normalizedFileName : normalizedFileNames[destinationFile]) {
|
||||
value.push_back(normalizedFileName);
|
||||
}
|
||||
resourcesFileNameMap[originFile] = value;
|
||||
}
|
||||
}
|
||||
auto clonedResourcesNameReverseMap = resourcesNameReverseMap;
|
||||
resourcesNameReverseMap.clear();
|
||||
for (std::map<gd::String, gd::String>::const_iterator it =
|
||||
clonedResourcesNameReverseMap.begin();
|
||||
it != clonedResourcesNameReverseMap.end(); ++it) {
|
||||
if (!it->first.empty()) {
|
||||
const gd::String& newResourceName = it->first;
|
||||
const gd::String& oldResourceName = it->second;
|
||||
for (auto&& normalizedFileName : normalizedFileNames[newResourceName]) {
|
||||
resourcesNameReverseMap[normalizedFileName] =
|
||||
oldResourceName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (std::map<gd::String, gd::String>::const_iterator it =
|
||||
cleanedResourcesFileNameMap.begin();
|
||||
it != cleanedResourcesFileNameMap.end(); ++it) {
|
||||
if (!it->first.empty()) {
|
||||
const gd::String &originFile = it->first;
|
||||
const gd::String &destinationFile = it->second;
|
||||
|
||||
std::vector<gd::String> value;
|
||||
value.push_back(destinationFile);
|
||||
resourcesFileNameMap[originFile] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace gd
|
66
Core/GDCore/IDE/ObjectAssetSerializer.h
Normal file
66
Core/GDCore/IDE/ObjectAssetSerializer.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Object;
|
||||
class ExtensionDependency;
|
||||
class PropertyDescriptor;
|
||||
class Project;
|
||||
class Layout;
|
||||
class ArbitraryResourceWorker;
|
||||
class InitialInstance;
|
||||
class SerializerElement;
|
||||
class EffectsContainer;
|
||||
class AbstractFileSystem;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Serialize objects into an asset for the store.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API ObjectAssetSerializer {
|
||||
public:
|
||||
/**
|
||||
* \brief Serialize an object into an asset.
|
||||
*
|
||||
* \param project The project that contains the object and its resources.
|
||||
* It's not actually modified.
|
||||
* \param object The object to serialize as an asset.
|
||||
* \param objectFullName The object name with spaces instead of PascalCase.
|
||||
* \param element The element where the asset is serialize.
|
||||
* \param resourcesFileNameMap The map from project resource file paths to
|
||||
* asset resource file paths. Resource files of Sprite objects may be
|
||||
* duplicated because the files names allow the asset store script to rebuild
|
||||
* the animations.
|
||||
*/
|
||||
static void
|
||||
SerializeTo(gd::Project &project, const gd::Object &object,
|
||||
const gd::String &objectFullName, SerializerElement &element,
|
||||
std::map<gd::String, std::vector<gd::String>> &resourcesFileNameMap);
|
||||
|
||||
~ObjectAssetSerializer(){};
|
||||
|
||||
private:
|
||||
ObjectAssetSerializer(){};
|
||||
|
||||
static void RenameObjectResourceFiles(
|
||||
gd::Project &project, gd::Object &object,
|
||||
const gd::String &destinationDirectory, const gd::String &objectFullName,
|
||||
std::map<gd::String, std::vector<gd::String>> &resourcesFileNameMap,
|
||||
std::map<gd::String, gd::String> &resourcesNameReverseMap);
|
||||
|
||||
static gd::String GetObjectExtensionName(const gd::Object &object);
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -52,6 +52,16 @@ void ArbitraryResourceWorker::ExposeModel3D(gd::String& resourceName){
|
||||
// do.
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeAtlas(gd::String& resourceName){
|
||||
// Nothing to do by default - each child class can define here the action to
|
||||
// do.
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeSpine(gd::String& resourceName){
|
||||
// Nothing to do by default - each child class can define here the action to
|
||||
// do.
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
|
||||
// Nothing to do by default - each child class can define here the action to
|
||||
// do.
|
||||
@@ -63,14 +73,10 @@ void ArbitraryResourceWorker::ExposeBitmapFont(gd::String& bitmapFontName){
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
|
||||
for (auto resources : GetResources()) {
|
||||
if (!resources) continue;
|
||||
|
||||
if (resources->HasResource(audioName) &&
|
||||
resources->GetResource(audioName).GetKind() == "audio") {
|
||||
// Nothing to do, the audio is a reference to a proper resource.
|
||||
return;
|
||||
}
|
||||
if (resourcesManager->HasResource(audioName) &&
|
||||
resourcesManager->GetResource(audioName).GetKind() == "audio") {
|
||||
// Nothing to do, the audio is a reference to a proper resource.
|
||||
return;
|
||||
}
|
||||
|
||||
// For compatibility with older projects (where events were referring to files
|
||||
@@ -80,14 +86,10 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
|
||||
for (auto resources : GetResources()) {
|
||||
if (!resources) continue;
|
||||
|
||||
if (resources->HasResource(fontName) &&
|
||||
resources->GetResource(fontName).GetKind() == "font") {
|
||||
// Nothing to do, the font is a reference to a proper resource.
|
||||
return;
|
||||
}
|
||||
if (resourcesManager->HasResource(fontName) &&
|
||||
resourcesManager->GetResource(fontName).GetKind() == "font") {
|
||||
// Nothing to do, the font is a reference to a proper resource.
|
||||
return;
|
||||
}
|
||||
|
||||
// For compatibility with older projects (where events were referring to files
|
||||
@@ -96,12 +98,7 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
|
||||
ExposeFile(fontName);
|
||||
};
|
||||
|
||||
void ArbitraryResourceWorker::ExposeResources(
|
||||
gd::ResourcesManager* resourcesManager) {
|
||||
if (!resourcesManager) return;
|
||||
|
||||
resourcesManagers.push_back(resourcesManager);
|
||||
|
||||
void ArbitraryResourceWorker::ExposeResources() {
|
||||
std::vector<gd::String> resources = resourcesManager->GetAllResourceNames();
|
||||
for (std::size_t i = 0; i < resources.size(); i++) {
|
||||
if (resourcesManager->GetResource(resources[i]).UseFile())
|
||||
@@ -110,9 +107,6 @@ void ArbitraryResourceWorker::ExposeResources(
|
||||
}
|
||||
|
||||
void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
|
||||
if (resourcesManagers.empty()) return;
|
||||
gd::ResourcesManager* resourcesManager = resourcesManagers[0];
|
||||
|
||||
gd::Resource& resource = resourcesManager->GetResource(resourceName);
|
||||
|
||||
if (!resource.GetMetadata().empty()) {
|
||||
@@ -136,6 +130,7 @@ void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
|
||||
|
||||
gd::String potentiallyUpdatedTargetResourceName = targetResourceName;
|
||||
ExposeResourceWithType(targetResource.GetKind(), potentiallyUpdatedTargetResourceName);
|
||||
ExposeEmbeddeds(potentiallyUpdatedTargetResourceName);
|
||||
|
||||
if (potentiallyUpdatedTargetResourceName != targetResourceName) {
|
||||
// The resource name was renamed. Also update the mapping.
|
||||
@@ -176,6 +171,7 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
|
||||
}
|
||||
if (resourceType == "tilemap") {
|
||||
ExposeTilemap(resourceName);
|
||||
ExposeEmbeddeds(resourceName);
|
||||
return;
|
||||
}
|
||||
if (resourceType == "tileset") {
|
||||
@@ -184,12 +180,21 @@ void ArbitraryResourceWorker::ExposeResourceWithType(
|
||||
}
|
||||
if (resourceType == "json") {
|
||||
ExposeJson(resourceName);
|
||||
ExposeEmbeddeds(resourceName);
|
||||
return;
|
||||
}
|
||||
if (resourceType == "video") {
|
||||
ExposeVideo(resourceName);
|
||||
return;
|
||||
}
|
||||
if (resourceType == "atlas") {
|
||||
ExposeAtlas(resourceName);
|
||||
return;
|
||||
}
|
||||
if (resourceType == "spine") {
|
||||
ExposeSpine(resourceName);
|
||||
return;
|
||||
}
|
||||
gd::LogError("Unexpected resource type: " + resourceType + " for: " + resourceName);
|
||||
return;
|
||||
}
|
||||
@@ -243,10 +248,12 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
} else if (parameterMetadata.GetType() == "jsonResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeJson(updatedParameterValue);
|
||||
worker.ExposeEmbeddeds(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
} else if (parameterMetadata.GetType() == "tilemapResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeTilemap(updatedParameterValue);
|
||||
worker.ExposeEmbeddeds(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
} else if (parameterMetadata.GetType() == "tilesetResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
@@ -256,19 +263,20 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeModel3D(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
} else if (parameterMetadata.GetType() == "atlasResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeAtlas(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
} else if (parameterMetadata.GetType() == "spineResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
worker.ExposeSpine(updatedParameterValue);
|
||||
instruction.SetParameter(parameterIndex, updatedParameterValue);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
void LaunchResourceWorkerOnEvents(const gd::Project& project,
|
||||
gd::EventsList& events,
|
||||
gd::ArbitraryResourceWorker& worker) {
|
||||
gd::ResourceWorkerInEventsWorker eventsWorker(project, worker);
|
||||
eventsWorker.Launch(events);
|
||||
}
|
||||
|
||||
gd::ResourceWorkerInEventsWorker
|
||||
GetResourceWorkerOnEvents(const gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
@@ -293,8 +301,8 @@ void ResourceWorkerInObjectsWorker::DoVisitBehavior(gd::Behavior &behavior){
|
||||
gd::ResourceWorkerInObjectsWorker
|
||||
GetResourceWorkerOnObjects(const gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
gd::ResourceWorkerInObjectsWorker eventsWorker(project, worker);
|
||||
return eventsWorker;
|
||||
gd::ResourceWorkerInObjectsWorker resourcesWorker(project, worker);
|
||||
return resourcesWorker;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -37,13 +37,14 @@ namespace gd {
|
||||
* \see ResourcesMergingHelper
|
||||
* \see gd::ResourcesInUseHelper
|
||||
*
|
||||
* \see gd::LaunchResourceWorkerOnEvents
|
||||
* \see gd::GetResourceWorkerOnEvents
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API ArbitraryResourceWorker {
|
||||
public:
|
||||
ArbitraryResourceWorker(){};
|
||||
public:
|
||||
ArbitraryResourceWorker(gd::ResourcesManager &resourcesManager_)
|
||||
: resourcesManager(&resourcesManager_){};
|
||||
virtual ~ArbitraryResourceWorker();
|
||||
|
||||
/**
|
||||
@@ -52,7 +53,7 @@ class GD_CORE_API ArbitraryResourceWorker {
|
||||
* first to ensure that resources are known so that images, shaders & audio
|
||||
* can make reference to them.
|
||||
*/
|
||||
void ExposeResources(gd::ResourcesManager *resourcesManager);
|
||||
void ExposeResources();
|
||||
|
||||
/**
|
||||
* \brief Expose a resource from a given type.
|
||||
@@ -95,6 +96,16 @@ class GD_CORE_API ArbitraryResourceWorker {
|
||||
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
|
||||
*/
|
||||
virtual void ExposeModel3D(gd::String &resourceName);
|
||||
|
||||
/**
|
||||
* \brief Expose an atlas, which is always a reference to a "atlas" resource.
|
||||
*/
|
||||
virtual void ExposeAtlas(gd::String &resourceName);
|
||||
|
||||
/**
|
||||
* \brief Expose an spine, which is always a reference to a "spine" resource.
|
||||
*/
|
||||
virtual void ExposeSpine(gd::String &resourceName);
|
||||
|
||||
/**
|
||||
* \brief Expose a video, which is always a reference to a "video" resource.
|
||||
@@ -122,10 +133,8 @@ class GD_CORE_API ArbitraryResourceWorker {
|
||||
*/
|
||||
virtual void ExposeEmbeddeds(gd::String &resourceName);
|
||||
|
||||
protected:
|
||||
const std::vector<gd::ResourcesManager *> &GetResources() {
|
||||
return resourcesManagers;
|
||||
};
|
||||
protected:
|
||||
gd::ResourcesManager * resourcesManager;
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -133,8 +142,6 @@ class GD_CORE_API ArbitraryResourceWorker {
|
||||
* exposed as file (see ExposeFile).
|
||||
*/
|
||||
void ExposeResource(gd::Resource &resource);
|
||||
|
||||
std::vector<gd::ResourcesManager *> resourcesManagers;
|
||||
};
|
||||
|
||||
/**
|
||||
|
74
Core/GDCore/IDE/Project/AssetResourcePathCleaner.cpp
Normal file
74
Core/GDCore/IDE/Project/AssetResourcePathCleaner.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "AssetResourcePathCleaner.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
void AssetResourcePathCleaner::ExposeImage(gd::String &imageName) {
|
||||
ExposeResourceAsFile(imageName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeAudio(gd::String &audioName) {
|
||||
ExposeResourceAsFile(audioName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeFont(gd::String &fontName) {
|
||||
ExposeResourceAsFile(fontName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeJson(gd::String &jsonName) {
|
||||
ExposeResourceAsFile(jsonName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeTilemap(gd::String &tilemapName) {
|
||||
ExposeResourceAsFile(tilemapName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeTileset(gd::String &tilesetName) {
|
||||
ExposeResourceAsFile(tilesetName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeVideo(gd::String &videoName) {
|
||||
ExposeResourceAsFile(videoName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeBitmapFont(gd::String &bitmapFontName) {
|
||||
ExposeResourceAsFile(bitmapFontName);
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeResourceAsFile(gd::String &resourceName) {
|
||||
|
||||
auto &resource = resourcesManager->GetResource(resourceName);
|
||||
gd::String file = resource.GetFile();
|
||||
ExposeFile(file);
|
||||
|
||||
resourcesNameReverseMap[file] = resourceName;
|
||||
resourceName = file;
|
||||
}
|
||||
|
||||
void AssetResourcePathCleaner::ExposeFile(gd::String &resourceFilePath) {
|
||||
|
||||
size_t slashPos = resourceFilePath.find_last_of("/");
|
||||
size_t antiSlashPos = resourceFilePath.find_last_of("\\");
|
||||
size_t baseNamePos =
|
||||
slashPos == String::npos
|
||||
? antiSlashPos == String::npos ? 0 : (antiSlashPos + 1)
|
||||
: antiSlashPos == String::npos ? (slashPos + 1)
|
||||
: slashPos > antiSlashPos ? (slashPos + 1)
|
||||
: (antiSlashPos + 1);
|
||||
gd::String baseName =
|
||||
baseNamePos != 0
|
||||
? resourceFilePath.substr(baseNamePos, resourceFilePath.length())
|
||||
: resourceFilePath;
|
||||
|
||||
resourcesFileNameMap[resourceFilePath] = baseName;
|
||||
resourceFilePath = baseName;
|
||||
}
|
||||
|
||||
} // namespace gd
|
65
Core/GDCore/IDE/Project/AssetResourcePathCleaner.h
Normal file
65
Core/GDCore/IDE/Project/AssetResourcePathCleaner.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
|
||||
#include "GDCore/String.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace gd {
|
||||
class AbstractFileSystem;
|
||||
class Project;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief AssetResourcePathCleaner is used when exporting an object as an asset.
|
||||
* It removes the folder from the path.
|
||||
*
|
||||
* \see ArbitraryResourceWorker
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API AssetResourcePathCleaner : public ArbitraryResourceWorker {
|
||||
public:
|
||||
AssetResourcePathCleaner(
|
||||
gd::ResourcesManager &resourcesManager,
|
||||
std::map<gd::String, gd::String> &resourcesFileNameMap_,
|
||||
std::map<gd::String, gd::String> &resourcesNameReverseMap_)
|
||||
: ArbitraryResourceWorker(resourcesManager),
|
||||
resourcesFileNameMap(resourcesFileNameMap_),
|
||||
resourcesNameReverseMap(resourcesNameReverseMap_){};
|
||||
virtual ~AssetResourcePathCleaner(){};
|
||||
|
||||
void ExposeImage(gd::String &imageName) override;
|
||||
void ExposeAudio(gd::String &audioName) override;
|
||||
void ExposeFont(gd::String &fontName) override;
|
||||
void ExposeJson(gd::String &jsonName) override;
|
||||
void ExposeTilemap(gd::String &tilemapName) override;
|
||||
void ExposeTileset(gd::String &tilesetName) override;
|
||||
void ExposeVideo(gd::String &videoName) override;
|
||||
void ExposeBitmapFont(gd::String &bitmapFontName) override;
|
||||
void ExposeFile(gd::String &resource) override;
|
||||
|
||||
protected:
|
||||
void ExposeResourceAsFile(gd::String &resourceName);
|
||||
|
||||
/**
|
||||
* New file names that can be accessed by their original name.
|
||||
*/
|
||||
std::map<gd::String, gd::String> &resourcesFileNameMap;
|
||||
|
||||
/**
|
||||
* Original resource names that can be accessed by their new name.
|
||||
*/
|
||||
std::map<gd::String, gd::String> &resourcesNameReverseMap;
|
||||
};
|
||||
|
||||
} // namespace gd
|
19
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.cpp
Normal file
19
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "ObjectsUsingResourceCollector.h"
|
||||
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void ObjectsUsingResourceCollector::DoVisitObject(gd::Object& object) {
|
||||
gd::ResourceNameMatcher resourceNameMatcher(*resourcesManager, resourceName);
|
||||
|
||||
object.GetConfiguration().ExposeResources(resourceNameMatcher);
|
||||
if (resourceNameMatcher.AnyResourceMatches()) {
|
||||
objectNames.push_back(object.GetName());
|
||||
}
|
||||
};
|
||||
|
||||
ObjectsUsingResourceCollector::~ObjectsUsingResourceCollector() {}
|
||||
|
||||
} // namespace gd
|
97
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Normal file
97
Core/GDCore/IDE/Project/ObjectsUsingResourceCollector.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 <vector>
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Object;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API ObjectsUsingResourceCollector
|
||||
: public ArbitraryObjectsWorker {
|
||||
public:
|
||||
ObjectsUsingResourceCollector(gd::ResourcesManager &resourcesManager_,
|
||||
const gd::String &resourceName_)
|
||||
: resourcesManager(&resourcesManager_), resourceName(resourceName_){};
|
||||
virtual ~ObjectsUsingResourceCollector();
|
||||
|
||||
std::vector<gd::String>& GetObjectNames() { return objectNames; }
|
||||
|
||||
private:
|
||||
void DoVisitObject(gd::Object& object) override;
|
||||
|
||||
std::vector<gd::String> objectNames;
|
||||
gd::String resourceName;
|
||||
gd::ResourcesManager *resourcesManager;
|
||||
};
|
||||
|
||||
class GD_CORE_API ResourceNameMatcher : public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourceNameMatcher(gd::ResourcesManager &resourcesManager,
|
||||
const gd::String &resourceName_)
|
||||
: resourceName(resourceName_),
|
||||
matchesResourceName(false), gd::ArbitraryResourceWorker(
|
||||
resourcesManager){};
|
||||
virtual ~ResourceNameMatcher(){};
|
||||
|
||||
bool AnyResourceMatches() { return matchesResourceName; }
|
||||
void Reset() { matchesResourceName = false; }
|
||||
|
||||
private:
|
||||
virtual void ExposeFile(gd::String& resource) override{
|
||||
/*Don't care, we just read resource names*/
|
||||
};
|
||||
virtual void ExposeImage(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeAudio(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeFont(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeJson(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeTilemap(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeTileset(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeVideo(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeBitmapFont(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeModel3D(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeAtlas(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
virtual void ExposeSpine(gd::String& otherResourceName) override {
|
||||
MatchResourceName(otherResourceName);
|
||||
};
|
||||
|
||||
void MatchResourceName(gd::String& otherResourceName) {
|
||||
if (otherResourceName == resourceName) matchesResourceName = true;
|
||||
}
|
||||
|
||||
gd::String resourceName;
|
||||
bool matchesResourceName;
|
||||
};
|
||||
|
||||
}; // namespace gd
|
@@ -18,8 +18,9 @@ namespace gd {
|
||||
std::vector<gd::String> ProjectResourcesAdder::GetAllUseless(
|
||||
gd::Project& project, const gd::String& resourceType) {
|
||||
std::vector<gd::String> unusedResources;
|
||||
|
||||
// Search for resources used in the project
|
||||
gd::ResourcesInUseHelper resourcesInUse;
|
||||
gd::ResourcesInUseHelper resourcesInUse(project.GetResourcesManager());
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(project, resourcesInUse);
|
||||
std::set<gd::String>& usedResources = resourcesInUse.GetAll(resourceType);
|
||||
|
||||
|
@@ -25,8 +25,29 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
bool updateOriginalProject,
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
if (updateOriginalProject) {
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, originalProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
} else {
|
||||
gd::Project clonedProject = originalProject;
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, clonedProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
|
||||
// Check if there are some resources with absolute filenames
|
||||
gd::ResourcesAbsolutePathChecker absolutePathChecker(fs);
|
||||
gd::ResourcesAbsolutePathChecker absolutePathChecker(originalProject.GetResourcesManager(), fs);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, absolutePathChecker);
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
|
||||
@@ -34,24 +55,18 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
<< destinationDirectory << "..." << std::endl;
|
||||
|
||||
// Get the resources to be copied
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(fs);
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
clonedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
resourcesMergingHelper.PreserveDirectoriesStructure(
|
||||
preserveDirectoryStructure);
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(
|
||||
preserveAbsoluteFilenames);
|
||||
|
||||
if (updateOriginalProject) {
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, resourcesMergingHelper);
|
||||
} else {
|
||||
std::shared_ptr<gd::Project> project(new gd::Project(originalProject));
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(*project, resourcesMergingHelper);
|
||||
}
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(preserveAbsoluteFilenames);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
// Copy resources
|
||||
map<gd::String, gd::String>& resourcesNewFilename =
|
||||
resourcesMergingHelper.GetAllResourcesOldAndNewFilename();
|
||||
unsigned int i = 0;
|
||||
for (map<gd::String, gd::String>::const_iterator it =
|
||||
resourcesNewFilename.begin();
|
||||
it != resourcesNewFilename.end();
|
||||
@@ -71,8 +86,6 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
destinationFile + _("\"."));
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -3,9 +3,10 @@
|
||||
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef PROJECTRESOURCESCOPIER_H
|
||||
#define PROJECTRESOURCESCOPIER_H
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class AbstractFileSystem;
|
||||
@@ -47,8 +48,14 @@ class GD_CORE_API ProjectResourcesCopier {
|
||||
bool updateOriginalProject,
|
||||
bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
|
||||
private:
|
||||
static bool CopyAllResourcesTo(gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
gd::AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // PROJECTRESOURCESCOPIER_H
|
||||
|
@@ -3,8 +3,7 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef RESOURCESABSOLUTEPATHCHECKER_H
|
||||
#define RESOURCESABSOLUTEPATHCHECKER_H
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
@@ -22,10 +21,10 @@ namespace gd {
|
||||
*/
|
||||
class GD_CORE_API ResourcesAbsolutePathChecker
|
||||
: public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesAbsolutePathChecker(AbstractFileSystem& fileSystem)
|
||||
: ArbitraryResourceWorker(),
|
||||
hasAbsoluteFilenames(false),
|
||||
public:
|
||||
ResourcesAbsolutePathChecker(gd::ResourcesManager &resourcesManager,
|
||||
AbstractFileSystem &fileSystem)
|
||||
: ArbitraryResourceWorker(resourcesManager), hasAbsoluteFilenames(false),
|
||||
fs(fileSystem){};
|
||||
virtual ~ResourcesAbsolutePathChecker(){};
|
||||
|
||||
@@ -47,5 +46,3 @@ class GD_CORE_API ResourcesAbsolutePathChecker
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // RESOURCESABSOLUTEPATHCHECKER_H
|
||||
|
25
Core/GDCore/IDE/Project/ResourcesInUseHelper.cpp
Normal file
25
Core/GDCore/IDE/Project/ResourcesInUseHelper.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "ResourcesInUseHelper.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
const std::vector<gd::String> ResourcesInUseHelper::resourceTypes = {
|
||||
"image", "audio", "font", "json", "tilemap",
|
||||
"tileset", "video", "bitmapFont", "model3D"};
|
||||
|
||||
const std::vector<gd::String> &ResourcesInUseHelper::GetAllResources() {
|
||||
allResources.clear();
|
||||
for (auto &&resourceType : gd::ResourcesInUseHelper::resourceTypes) {
|
||||
for (auto &&resourceName : GetAll(resourceType)) {
|
||||
allResources.push_back(resourceName);
|
||||
}
|
||||
}
|
||||
return allResources;
|
||||
}
|
||||
|
||||
} // namespace gd
|
@@ -4,9 +4,7 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#ifndef IMAGESUSEDINVENTORIZER_H
|
||||
#define IMAGESUSEDINVENTORIZER_H
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@@ -33,10 +31,12 @@ std::set<gd::String> & usedImages = resourcesInUse.GetAllImages();
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesInUseHelper() : gd::ArbitraryResourceWorker(){};
|
||||
public:
|
||||
ResourcesInUseHelper(gd::ResourcesManager &resourcesManager)
|
||||
: gd::ArbitraryResourceWorker(resourcesManager){};
|
||||
virtual ~ResourcesInUseHelper(){};
|
||||
|
||||
const std::vector<gd::String>& GetAllResources();
|
||||
std::set<gd::String>& GetAllImages() { return GetAll("image"); };
|
||||
std::set<gd::String>& GetAllAudios() { return GetAll("audio"); };
|
||||
std::set<gd::String>& GetAllFonts() { return GetAll("font"); };
|
||||
@@ -46,6 +46,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
std::set<gd::String>& GetAllVideos() { return GetAll("video"); };
|
||||
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
|
||||
std::set<gd::String>& GetAll3DModels() { return GetAll("model3D"); };
|
||||
std::set<gd::String>& GetAllAtlases() { return GetAll("atlas"); };
|
||||
std::set<gd::String>& GetAllSpines() { return GetAll("spine"); };
|
||||
std::set<gd::String>& GetAll(const gd::String& resourceType) {
|
||||
if (resourceType == "image") return allImages;
|
||||
if (resourceType == "audio") return allAudios;
|
||||
@@ -56,6 +58,8 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
if (resourceType == "video") return allVideos;
|
||||
if (resourceType == "bitmapFont") return allBitmapFonts;
|
||||
if (resourceType == "model3D") return allModel3Ds;
|
||||
if (resourceType == "atlas") return allAtlases;
|
||||
if (resourceType == "spine") return allSpines;
|
||||
|
||||
return emptyResources;
|
||||
};
|
||||
@@ -63,35 +67,42 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
virtual void ExposeFile(gd::String& resource) override{
|
||||
/*Don't care, we just list resource names*/
|
||||
};
|
||||
virtual void ExposeImage(gd::String& imageResourceName) override {
|
||||
allImages.insert(imageResourceName);
|
||||
virtual void ExposeImage(gd::String& resourceName) override {
|
||||
allImages.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeAudio(gd::String& audioResourceName) override {
|
||||
allAudios.insert(audioResourceName);
|
||||
virtual void ExposeAudio(gd::String& resourceName) override {
|
||||
allAudios.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeFont(gd::String& fontResourceName) override {
|
||||
allFonts.insert(fontResourceName);
|
||||
virtual void ExposeFont(gd::String& resourceName) override {
|
||||
allFonts.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeJson(gd::String& jsonResourceName) override {
|
||||
allJsons.insert(jsonResourceName);
|
||||
virtual void ExposeJson(gd::String& resourceName) override {
|
||||
allJsons.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeTilemap(gd::String& tilemapResourceName) override {
|
||||
allTilemaps.insert(tilemapResourceName);
|
||||
virtual void ExposeTilemap(gd::String& resourceName) override {
|
||||
allTilemaps.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeTileset(gd::String& tilesetResourceName) override {
|
||||
allTilesets.insert(tilesetResourceName);
|
||||
virtual void ExposeTileset(gd::String& resourceName) override {
|
||||
allTilesets.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeVideo(gd::String& videoResourceName) override {
|
||||
allVideos.insert(videoResourceName);
|
||||
virtual void ExposeVideo(gd::String& resourceName) override {
|
||||
allVideos.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeBitmapFont(gd::String& bitmapFontResourceName) override {
|
||||
allBitmapFonts.insert(bitmapFontResourceName);
|
||||
virtual void ExposeBitmapFont(gd::String& resourceName) override {
|
||||
allBitmapFonts.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeModel3D(gd::String& resourceName) override {
|
||||
allModel3Ds.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeAtlas(gd::String& resourceName) override {
|
||||
allAtlases.insert(resourceName);
|
||||
};
|
||||
virtual void ExposeSpine(gd::String& resourceName) override {
|
||||
allSpines.insert(resourceName);
|
||||
};
|
||||
|
||||
protected:
|
||||
std::vector<gd::String> allResources;
|
||||
std::set<gd::String> allImages;
|
||||
std::set<gd::String> allAudios;
|
||||
std::set<gd::String> allFonts;
|
||||
@@ -101,10 +112,11 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
|
||||
std::set<gd::String> allVideos;
|
||||
std::set<gd::String> allBitmapFonts;
|
||||
std::set<gd::String> allModel3Ds;
|
||||
std::set<gd::String> allAtlases;
|
||||
std::set<gd::String> allSpines;
|
||||
std::set<gd::String> emptyResources;
|
||||
|
||||
static const std::vector<gd::String> resourceTypes;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // IMAGESUSEDINVENTORIZER_H
|
||||
#endif
|
||||
|
@@ -28,7 +28,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
|
||||
auto stripToFilenameOnly = [&]() {
|
||||
fs.MakeAbsolute(resourceFullFilename, baseDirectory);
|
||||
SetNewFilename(resourceFullFilename, fs.FileNameFrom(resourceFullFilename));
|
||||
resourceFilename = oldFilenames[resourceFullFilename];
|
||||
resourceFilename = newFilenames[resourceFullFilename];
|
||||
};
|
||||
|
||||
// if we do not want to preserve the folders at all,
|
||||
@@ -45,7 +45,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
|
||||
gd::String relativeFilename = resourceFullFilename;
|
||||
if (fs.MakeRelative(relativeFilename, baseDirectory)) {
|
||||
SetNewFilename(resourceFullFilename, relativeFilename);
|
||||
resourceFilename = oldFilenames[resourceFullFilename];
|
||||
resourceFilename = newFilenames[resourceFullFilename];
|
||||
} else {
|
||||
// The filename cannot be made relative. Consider that it is absolute.
|
||||
// Just strip the filename to its file part
|
||||
@@ -63,7 +63,7 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
|
||||
|
||||
void ResourcesMergingHelper::SetNewFilename(gd::String oldFilename,
|
||||
gd::String newFilename) {
|
||||
if (oldFilenames.find(oldFilename) != oldFilenames.end()) return;
|
||||
if (newFilenames.find(oldFilename) != newFilenames.end()) return;
|
||||
|
||||
// Extract baseName and extension from the new filename
|
||||
size_t extensionPos = newFilename.find_last_of(".");
|
||||
@@ -80,13 +80,13 @@ void ResourcesMergingHelper::SetNewFilename(gd::String oldFilename,
|
||||
gd::NewNameGenerator::Generate(
|
||||
baseName,
|
||||
[this, extension](const gd::String& newBaseName) {
|
||||
return newFilenames.find(newBaseName + extension) !=
|
||||
newFilenames.end();
|
||||
return oldFilenames.find(newBaseName + extension) !=
|
||||
oldFilenames.end();
|
||||
}) +
|
||||
extension;
|
||||
|
||||
oldFilenames[oldFilename] = finalFilename;
|
||||
newFilenames[finalFilename] = oldFilename;
|
||||
newFilenames[oldFilename] = finalFilename;
|
||||
oldFilenames[finalFilename] = oldFilename;
|
||||
}
|
||||
|
||||
void ResourcesMergingHelper::SetBaseDirectory(
|
||||
|
@@ -28,11 +28,11 @@ namespace gd {
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesMergingHelper(gd::AbstractFileSystem& fileSystem)
|
||||
: ArbitraryResourceWorker(),
|
||||
preserveDirectoriesStructure(false),
|
||||
preserveAbsoluteFilenames(false),
|
||||
public:
|
||||
ResourcesMergingHelper(gd::ResourcesManager &resourcesManager,
|
||||
gd::AbstractFileSystem &fileSystem)
|
||||
: ArbitraryResourceWorker(resourcesManager),
|
||||
preserveDirectoriesStructure(false), preserveAbsoluteFilenames(false),
|
||||
fs(fileSystem){};
|
||||
virtual ~ResourcesMergingHelper(){};
|
||||
|
||||
@@ -64,19 +64,25 @@ class GD_CORE_API ResourcesMergingHelper : public ArbitraryResourceWorker {
|
||||
* the Base Directory.
|
||||
*/
|
||||
std::map<gd::String, gd::String>& GetAllResourcesOldAndNewFilename() {
|
||||
return oldFilenames;
|
||||
return newFilenames;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resources merging helper collects all resources filenames and update these
|
||||
* filenames.
|
||||
*/
|
||||
virtual void ExposeFile(gd::String& resource) override;
|
||||
void ExposeFile(gd::String& resource) override;
|
||||
|
||||
protected:
|
||||
void SetNewFilename(gd::String oldFilename, gd::String newFilename);
|
||||
|
||||
/**
|
||||
* Original file names that can be accessed by their new name.
|
||||
*/
|
||||
std::map<gd::String, gd::String> oldFilenames;
|
||||
/**
|
||||
* New file names that can be accessed by their original name.
|
||||
*/
|
||||
std::map<gd::String, gd::String> newFilenames;
|
||||
gd::String baseDirectory;
|
||||
bool preserveDirectoriesStructure; ///< If set to true, the directory
|
||||
|
@@ -22,13 +22,16 @@ namespace gd {
|
||||
*/
|
||||
class ResourcesRenamer : public gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor taking the map from old name to new name.
|
||||
* @param oldToNewNames_ A map associating to a resource name the new name to
|
||||
* use.
|
||||
*/
|
||||
ResourcesRenamer(const std::map<gd::String, gd::String>& oldToNewNames_)
|
||||
: gd::ArbitraryResourceWorker(), oldToNewNames(oldToNewNames_){};
|
||||
/**
|
||||
* @brief Constructor taking the map from old name to new name.
|
||||
* @param oldToNewNames_ A map associating to a resource name the new name to
|
||||
* use.
|
||||
*/
|
||||
ResourcesRenamer(gd::ResourcesManager &resourcesManager,
|
||||
const std::map<gd::String, gd::String> &oldToNewNames_)
|
||||
: gd::ArbitraryResourceWorker(resourcesManager),
|
||||
oldToNewNames(oldToNewNames_){};
|
||||
|
||||
virtual ~ResourcesRenamer(){};
|
||||
|
||||
virtual void ExposeFile(gd::String& resourceFileName) override{
|
||||
@@ -62,6 +65,12 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
|
||||
virtual void ExposeModel3D(gd::String& resourceName) override {
|
||||
RenameIfNeeded(resourceName);
|
||||
};
|
||||
virtual void ExposeAtlas(gd::String& resourceName) override {
|
||||
RenameIfNeeded(resourceName);
|
||||
};
|
||||
virtual void ExposeSpine(gd::String& resourceName) override {
|
||||
RenameIfNeeded(resourceName);
|
||||
};
|
||||
|
||||
private:
|
||||
void RenameIfNeeded(gd::String& resourceName) {
|
||||
|
37
Core/GDCore/IDE/Project/SceneResourcesFinder.cpp
Normal file
37
Core/GDCore/IDE/Project/SceneResourcesFinder.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "SceneResourcesFinder.h"
|
||||
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindProjectResources(gd::Project &project) {
|
||||
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
|
||||
|
||||
gd::ResourceExposer::ExposeProjectResources(project, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &project,
|
||||
gd::Layout &layout) {
|
||||
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
|
||||
|
||||
gd::ResourceExposer::ExposeLayoutResources(project, layout, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
|
||||
if (resourceName.empty()) {
|
||||
return;
|
||||
}
|
||||
resourceNames.insert(resourceName);
|
||||
}
|
||||
|
||||
} // namespace gd
|
93
Core/GDCore/IDE/Project/SceneResourcesFinder.h
Normal file
93
Core/GDCore/IDE/Project/SceneResourcesFinder.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find resource usages in several parts of the project.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Find resource usages in a given scenes.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
static std::set<gd::String> FindSceneResources(gd::Project &project,
|
||||
gd::Layout &layout);
|
||||
|
||||
/**
|
||||
* @brief Find resource that are used globally in the project.
|
||||
*
|
||||
* It doesn't include resources used in scenes.
|
||||
*/
|
||||
static std::set<gd::String> FindProjectResources(gd::Project &project);
|
||||
|
||||
virtual ~SceneResourcesFinder(){};
|
||||
|
||||
private:
|
||||
SceneResourcesFinder(gd::ResourcesManager &resourcesManager)
|
||||
: gd::ArbitraryResourceWorker(resourcesManager){};
|
||||
|
||||
void AddUsedResource(gd::String &resourceName);
|
||||
|
||||
void ExposeFile(gd::String &resourceFileName) override{
|
||||
// Don't do anything: we're renaming resources, not the files they are
|
||||
// pointing to.
|
||||
};
|
||||
void ExposeImage(gd::String &imageResourceName) override {
|
||||
AddUsedResource(imageResourceName);
|
||||
};
|
||||
void ExposeAudio(gd::String &audioResourceName) override {
|
||||
AddUsedResource(audioResourceName);
|
||||
};
|
||||
void ExposeFont(gd::String &fontResourceName) override {
|
||||
AddUsedResource(fontResourceName);
|
||||
};
|
||||
void ExposeJson(gd::String &jsonResourceName) override {
|
||||
AddUsedResource(jsonResourceName);
|
||||
};
|
||||
void ExposeTilemap(gd::String &tilemapResourceName) override {
|
||||
AddUsedResource(tilemapResourceName);
|
||||
};
|
||||
void ExposeTileset(gd::String &tilesetResourceName) override {
|
||||
AddUsedResource(tilesetResourceName);
|
||||
};
|
||||
void ExposeVideo(gd::String &videoResourceName) override {
|
||||
AddUsedResource(videoResourceName);
|
||||
};
|
||||
void ExposeBitmapFont(gd::String &bitmapFontName) override {
|
||||
AddUsedResource(bitmapFontName);
|
||||
};
|
||||
void ExposeModel3D(gd::String &resourceName) override {
|
||||
AddUsedResource(resourceName);
|
||||
};
|
||||
void ExposeAtlas(gd::String &resourceName) override {
|
||||
AddUsedResource(resourceName);
|
||||
};
|
||||
void ExposeSpine(gd::String &resourceName) override {
|
||||
AddUsedResource(resourceName);
|
||||
};
|
||||
|
||||
std::set<gd::String> resourceNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -20,6 +20,7 @@
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/IDE/DependenciesAnalyzer.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -33,27 +34,8 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
// Add events based extensions
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
// Add (free) events functions
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
|
||||
// Add (behavior) events functions
|
||||
for (auto &&eventsBasedBehavior :
|
||||
eventsFunctionsExtension.GetEventsBasedBehaviors()
|
||||
.GetInternalVector()) {
|
||||
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
|
||||
}
|
||||
|
||||
// Add (object) events functions
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
auto &objectEventsFunctions = eventsBasedObject->GetEventsFunctions();
|
||||
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
}
|
||||
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +51,7 @@ void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
|
||||
@@ -85,7 +67,7 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
auto projectScopedContainers =
|
||||
@@ -103,6 +85,32 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
// Add layouts events
|
||||
worker.Launch(layout.GetEvents());
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
bool hasCircularDependencies = !dependenciesAnalyzer.Analyze();
|
||||
if (hasCircularDependencies) {
|
||||
// The analyzer stops when it finds circular dependencies so the dependencies are not complete.
|
||||
// TODO Should the analyzer still continue to avoid side effect on thing that would not be code generation related?
|
||||
// Maybe a boolean parameter should be added?
|
||||
return;
|
||||
}
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
||||
worker.Launch(externalEvents.GetEvents());
|
||||
}
|
||||
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
|
||||
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
|
||||
|
||||
worker.Launch(dependencyLayout.GetEvents());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
// See also gd::Project::ExposeResources for a method that traverse the whole
|
||||
@@ -130,8 +138,43 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
// Add events based extensions
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
// Add (free) events functions
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
// Add (free) events functions
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
|
||||
project, eventsFunctionsExtension, *eventsFunction,
|
||||
globalObjectsAndGroups, objectsAndGroups);
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
|
||||
// Add (behavior) events functions
|
||||
for (auto &&eventsBasedBehavior :
|
||||
eventsFunctionsExtension.GetEventsBasedBehaviors()
|
||||
.GetInternalVector()) {
|
||||
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
|
||||
}
|
||||
|
||||
// Add (object) events functions
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
// Add (free) events functions
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
@@ -140,7 +183,7 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
globalObjectsAndGroups, objectsAndGroups);
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsFunctionsExtension));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
@@ -157,7 +200,6 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
@@ -183,12 +225,27 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetSharedPropertyDescriptors());
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedBehavior.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedBehavior.GetEventsFunctions()));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
|
||||
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
|
||||
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
@@ -202,7 +259,7 @@ void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
auto projectScopedContainers =
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersFor(globalObjectsAndGroups, objectsAndGroups);
|
||||
projectScopedContainers.AddPropertiesContainer(eventsBasedObject.GetPropertyDescriptors());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParameters());
|
||||
projectScopedContainers.AddParameters(eventsFunction->GetParametersForEvents(eventsBasedObject.GetEventsFunctions()));
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents(), projectScopedContainers);
|
||||
}
|
||||
@@ -216,7 +273,7 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
|
||||
// Layout objects
|
||||
for (size_t i = 0; i < project.GetLayoutsCount(); i++) {
|
||||
worker.Launch(project.GetLayout(i));
|
||||
gd::ProjectBrowserHelper::ExposeLayoutObjects(project.GetLayout(i), worker);
|
||||
}
|
||||
|
||||
// Event based objects children
|
||||
@@ -232,6 +289,14 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
}
|
||||
};
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker) {
|
||||
// In the future, layouts may have children object containers.
|
||||
|
||||
// Layout objects
|
||||
worker.Launch(layout);
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) {
|
||||
|
||||
|
@@ -60,18 +60,52 @@ public:
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its external events.
|
||||
*/
|
||||
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
|
||||
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its external events.
|
||||
*/
|
||||
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
|
||||
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its dependencies according to EventLink (external events or other layout
|
||||
* events).
|
||||
*/
|
||||
static void
|
||||
ExposeLayoutEventsAndDependencies(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior
|
||||
* extension.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based extension.
|
||||
*/
|
||||
static void ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* extension.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based extension.
|
||||
*/
|
||||
static void ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based behavior.
|
||||
@@ -93,10 +127,22 @@ public:
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior.
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an
|
||||
* event-based behavior.
|
||||
* event-based object.
|
||||
*/
|
||||
static void
|
||||
ExposeEventsBasedObjectEvents(gd::Project &project,
|
||||
const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an
|
||||
* event-based object.
|
||||
*/
|
||||
static void
|
||||
ExposeEventsBasedObjectEvents(gd::Project &project,
|
||||
@@ -112,6 +158,14 @@ public:
|
||||
static void ExposeProjectObjects(gd::Project &project,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all ObjectContainers of the layout.
|
||||
*
|
||||
* This should be the preferred way to traverse all the objects of a layout.
|
||||
*/
|
||||
static void ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all FunctionsContainers of the project
|
||||
* (global, layouts...)
|
||||
|
@@ -128,11 +128,7 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
|
||||
} else {
|
||||
gd::Instruction action;
|
||||
action.SetType("SetReturn" + numberOrString);
|
||||
gd::String receiver = isBehavior ? "Object.Behavior::" : "Object.";
|
||||
gd::String propertyPrefix =
|
||||
(isSharedProperties ? "SharedProperty" : "Property");
|
||||
action.AddParameter(receiver + propertyPrefix + property.GetName() +
|
||||
"()");
|
||||
action.AddParameter(property.GetName());
|
||||
event.GetActions().Insert(action, 0);
|
||||
}
|
||||
}
|
||||
@@ -233,15 +229,13 @@ void PropertyFunctionGenerator::GenerateGetterAndSetter(
|
||||
gd::Instruction action;
|
||||
action.SetType(setterType);
|
||||
action.AddParameter("Object");
|
||||
gd::String parameterGetterCall =
|
||||
"GetArgumentAs" + numberOrString + "(\"Value\")";
|
||||
if (isBehavior) {
|
||||
action.AddParameter("Behavior");
|
||||
action.AddParameter("=");
|
||||
action.AddParameter(parameterGetterCall);
|
||||
action.AddParameter("Value");
|
||||
} else {
|
||||
action.AddParameter("=");
|
||||
action.AddParameter(parameterGetterCall);
|
||||
action.AddParameter("Value");
|
||||
}
|
||||
event.GetActions().Insert(action, 0);
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -32,10 +33,9 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
|
||||
// traverse the whole project (this time for events) and ExposeProjectEffects
|
||||
// (this time for effects).
|
||||
|
||||
gd::ResourcesManager* resourcesManager = &(project.GetResourcesManager());
|
||||
|
||||
// Expose any project resources as files.
|
||||
worker.ExposeResources(resourcesManager);
|
||||
worker.ExposeResources();
|
||||
|
||||
project.GetPlatformSpecificAssets().ExposeResources(worker);
|
||||
|
||||
// Expose event resources
|
||||
@@ -73,6 +73,49 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
|
||||
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
|
||||
// Expose global objects configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
objectWorker.Launch(project);
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeLayoutResources(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
|
||||
// Expose object configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
|
||||
|
||||
// Expose layer effect resources
|
||||
for (std::size_t layerIndex = 0; layerIndex < layout.GetLayersCount();
|
||||
layerIndex++) {
|
||||
auto &layer = layout.GetLayer(layerIndex);
|
||||
|
||||
auto &effects = layer.GetEffects();
|
||||
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
|
||||
effectIndex++) {
|
||||
auto &effect = effects.GetEffect(effectIndex);
|
||||
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
|
||||
effect, worker);
|
||||
}
|
||||
}
|
||||
|
||||
// Expose event resources
|
||||
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
|
||||
eventWorker);
|
||||
|
||||
// Exposed extension event resources
|
||||
// Note that using resources in extensions is very unlikely and probably not
|
||||
// worth the effort of something smart.
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEffectResources(
|
||||
gd::Platform &platform, gd::Effect &effect,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
@@ -88,11 +131,13 @@ void ResourceExposer::ExposeEffectResources(
|
||||
auto &resourceType = propertyDescriptor.GetExtraInfo()[0];
|
||||
|
||||
const gd::String &resourceName = effect.GetStringParameter(propertyName);
|
||||
gd::String potentiallyUpdatedResourceName = resourceName;
|
||||
worker.ExposeResourceWithType(resourceType,
|
||||
potentiallyUpdatedResourceName);
|
||||
if (potentiallyUpdatedResourceName != resourceName) {
|
||||
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
|
||||
if (!resourceName.empty()) {
|
||||
gd::String potentiallyUpdatedResourceName = resourceName;
|
||||
worker.ExposeResourceWithType(resourceType,
|
||||
potentiallyUpdatedResourceName);
|
||||
if (potentiallyUpdatedResourceName != resourceName) {
|
||||
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ class Platform;
|
||||
class Project;
|
||||
class ArbitraryResourceWorker;
|
||||
class Effect;
|
||||
class Layout;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -31,6 +32,25 @@ public:
|
||||
static void ExposeWholeProjectResources(gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose only the resources used globally on a project.
|
||||
*
|
||||
* It doesn't include resources used in layouts.
|
||||
*/
|
||||
static void ExposeProjectResources(gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given layout.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given effect.
|
||||
*/
|
||||
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
};
|
||||
|
@@ -16,8 +16,8 @@
|
||||
#include "GDCore/IDE/Events/BehaviorTypeRenamer.h"
|
||||
#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/EventsRefactorer.h"
|
||||
#include "GDCore/IDE/Events/EventsVariableReplacer.h"
|
||||
#include "GDCore/IDE/Events/ExpressionsParameterMover.h"
|
||||
#include "GDCore/IDE/Events/ExpressionsRenamer.h"
|
||||
@@ -138,7 +138,8 @@ void WholeProjectRefactorer::EnsureObjectEventsFunctionsProperParameters(
|
||||
}
|
||||
}
|
||||
|
||||
VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
VariablesChangeset
|
||||
WholeProjectRefactorer::ComputeChangesetForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::SerializerElement &oldSerializedVariablesContainer,
|
||||
const gd::VariablesContainer &newVariablesContainer) {
|
||||
@@ -149,9 +150,9 @@ VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer
|
||||
|
||||
if (oldVariablesContainer.GetPersistentUuid() !=
|
||||
newVariablesContainer.GetPersistentUuid()) {
|
||||
gd::LogWarning(
|
||||
_("Called ComputeChangesetForVariablesContainer on variables containers "
|
||||
"that are different - they can't be compared."));
|
||||
gd::LogWarning(_(
|
||||
"Called ComputeChangesetForVariablesContainer on variables containers "
|
||||
"that are different - they can't be compared."));
|
||||
return changeset;
|
||||
}
|
||||
|
||||
@@ -192,14 +193,11 @@ VariablesChangeset WholeProjectRefactorer::ComputeChangesetForVariablesContainer
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ApplyRefactoringForVariablesContainer(
|
||||
gd::Project &project,
|
||||
const gd::VariablesContainer &newVariablesContainer,
|
||||
const gd::VariablesChangeset& changeset) {
|
||||
gd::Project &project, const gd::VariablesContainer &newVariablesContainer,
|
||||
const gd::VariablesChangeset &changeset) {
|
||||
gd::EventsVariableReplacer eventsVariableReplacer(
|
||||
project.GetCurrentPlatform(),
|
||||
newVariablesContainer,
|
||||
changeset.oldToNewVariableNames,
|
||||
changeset.removedVariableNames);
|
||||
project.GetCurrentPlatform(), newVariablesContainer,
|
||||
changeset.oldToNewVariableNames, changeset.removedVariableNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
eventsVariableReplacer);
|
||||
}
|
||||
@@ -743,14 +741,14 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorProperty(
|
||||
EventsBasedBehavior::GetPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
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);
|
||||
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -813,14 +811,14 @@ void WholeProjectRefactorer::RenameEventsBasedBehaviorSharedProperty(
|
||||
EventsBasedBehavior::GetSharedPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
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);
|
||||
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -870,14 +868,14 @@ void WholeProjectRefactorer::RenameEventsBasedObjectProperty(
|
||||
EventsBasedObject::GetPropertyExpressionName(newPropertyName));
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project, expressionRenamer);
|
||||
|
||||
std::unordered_map<gd::String, gd::String> oldToNewPropertyNames = {{oldPropertyName, newPropertyName}};
|
||||
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);
|
||||
project.GetCurrentPlatform(), properties, oldToNewPropertyNames,
|
||||
removedPropertyNames);
|
||||
gd::ProjectBrowserHelper::ExposeProjectEvents(project,
|
||||
eventsPropertyReplacer);
|
||||
|
||||
gd::InstructionsTypeRenamer actionRenamer = gd::InstructionsTypeRenamer(
|
||||
project,
|
||||
@@ -1351,7 +1349,6 @@ void WholeProjectRefactorer::DoRenameBehavior(
|
||||
gd::Project &project, const gd::String &oldBehaviorType,
|
||||
const gd::String &newBehaviorType,
|
||||
const gd::ProjectBrowser &projectBrowser) {
|
||||
|
||||
// Rename behavior in required behavior properties
|
||||
auto requiredBehaviorRenamer =
|
||||
gd::RequiredBehaviorRenamer(oldBehaviorType, newBehaviorType);
|
||||
@@ -1378,7 +1375,6 @@ void WholeProjectRefactorer::DoRenameBehavior(
|
||||
void WholeProjectRefactorer::DoRenameObject(
|
||||
gd::Project &project, const gd::String &oldObjectType,
|
||||
const gd::String &newObjectType, const gd::ProjectBrowser &projectBrowser) {
|
||||
|
||||
// Rename object type in objects lists.
|
||||
auto customObjectTypeRenamer =
|
||||
gd::CustomObjectTypeRenamer(oldObjectType, newObjectType);
|
||||
@@ -1398,7 +1394,8 @@ 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);
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
|
||||
// Remove object in the current layout
|
||||
if (removeEventsAndGroups) {
|
||||
@@ -1447,7 +1444,8 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
|
||||
if (oldName == newName || newName.empty() || oldName.empty())
|
||||
return;
|
||||
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
auto projectScopedContainers = gd::ProjectScopedContainers::
|
||||
MakeNewProjectScopedContainersForProjectAndLayout(project, layout);
|
||||
|
||||
// Rename object in the current layout
|
||||
gd::EventsRefactorer::RenameObjectInEvents(
|
||||
@@ -1536,10 +1534,19 @@ void WholeProjectRefactorer::RenameLayer(gd::Project &project,
|
||||
const gd::String &newName) {
|
||||
if (oldName == newName || newName.empty() || oldName.empty())
|
||||
return;
|
||||
|
||||
gd::ProjectElementRenamer projectElementRenamer(project.GetCurrentPlatform(),
|
||||
"layer", oldName, newName);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
project, layout, projectElementRenamer);
|
||||
layout.GetInitialInstances().MoveInstancesToLayer(oldName, newName);
|
||||
|
||||
std::vector<gd::String> externalLayoutsNames =
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto &externalLayout = project.GetExternalLayout(name);
|
||||
externalLayout.GetInitialInstances().MoveInstancesToLayer(oldName, newName);
|
||||
}
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
|
||||
@@ -1552,8 +1559,8 @@ void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "layerEffectName", oldName, newName);
|
||||
projectElementRenamer.SetLayerConstraint(layer.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
project, layout, projectElementRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
|
||||
@@ -1566,8 +1573,8 @@ void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectAnimationName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
project, layout, projectElementRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
|
||||
@@ -1580,8 +1587,8 @@ void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectPointName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
project, layout, projectElementRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
|
||||
@@ -1594,8 +1601,8 @@ void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectEffectName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
project, layout, projectElementRenamer);
|
||||
}
|
||||
|
||||
void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsBasedObject(
|
||||
@@ -1617,9 +1624,12 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer, const gd::String &objectName,
|
||||
bool isObjectGroup, bool removeEventsAndGroups) {
|
||||
// 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);
|
||||
// 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(
|
||||
@@ -1655,9 +1665,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
|
||||
gd::ObjectsContainer &globalObjectsContainer,
|
||||
gd::ObjectsContainer &objectsContainer, const gd::String &oldName,
|
||||
const gd::String &newName, bool isObjectGroup) {
|
||||
// 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);
|
||||
// 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(), projectScopedContainers,
|
||||
@@ -1705,7 +1718,7 @@ void WholeProjectRefactorer::GlobalObjectOrGroupRemoved(
|
||||
if (layout.HasObjectNamed(objectName))
|
||||
continue;
|
||||
|
||||
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
|
||||
ObjectOrGroupRemovedInLayout(project, layout, objectName, isObjectGroup,
|
||||
removeEventsAndGroups);
|
||||
}
|
||||
}
|
||||
@@ -1753,7 +1766,8 @@ size_t WholeProjectRefactorer::GetLayoutAndExternalLayoutLayerInstancesCount(
|
||||
GetAssociatedExternalLayouts(project, layout);
|
||||
for (gd::String name : externalLayoutsNames) {
|
||||
auto &externalLayout = project.GetExternalLayout(name);
|
||||
count += externalLayout.GetInitialInstances().GetLayerInstancesCount(layerName);
|
||||
count +=
|
||||
externalLayout.GetInitialInstances().GetLayerInstancesCount(layerName);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
#include "CustomConfigurationHelper.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
@@ -13,8 +15,6 @@
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
using namespace gd;
|
||||
|
||||
void CustomConfigurationHelper::InitializeContent(
|
||||
@@ -25,7 +25,8 @@ void CustomConfigurationHelper::InitializeContent(
|
||||
auto propertyType = property->GetType();
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
element.SetStringValue(property->GetValue());
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(property->GetValue().To<double>());
|
||||
@@ -51,7 +52,8 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
|
||||
if (configurationContent.HasChild(propertyName)) {
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
newProperty.SetValue(
|
||||
configurationContent.GetChild(propertyName).GetStringValue());
|
||||
} else if (propertyType == "Number") {
|
||||
@@ -59,8 +61,9 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
configurationContent.GetChild(propertyName).GetDoubleValue()));
|
||||
} else if (propertyType == "Boolean") {
|
||||
newProperty.SetValue(
|
||||
configurationContent.GetChild(propertyName).GetBoolValue() ? "true"
|
||||
: "false");
|
||||
configurationContent.GetChild(propertyName).GetBoolValue()
|
||||
? "true"
|
||||
: "false");
|
||||
}
|
||||
} else {
|
||||
// No value was serialized for this property. `newProperty`
|
||||
@@ -85,7 +88,8 @@ bool CustomConfigurationHelper::UpdateProperty(
|
||||
const gd::String &propertyType = property.GetType();
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
element.SetStringValue(newValue);
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(newValue.To<double>());
|
||||
|
@@ -155,6 +155,10 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
|
||||
worker.ExposeBitmapFont(newPropertyValue);
|
||||
} else if (resourceType == "model3D") {
|
||||
worker.ExposeModel3D(newPropertyValue);
|
||||
} else if (resourceType == "atlas") {
|
||||
worker.ExposeAtlas(newPropertyValue);
|
||||
} else if (resourceType == "spine") {
|
||||
worker.ExposeSpine(newPropertyValue);
|
||||
}
|
||||
|
||||
if (newPropertyValue != oldPropertyValue) {
|
||||
|
@@ -17,7 +17,7 @@ EventsBasedObject::EventsBasedObject()
|
||||
}
|
||||
|
||||
EventsBasedObject::~EventsBasedObject() {}
|
||||
|
||||
|
||||
EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObject)
|
||||
: AbstractEventsBasedEntity(_eventBasedObject) {
|
||||
// TODO Add a copy constructor in ObjectsContainer.
|
||||
@@ -27,17 +27,26 @@ EventsBasedObject::EventsBasedObject(const gd::EventsBasedObject &_eventBasedObj
|
||||
|
||||
void EventsBasedObject::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("defaultName", defaultName);
|
||||
if (isRenderedIn3D) {
|
||||
element.SetBoolAttribute("is3D", true);
|
||||
}
|
||||
|
||||
AbstractEventsBasedEntity::SerializeTo(element);
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
}
|
||||
|
||||
void EventsBasedObject::UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element) {
|
||||
const SerializerElement& element) {
|
||||
defaultName = element.GetStringAttribute("defaultName");
|
||||
isRenderedIn3D = element.GetBoolAttribute("is3D", false);
|
||||
|
||||
AbstractEventsBasedEntity::UnserializeFrom(project, element);
|
||||
UnserializeObjectsFrom(project, element.GetChild("objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -72,6 +72,19 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Declare a usage of the 3D renderer.
|
||||
*/
|
||||
EventsBasedObject& MarkAsRenderedIn3D(bool isRenderedIn3D_) {
|
||||
isRenderedIn3D = isRenderedIn3D_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return true if the object uses the 3D renderer.
|
||||
*/
|
||||
bool IsRenderedIn3D() const { return isRenderedIn3D; }
|
||||
|
||||
void SerializeTo(SerializerElement& element) const override;
|
||||
|
||||
void UnserializeFrom(gd::Project& project,
|
||||
@@ -79,6 +92,7 @@ class GD_CORE_API EventsBasedObject: public AbstractEventsBasedEntity, public Ob
|
||||
|
||||
private:
|
||||
gd::String defaultName;
|
||||
bool isRenderedIn3D;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -19,7 +19,7 @@ Layer::Layer()
|
||||
isLocked(false),
|
||||
isLightingLayer(false),
|
||||
followBaseLayerCamera(false),
|
||||
camera3DNearPlaneDistance(0.1),
|
||||
camera3DNearPlaneDistance(3),
|
||||
camera3DFarPlaneDistance(10000),
|
||||
camera3DFieldOfView(45),
|
||||
ambientLightColorR(200),
|
||||
@@ -39,6 +39,7 @@ void Layer::SetCameraCount(std::size_t n) {
|
||||
void Layer::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("renderingType", GetRenderingType());
|
||||
element.SetAttribute("cameraType", GetCameraType());
|
||||
element.SetAttribute("visibility", GetVisibility());
|
||||
element.SetAttribute("isLocked", IsLocked());
|
||||
element.SetAttribute("isLightingLayer", IsLightingLayer());
|
||||
@@ -78,6 +79,7 @@ void Layer::SerializeTo(SerializerElement& element) const {
|
||||
void Layer::UnserializeFrom(const SerializerElement& element) {
|
||||
SetName(element.GetStringAttribute("name", "", "Name"));
|
||||
SetRenderingType(element.GetStringAttribute("renderingType", ""));
|
||||
SetCameraType(element.GetStringAttribute("cameraType", "perspective"));
|
||||
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
|
||||
SetLocked(element.GetBoolAttribute("isLocked", false));
|
||||
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
|
||||
|
@@ -104,10 +104,17 @@ class GD_CORE_API Layer {
|
||||
const gd::String& GetName() const { return name; }
|
||||
|
||||
const gd::String& GetRenderingType() const { return renderingType; }
|
||||
|
||||
void SetRenderingType(const gd::String& renderingType_) {
|
||||
renderingType = renderingType_;
|
||||
}
|
||||
|
||||
const gd::String& GetCameraType() const { return cameraType; }
|
||||
|
||||
void SetCameraType(const gd::String& cameraType_) {
|
||||
cameraType = cameraType_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Change if layer is displayed or not
|
||||
*/
|
||||
@@ -268,6 +275,7 @@ class GD_CORE_API Layer {
|
||||
gd::String name; ///< The name of the layer
|
||||
gd::String renderingType; ///< The rendering type: "" (empty), "2d", "3d" or
|
||||
///< "2d+3d".
|
||||
gd::String cameraType;
|
||||
bool isVisible; ///< True if the layer is visible
|
||||
bool isLocked; ///< True if the layer is locked
|
||||
bool isLightingLayer; ///< True if the layer is used to display lights and
|
||||
|
@@ -294,6 +294,7 @@ void Layout::SerializeTo(SerializerElement& element) const {
|
||||
GetVariables().SerializeTo(element.AddChild("variables"));
|
||||
GetInitialInstances().SerializeTo(element.AddChild("instances"));
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
gd::EventsListSerialization::SerializeEventsTo(events,
|
||||
element.AddChild("events"));
|
||||
|
||||
@@ -353,6 +354,11 @@ void Layout::UnserializeFrom(gd::Project& project,
|
||||
project, GetEvents(), element.GetChild("events", 0, "Events"));
|
||||
|
||||
UnserializeObjectsFrom(project, element.GetChild("objects", 0, "Objets"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(project, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
|
||||
initialInstances.UnserializeFrom(
|
||||
element.GetChild("instances", 0, "Positions"));
|
||||
variables.UnserializeFrom(element.GetChild("variables", 0, "Variables"));
|
||||
@@ -444,13 +450,15 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
|
||||
bool searchInGroups) {
|
||||
gd::String type;
|
||||
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(name))
|
||||
type = layout.GetObject(name).GetType();
|
||||
else if (project.HasObjectNamed(name))
|
||||
type = project.GetObject(name).GetType();
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
else if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
@@ -523,7 +531,7 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
const gd::ObjectsContainer &project, const gd::ObjectsContainer &layout,
|
||||
const gd::String &objectOrGroupName, const gd::String &behaviorType,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
auto behaviorNames = object.GetAllBehaviorNames();
|
||||
@@ -542,7 +550,9 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
return behaviorNames;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -554,12 +564,14 @@ std::vector<gd::String> GD_CORE_API GetBehaviorNamesInObjectOrGroup(
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
std::vector<gd::String> behaviorNames;
|
||||
return behaviorNames;
|
||||
}
|
||||
|
||||
// Compute the intersection of the behaviors of all objects.
|
||||
auto behaviorNames = GetBehaviorNamesInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorType, false);
|
||||
for (size_t i = 1; i < groupsObjects.size(); i++) {
|
||||
@@ -587,7 +599,7 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
const gd::String &objectOrGroupName,
|
||||
const gd::String &behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
return layout.GetObject(objectOrGroupName).HasBehaviorNamed(behaviorName);
|
||||
}
|
||||
@@ -599,7 +611,9 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -610,10 +624,12 @@ bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all objects have the behavior.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
|
||||
@@ -629,7 +645,7 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
gd::String objectOrGroupName,
|
||||
gd::String behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) &&
|
||||
@@ -645,7 +661,9 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -656,10 +674,12 @@ bool GD_CORE_API IsDefaultBehavior(const gd::ObjectsContainer& project,
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all objects have the same type.
|
||||
for (auto &&object : groupsObjects) {
|
||||
if (!IsDefaultBehavior(project, layout, object, behaviorName,
|
||||
@@ -675,7 +695,7 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& behaviorName,
|
||||
bool searchInGroups) {
|
||||
// Search in objects
|
||||
// Search in objects.
|
||||
if (layout.HasObjectNamed(objectOrGroupName)) {
|
||||
auto &object = layout.GetObject(objectOrGroupName);
|
||||
return object.HasBehaviorNamed(behaviorName) ?
|
||||
@@ -691,7 +711,9 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
return "";
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Search in groups.
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
const gd::ObjectsContainer *container;
|
||||
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
|
||||
container = &layout;
|
||||
@@ -702,10 +724,12 @@ gd::String GD_CORE_API GetTypeOfBehaviorInObjectOrGroup(const gd::ObjectsContain
|
||||
}
|
||||
const vector<gd::String> &groupsObjects =
|
||||
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
// Empty groups don't contain any behavior.
|
||||
if (groupsObjects.empty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Check that all objects have the behavior with the same type.
|
||||
auto behaviorType = GetTypeOfBehaviorInObjectOrGroup(
|
||||
project, layout, groupsObjects[0], behaviorName, false);
|
||||
@@ -767,6 +791,8 @@ GetBehaviorsOfObject(const gd::ObjectsContainer& project,
|
||||
}
|
||||
|
||||
// Search in groups
|
||||
// Currently, a group is considered as the "intersection" of all of its objects.
|
||||
// Search "groups is the intersection of its objects" in the codebase.
|
||||
if (searchInGroups) {
|
||||
for (std::size_t i = 0; i < layout.GetObjectGroups().size(); ++i) {
|
||||
if (layout.GetObjectGroups()[i].GetName() == name) {
|
||||
|
@@ -17,7 +17,7 @@ LoadingScreen::LoadingScreen()
|
||||
backgroundFadeInDuration(0.2),
|
||||
minDuration(1.5),
|
||||
logoAndProgressFadeInDuration(0.2),
|
||||
logoAndProgressLogoFadeInDelay(0.2),
|
||||
logoAndProgressLogoFadeInDelay(0),
|
||||
showProgressBar(true),
|
||||
progressBarMinWidth(40),
|
||||
progressBarMaxWidth(200),
|
||||
|
@@ -40,7 +40,6 @@ void Object::Init(const gd::Object& object) {
|
||||
name = object.name;
|
||||
assetStoreId = object.assetStoreId;
|
||||
objectVariables = object.objectVariables;
|
||||
tags = object.tags;
|
||||
effectsContainer = object.effectsContainer;
|
||||
|
||||
behaviors.clear();
|
||||
@@ -134,7 +133,6 @@ void Object::UnserializeFrom(gd::Project& project,
|
||||
SetType(element.GetStringAttribute("type"));
|
||||
assetStoreId = element.GetStringAttribute("assetStoreId");
|
||||
name = element.GetStringAttribute("name", name, "nom");
|
||||
tags = element.GetStringAttribute("tags");
|
||||
|
||||
objectVariables.UnserializeFrom(
|
||||
element.GetChild("variables", 0, "Variables"));
|
||||
@@ -207,7 +205,6 @@ void Object::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("name", GetName());
|
||||
element.SetAttribute("assetStoreId", GetAssetStoreId());
|
||||
element.SetAttribute("type", GetType());
|
||||
element.SetAttribute("tags", GetTags());
|
||||
objectVariables.SerializeTo(element.AddChild("variables"));
|
||||
effectsContainer.SerializeTo(element.AddChild("effects"));
|
||||
|
||||
|
@@ -120,14 +120,6 @@ class GD_CORE_API Object {
|
||||
*/
|
||||
const gd::String& GetType() const { return configuration->GetType(); }
|
||||
|
||||
/** \brief Change the tags of the object.
|
||||
*/
|
||||
void SetTags(const gd::String& tags_) { tags = tags_; }
|
||||
|
||||
/** \brief Return the tags of the object.
|
||||
*/
|
||||
const gd::String& GetTags() const { return tags; }
|
||||
|
||||
/** \brief Shortcut to check if the object is a 3D object.
|
||||
*/
|
||||
bool Is3DObject() const { return configuration->Is3DObject(); }
|
||||
@@ -268,7 +260,6 @@ class GD_CORE_API Object {
|
||||
///< object.
|
||||
gd::VariablesContainer
|
||||
objectVariables; ///< List of the variables of the 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,
|
||||
|
248
Core/GDCore/Project/ObjectFolderOrObject.cpp
Normal file
248
Core/GDCore/Project/ObjectFolderOrObject.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/Tools/Log.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
ObjectFolderOrObject ObjectFolderOrObject::badObjectFolderOrObject;
|
||||
|
||||
ObjectFolderOrObject::ObjectFolderOrObject()
|
||||
: folderName("__NULL"), object(nullptr) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::String folderName_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: folderName(folderName_), parent(parent_), object(nullptr) {}
|
||||
ObjectFolderOrObject::ObjectFolderOrObject(gd::Object* object_,
|
||||
ObjectFolderOrObject* parent_)
|
||||
: object(object_), parent(parent_) {}
|
||||
ObjectFolderOrObject::~ObjectFolderOrObject() {}
|
||||
|
||||
bool ObjectFolderOrObject::HasObjectNamed(const gd::String& name) {
|
||||
if (IsFolder()) {
|
||||
return std::any_of(
|
||||
children.begin(),
|
||||
children.end(),
|
||||
[&name](
|
||||
std::unique_ptr<gd::ObjectFolderOrObject>& objectFolderOrObject) {
|
||||
return objectFolderOrObject->HasObjectNamed(name);
|
||||
});
|
||||
}
|
||||
if (!object) return false;
|
||||
return object->GetName() == name;
|
||||
}
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetObjectNamed(
|
||||
const gd::String& name) {
|
||||
if (object && object->GetName() == name) {
|
||||
return *this;
|
||||
}
|
||||
if (IsFolder()) {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
ObjectFolderOrObject& foundInChild = children[j]->GetObjectNamed(name);
|
||||
if (&(foundInChild) != &badObjectFolderOrObject) {
|
||||
return foundInChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::SetFolderName(const gd::String& name) {
|
||||
if (!IsFolder()) return;
|
||||
folderName = name;
|
||||
}
|
||||
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
const ObjectFolderOrObject& ObjectFolderOrObject::GetChildAt(std::size_t index) const {
|
||||
if (index >= children.size()) return badObjectFolderOrObject;
|
||||
return *children[index];
|
||||
}
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::GetObjectChild(
|
||||
const gd::String& name) {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
if (!children[j]->IsFolder()) {
|
||||
if (children[j]->GetObject().GetName() == name) return *children[j];
|
||||
};
|
||||
}
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::InsertObject(gd::Object* insertedObject,
|
||||
std::size_t position) {
|
||||
auto objectFolderOrObject =
|
||||
gd::make_unique<ObjectFolderOrObject>(insertedObject, this);
|
||||
if (position < children.size()) {
|
||||
children.insert(children.begin() + position,
|
||||
std::move(objectFolderOrObject));
|
||||
} else {
|
||||
children.push_back(std::move(objectFolderOrObject));
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ObjectFolderOrObject::GetChildPosition(
|
||||
const ObjectFolderOrObject& child) const {
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
if (children[j].get() == &child) return j;
|
||||
}
|
||||
return gd::String::npos;
|
||||
}
|
||||
|
||||
ObjectFolderOrObject& ObjectFolderOrObject::InsertNewFolder(
|
||||
const gd::String& newFolderName, std::size_t position) {
|
||||
auto newFolderPtr =
|
||||
gd::make_unique<ObjectFolderOrObject>(newFolderName, this);
|
||||
gd::ObjectFolderOrObject& newFolder = *(*(children.insert(
|
||||
position < children.size() ? children.begin() + position : children.end(),
|
||||
std::move(newFolderPtr))));
|
||||
return newFolder;
|
||||
};
|
||||
|
||||
void ObjectFolderOrObject::RemoveRecursivelyObjectNamed(
|
||||
const gd::String& name) {
|
||||
if (IsFolder()) {
|
||||
children.erase(
|
||||
std::remove_if(children.begin(),
|
||||
children.end(),
|
||||
[&name](std::unique_ptr<gd::ObjectFolderOrObject>&
|
||||
objectFolderOrObject) {
|
||||
return !objectFolderOrObject->IsFolder() &&
|
||||
objectFolderOrObject->GetObject().GetName() ==
|
||||
name;
|
||||
}),
|
||||
children.end());
|
||||
for (auto& it : children) {
|
||||
it->RemoveRecursivelyObjectNamed(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool ObjectFolderOrObject::IsADescendantOf(
|
||||
const ObjectFolderOrObject& otherObjectFolderOrObject) {
|
||||
if (parent == nullptr) return false;
|
||||
if (&(*parent) == &otherObjectFolderOrObject) return true;
|
||||
return parent->IsADescendantOf(otherObjectFolderOrObject);
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::MoveChild(std::size_t oldIndex,
|
||||
std::size_t newIndex) {
|
||||
if (!IsFolder()) return;
|
||||
if (oldIndex >= children.size() || newIndex >= children.size()) return;
|
||||
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> objectFolderOrObject =
|
||||
std::move(children[oldIndex]);
|
||||
children.erase(children.begin() + oldIndex);
|
||||
children.insert(children.begin() + newIndex, std::move(objectFolderOrObject));
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::RemoveFolderChild(
|
||||
const ObjectFolderOrObject& childToRemove) {
|
||||
if (!IsFolder() || !childToRemove.IsFolder() ||
|
||||
childToRemove.GetChildrenCount() > 0) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::unique_ptr<gd::ObjectFolderOrObject>>::iterator it = find_if(
|
||||
children.begin(),
|
||||
children.end(),
|
||||
[&childToRemove](std::unique_ptr<gd::ObjectFolderOrObject>& child) {
|
||||
return child.get() == &childToRemove;
|
||||
});
|
||||
if (it == children.end()) return;
|
||||
|
||||
children.erase(it);
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::MoveObjectFolderOrObjectToAnotherFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition) {
|
||||
if (!newParentFolder.IsFolder()) return;
|
||||
if (newParentFolder.IsADescendantOf(objectFolderOrObject)) return;
|
||||
|
||||
std::vector<std::unique_ptr<gd::ObjectFolderOrObject>>::iterator it =
|
||||
find_if(children.begin(),
|
||||
children.end(),
|
||||
[&objectFolderOrObject](std::unique_ptr<gd::ObjectFolderOrObject>&
|
||||
childObjectFolderOrObject) {
|
||||
return childObjectFolderOrObject.get() == &objectFolderOrObject;
|
||||
});
|
||||
if (it == children.end()) return;
|
||||
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> objectFolderOrObjectPtr =
|
||||
std::move(*it);
|
||||
children.erase(it);
|
||||
|
||||
objectFolderOrObjectPtr->parent = &newParentFolder;
|
||||
newParentFolder.children.insert(
|
||||
newPosition < newParentFolder.children.size()
|
||||
? newParentFolder.children.begin() + newPosition
|
||||
: newParentFolder.children.end(),
|
||||
std::move(objectFolderOrObjectPtr));
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::SerializeTo(SerializerElement& element) const {
|
||||
if (IsFolder()) {
|
||||
element.SetAttribute("folderName", GetFolderName());
|
||||
if (children.size() > 0) {
|
||||
SerializerElement& childrenElement = element.AddChild("children");
|
||||
childrenElement.ConsiderAsArrayOf("objectFolderOrObject");
|
||||
for (std::size_t j = 0; j < children.size(); j++) {
|
||||
children[j]->SerializeTo(
|
||||
childrenElement.AddChild("objectFolderOrObject"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
element.SetAttribute("objectName", GetObject().GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFolderOrObject::UnserializeFrom(
|
||||
gd::Project& project,
|
||||
const SerializerElement& element,
|
||||
gd::ObjectsContainer& objectsContainer) {
|
||||
children.clear();
|
||||
gd::String potentialFolderName = element.GetStringAttribute("folderName", "");
|
||||
|
||||
if (!potentialFolderName.empty()) {
|
||||
object = nullptr;
|
||||
folderName = potentialFolderName;
|
||||
|
||||
if (element.HasChild("children")) {
|
||||
const SerializerElement& childrenElements =
|
||||
element.GetChild("children", 0);
|
||||
childrenElements.ConsiderAsArrayOf("objectFolderOrObject");
|
||||
for (std::size_t i = 0; i < childrenElements.GetChildrenCount(); ++i) {
|
||||
std::unique_ptr<ObjectFolderOrObject> childObjectFolderOrObject =
|
||||
make_unique<ObjectFolderOrObject>();
|
||||
childObjectFolderOrObject->UnserializeFrom(
|
||||
project, childrenElements.GetChild(i), objectsContainer);
|
||||
childObjectFolderOrObject->parent = this;
|
||||
children.push_back(std::move(childObjectFolderOrObject));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
folderName = "";
|
||||
gd::String objectName = element.GetStringAttribute("objectName");
|
||||
if (objectsContainer.HasObjectNamed(objectName)) {
|
||||
object = &objectsContainer.GetObject(objectName);
|
||||
} else {
|
||||
gd::LogError("Object with name " + objectName +
|
||||
" not found in objects container.");
|
||||
object = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
203
Core/GDCore/Project/ObjectFolderOrObject.h
Normal file
203
Core/GDCore/Project/ObjectFolderOrObject.h
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef GDCORE_OBJECTFOLDEROROBJECT_H
|
||||
#define GDCORE_OBJECTFOLDEROROBJECT_H
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Object;
|
||||
class SerializerElement;
|
||||
class ObjectsContainer;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Class representing a folder structure in order to organize objects
|
||||
* in folders (to be used with an ObjectsContainer.)
|
||||
*
|
||||
* \see gd::ObjectsContainer
|
||||
*/
|
||||
class GD_CORE_API ObjectFolderOrObject {
|
||||
public:
|
||||
/**
|
||||
* \brief Default constructor creating an empty instance. Useful for the null
|
||||
* object pattern.
|
||||
*/
|
||||
ObjectFolderOrObject();
|
||||
virtual ~ObjectFolderOrObject();
|
||||
/**
|
||||
* \brief Constructor for creating an instance representing a folder.
|
||||
*/
|
||||
ObjectFolderOrObject(gd::String folderName_,
|
||||
ObjectFolderOrObject* parent_ = nullptr);
|
||||
/**
|
||||
* \brief Constructor for creating an instance representing an object.
|
||||
*/
|
||||
ObjectFolderOrObject(gd::Object* object_,
|
||||
ObjectFolderOrObject* parent_ = nullptr);
|
||||
|
||||
/**
|
||||
* \brief Returns the object behind the instance.
|
||||
*/
|
||||
gd::Object& GetObject() const { return *object; }
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance represents a folder.
|
||||
*/
|
||||
bool IsFolder() const { return !folderName.empty(); }
|
||||
/**
|
||||
* \brief Returns the name of the folder.
|
||||
*/
|
||||
const gd::String& GetFolderName() const { return folderName; }
|
||||
|
||||
/**
|
||||
* \brief Set the folder name. Does nothing if called on an instance not
|
||||
* representing a folder.
|
||||
*/
|
||||
void SetFolderName(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance represents the object with the given
|
||||
* name or if any of the children does (recursive search).
|
||||
*/
|
||||
bool HasObjectNamed(const gd::String& name);
|
||||
/**
|
||||
* \brief Returns the child instance holding the object with the given name
|
||||
* (recursive search).
|
||||
*/
|
||||
ObjectFolderOrObject& GetObjectNamed(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns the number of children. Returns 0 if the instance represents
|
||||
* an object.
|
||||
*/
|
||||
std::size_t GetChildrenCount() const {
|
||||
if (IsFolder()) return children.size();
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject at the given index.
|
||||
*/
|
||||
ObjectFolderOrObject& GetChildAt(std::size_t index);
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject at the given index.
|
||||
*/
|
||||
const ObjectFolderOrObject& GetChildAt(std::size_t index) const;
|
||||
/**
|
||||
* \brief Returns the child ObjectFolderOrObject that represents the object
|
||||
* with the given name. To use only if sure that the instance holds the object
|
||||
* in its direct children (no recursive search).
|
||||
*
|
||||
* \note The equivalent method to get a folder by its name cannot be
|
||||
* implemented because there is no unicity enforced on the folder name.
|
||||
*/
|
||||
ObjectFolderOrObject& GetObjectChild(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Returns the parent of the instance. If the instance has no parent
|
||||
* (root folder), the null object is returned.
|
||||
*/
|
||||
ObjectFolderOrObject& GetParent() {
|
||||
if (parent == nullptr) {
|
||||
return badObjectFolderOrObject;
|
||||
}
|
||||
return *parent;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Returns true if the instance is a root folder (that's to say it
|
||||
* has no parent).
|
||||
*/
|
||||
bool IsRootFolder() { return !object && !parent; }
|
||||
|
||||
/**
|
||||
* \brief Moves a child from a position to a new one.
|
||||
*/
|
||||
void MoveChild(std::size_t oldIndex, std::size_t newIndex);
|
||||
/**
|
||||
* \brief Removes the given child from the instance's children. If the given
|
||||
* child contains children of its own, does nothing.
|
||||
*/
|
||||
void RemoveFolderChild(const ObjectFolderOrObject& childToRemove);
|
||||
/**
|
||||
* \brief Removes the child representing the object with the given name from
|
||||
* the instance children and recursively does it for every folder children.
|
||||
*/
|
||||
void RemoveRecursivelyObjectNamed(const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Inserts an instance representing the given object at the given
|
||||
* position.
|
||||
*/
|
||||
void InsertObject(gd::Object* insertedObject,
|
||||
std::size_t position = (size_t)-1);
|
||||
/**
|
||||
* \brief Inserts an instance representing a folder with the given name at the
|
||||
* given position.
|
||||
*/
|
||||
ObjectFolderOrObject& InsertNewFolder(const gd::String& newFolderName,
|
||||
std::size_t position);
|
||||
/**
|
||||
* \brief Returns true if the instance is a descendant of the given instance
|
||||
* of ObjectFolderOrObject.
|
||||
*/
|
||||
bool IsADescendantOf(const ObjectFolderOrObject& otherObjectFolderOrObject);
|
||||
|
||||
/**
|
||||
* \brief Returns the position of the given instance of ObjectFolderOrObject
|
||||
* in the instance's children.
|
||||
*/
|
||||
std::size_t GetChildPosition(const ObjectFolderOrObject& child) const;
|
||||
/**
|
||||
* \brief Moves the given child ObjectFolderOrObject to the given folder at
|
||||
* the given position.
|
||||
*/
|
||||
void MoveObjectFolderOrObjectToAnotherFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition);
|
||||
|
||||
/** \name Saving and loading
|
||||
* Members functions related to saving and loading the objects of the class.
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize the ObjectFolderOrObject instance.
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the ObjectFolderOrObject instance.
|
||||
*/
|
||||
void UnserializeFrom(gd::Project& project,
|
||||
const SerializerElement& element,
|
||||
ObjectsContainer& objectsContainer);
|
||||
///@}
|
||||
|
||||
private:
|
||||
static gd::ObjectFolderOrObject badObjectFolderOrObject;
|
||||
|
||||
gd::ObjectFolderOrObject*
|
||||
parent; // nullptr if root folder, points to the parent folder otherwise.
|
||||
|
||||
// Representing an object:
|
||||
gd::Object* object; // nullptr if folderName is set.
|
||||
|
||||
// or representing a folder:
|
||||
gd::String folderName; // Empty if object is set.
|
||||
std::vector<std::unique_ptr<ObjectFolderOrObject>>
|
||||
children; // Folder children.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // GDCORE_OBJECTFOLDEROROBJECT_H
|
@@ -162,10 +162,12 @@ 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());
|
||||
void ObjectGroupsContainer::ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name)> fn) const {
|
||||
for (const auto& objectGroup : objectGroups) {
|
||||
if (objectGroup->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(objectGroup->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -118,9 +118,9 @@ class GD_CORE_API ObjectGroupsContainer {
|
||||
inline void Clear() { objectGroups.clear(); }
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each group name starting with the prefix passed in parameter.
|
||||
* \brief Call the callback for each group name matching the specified search.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name)> fn) const;
|
||||
void ForEachNameMatchingSearch(const gd::String& search, std::function<void(const gd::String& name)> fn) const;
|
||||
///@}
|
||||
|
||||
/** \name Saving and loading
|
||||
|
@@ -9,12 +9,15 @@
|
||||
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
ObjectsContainer::ObjectsContainer() {}
|
||||
ObjectsContainer::ObjectsContainer() {
|
||||
rootFolder = gd::make_unique<gd::ObjectFolderOrObject>("__ROOT");
|
||||
}
|
||||
|
||||
ObjectsContainer::~ObjectsContainer() {}
|
||||
|
||||
@@ -24,6 +27,22 @@ void ObjectsContainer::SerializeObjectsTo(SerializerElement& element) const {
|
||||
initialObjects[j]->SerializeTo(element.AddChild("object"));
|
||||
}
|
||||
}
|
||||
void ObjectsContainer::SerializeFoldersTo(SerializerElement& element) const {
|
||||
rootFolder->SerializeTo(element);
|
||||
}
|
||||
|
||||
void ObjectsContainer::UnserializeFoldersFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
rootFolder->UnserializeFrom(project, element, *this);
|
||||
}
|
||||
|
||||
void ObjectsContainer::AddMissingObjectsInRootFolder() {
|
||||
for (std::size_t i = 0; i < initialObjects.size(); ++i) {
|
||||
if (!rootFolder->HasObjectNamed(initialObjects[i]->GetName())) {
|
||||
rootFolder->InsertObject(&(*initialObjects[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainer::UnserializeObjectsFrom(
|
||||
gd::Project& project, const SerializerElement& element) {
|
||||
@@ -48,17 +67,23 @@ void ObjectsContainer::UnserializeObjectsFrom(
|
||||
bool ObjectsContainer::HasObjectNamed(const gd::String& name) const {
|
||||
return (find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)) != initialObjects.end());
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}) != initialObjects.end());
|
||||
}
|
||||
gd::Object& ObjectsContainer::GetObject(const gd::String& name) {
|
||||
return *(*find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}));
|
||||
}
|
||||
const gd::Object& ObjectsContainer::GetObject(const gd::String& name) const {
|
||||
return *(*find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(gd::ObjectHasName(), name)));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
}));
|
||||
}
|
||||
gd::Object& ObjectsContainer::GetObject(std::size_t index) {
|
||||
return *initialObjects[index];
|
||||
@@ -84,6 +109,22 @@ gd::Object& ObjectsContainer::InsertNewObject(const gd::Project& project,
|
||||
: initialObjects.end(),
|
||||
project.CreateObject(objectType, name))));
|
||||
|
||||
rootFolder->InsertObject(&newlyCreatedObject);
|
||||
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
gd::Object& ObjectsContainer::InsertNewObjectInFolder(
|
||||
const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
std::size_t position) {
|
||||
gd::Object& newlyCreatedObject = *(*(initialObjects.insert(
|
||||
initialObjects.end(), project.CreateObject(objectType, name))));
|
||||
|
||||
objectFolderOrObject.InsertObject(&newlyCreatedObject, position);
|
||||
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
@@ -97,16 +138,6 @@ gd::Object& ObjectsContainer::InsertObject(const gd::Object& object,
|
||||
return newlyCreatedObject;
|
||||
}
|
||||
|
||||
void ObjectsContainer::SwapObjects(std::size_t firstObjectIndex,
|
||||
std::size_t secondObjectIndex) {
|
||||
if (firstObjectIndex >= initialObjects.size() ||
|
||||
secondObjectIndex >= initialObjects.size())
|
||||
return;
|
||||
|
||||
std::iter_swap(initialObjects.begin() + firstObjectIndex,
|
||||
initialObjects.begin() + secondObjectIndex);
|
||||
}
|
||||
|
||||
void ObjectsContainer::MoveObject(std::size_t oldIndex, std::size_t newIndex) {
|
||||
if (oldIndex >= initialObjects.size() || newIndex >= initialObjects.size())
|
||||
return;
|
||||
@@ -120,30 +151,59 @@ void ObjectsContainer::RemoveObject(const gd::String& name) {
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
|
||||
find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(ObjectHasName(), name));
|
||||
[&](const std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == name;
|
||||
});
|
||||
if (objectIt == initialObjects.end()) return;
|
||||
|
||||
rootFolder->RemoveRecursivelyObjectNamed(name);
|
||||
|
||||
initialObjects.erase(objectIt);
|
||||
}
|
||||
|
||||
void ObjectsContainer::MoveObjectToAnotherContainer(
|
||||
const gd::String& name,
|
||||
void ObjectsContainer::MoveObjectFolderOrObjectToAnotherContainerInFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectsContainer& newContainer,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition) {
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt =
|
||||
find_if(initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
bind2nd(ObjectHasName(), name));
|
||||
if (objectFolderOrObject.IsFolder() || !newParentFolder.IsFolder()) return;
|
||||
|
||||
std::vector<std::unique_ptr<gd::Object>>::iterator objectIt = find_if(
|
||||
initialObjects.begin(),
|
||||
initialObjects.end(),
|
||||
[&objectFolderOrObject](std::unique_ptr<gd::Object>& object) {
|
||||
return object->GetName() == objectFolderOrObject.GetObject().GetName();
|
||||
});
|
||||
if (objectIt == initialObjects.end()) return;
|
||||
|
||||
std::unique_ptr<gd::Object> object = std::move(*objectIt);
|
||||
initialObjects.erase(objectIt);
|
||||
|
||||
newContainer.initialObjects.insert(
|
||||
newPosition < newContainer.initialObjects.size()
|
||||
? newContainer.initialObjects.begin() + newPosition
|
||||
: newContainer.initialObjects.end(),
|
||||
std::move(object));
|
||||
newContainer.initialObjects.push_back(std::move(object));
|
||||
|
||||
objectFolderOrObject.GetParent().MoveObjectFolderOrObjectToAnotherFolder(
|
||||
objectFolderOrObject, newParentFolder, newPosition);
|
||||
}
|
||||
|
||||
std::vector<const ObjectFolderOrObject*>
|
||||
ObjectsContainer::GetAllObjectFolderOrObjects() const {
|
||||
std::vector<const ObjectFolderOrObject*> results;
|
||||
|
||||
std::function<void(const ObjectFolderOrObject& folder)> addChildrenOfFolder =
|
||||
[&](const ObjectFolderOrObject& folder) {
|
||||
for (size_t i = 0; i < folder.GetChildrenCount(); ++i) {
|
||||
const auto& child = folder.GetChildAt(i);
|
||||
results.push_back(&child);
|
||||
|
||||
if (child.IsFolder()) {
|
||||
addChildrenOfFolder(child);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addChildrenOfFolder(*rootFolder);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -9,11 +9,12 @@
|
||||
#include <vector>
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectFolderOrObject.h"
|
||||
namespace gd {
|
||||
class Object;
|
||||
class Project;
|
||||
class SerializerElement;
|
||||
}
|
||||
} // namespace gd
|
||||
#undef GetObject // Disable an annoying macro
|
||||
|
||||
namespace gd {
|
||||
@@ -98,6 +99,19 @@ class GD_CORE_API ObjectsContainer {
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
std::size_t position);
|
||||
/**
|
||||
* \brief Add a new empty object of type \a objectType called \a name in the
|
||||
* given folder at the specified position.<br>
|
||||
*
|
||||
* \note The object is created using the project's current platform.
|
||||
* \return A reference to the object in the list.
|
||||
*/
|
||||
gd::Object& InsertNewObjectInFolder(
|
||||
const gd::Project& project,
|
||||
const gd::String& objectType,
|
||||
const gd::String& name,
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
std::size_t position);
|
||||
|
||||
/**
|
||||
* \brief Add a new object to the list
|
||||
@@ -125,18 +139,18 @@ class GD_CORE_API ObjectsContainer {
|
||||
void MoveObject(std::size_t oldIndex, std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Swap the position of the specified objects.
|
||||
*/
|
||||
void SwapObjects(std::size_t firstObjectIndex, std::size_t secondObjectIndex);
|
||||
|
||||
/**
|
||||
* Move the specified object to another container, removing it from the current one
|
||||
* and adding it to the new one at the specified position.
|
||||
* Move the specified object to another container, removing it from the
|
||||
* current one and adding it to the new one at the specified position in the
|
||||
* given folder.
|
||||
*
|
||||
* \note This does not invalidate the references to the object (object is not moved in memory,
|
||||
* as referenced by smart pointers internally).
|
||||
* \note This does not invalidate the references to the object (object is not
|
||||
* moved in memory, as referenced by smart pointers internally).
|
||||
*/
|
||||
void MoveObjectToAnotherContainer(const gd::String& name, gd::ObjectsContainer & newContainer, std::size_t newPosition);
|
||||
void MoveObjectFolderOrObjectToAnotherContainerInFolder(
|
||||
gd::ObjectFolderOrObject& objectFolderOrObject,
|
||||
gd::ObjectsContainer& newContainer,
|
||||
gd::ObjectFolderOrObject& newParentFolder,
|
||||
std::size_t newPosition);
|
||||
|
||||
/**
|
||||
* Provide a raw access to the vector containing the objects
|
||||
@@ -153,20 +167,43 @@ class GD_CORE_API ObjectsContainer {
|
||||
}
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Returns a vector containing all object and folders in this container.
|
||||
* Only use this for checking if you hold a valid `ObjectFolderOrObject` -
|
||||
* don't use this for rendering or anything else.
|
||||
*/
|
||||
std::vector<const ObjectFolderOrObject*> GetAllObjectFolderOrObjects() const;
|
||||
|
||||
gd::ObjectFolderOrObject& GetRootFolder() {
|
||||
return *rootFolder;
|
||||
}
|
||||
|
||||
void AddMissingObjectsInRootFolder();
|
||||
|
||||
/** \name Saving and loading
|
||||
* Members functions related to saving and loading the objects of the class.
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize instances container.
|
||||
* \brief Serialize the objects container.
|
||||
*/
|
||||
void SerializeObjectsTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the instances container.
|
||||
* \brief Unserialize the objects container.
|
||||
*/
|
||||
void UnserializeObjectsFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
/**
|
||||
* \brief Serialize folder structure.
|
||||
*/
|
||||
void SerializeFoldersTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize folder structure.
|
||||
*/
|
||||
void UnserializeFoldersFrom(gd::Project& project,
|
||||
const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
/** \name Objects groups management
|
||||
@@ -190,6 +227,9 @@ class GD_CORE_API ObjectsContainer {
|
||||
std::vector<std::unique_ptr<gd::Object> >
|
||||
initialObjects; ///< Objects contained.
|
||||
gd::ObjectGroupsContainer objectGroups;
|
||||
|
||||
private:
|
||||
std::unique_ptr<gd::ObjectFolderOrObject> rootFolder;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -51,24 +51,69 @@ bool ObjectsContainersList::HasObjectNamed(const gd::String& name) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectOrGroupWithVariableNamed(
|
||||
ObjectsContainersList::VariableExistence
|
||||
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);
|
||||
return variables.Has(variableName) ? VariableExistence::Exists
|
||||
: VariableExistence::DoesNotExist;
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that a group has a variable if all objects of the
|
||||
// group have it:
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
if (objectNames.empty()) return VariableExistence::GroupIsEmpty;
|
||||
|
||||
bool existsOnAtLeastOneObject = false;
|
||||
bool missingOnAtLeastOneObject = false;
|
||||
for (const auto& objectName : objectNames) {
|
||||
if (!HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
missingOnAtLeastOneObject = true;
|
||||
if (existsOnAtLeastOneObject) {
|
||||
return VariableExistence::ExistsOnlyOnSomeObjectsOfTheGroup;
|
||||
}
|
||||
} else {
|
||||
existsOnAtLeastOneObject = true;
|
||||
if (missingOnAtLeastOneObject) {
|
||||
return VariableExistence::ExistsOnlyOnSomeObjectsOfTheGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (missingOnAtLeastOneObject) {
|
||||
return VariableExistence::DoesNotExist;
|
||||
}
|
||||
|
||||
return VariableExistence::Exists;
|
||||
}
|
||||
}
|
||||
|
||||
return VariableExistence::DoesNotExist;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectWithVariableNamed(
|
||||
const gd::String& objectName, const gd::String& variableName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
const auto& variables = (*it)->GetObject(objectName).GetVariables();
|
||||
return variables.Has(variableName);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasVariablesContainer(
|
||||
bool ObjectsContainersList::HasObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::VariablesContainer& variablesContainer) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
@@ -78,7 +123,31 @@ bool ObjectsContainersList::HasVariablesContainer(
|
||||
&(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// For groups, we consider that the first object of the group defines the
|
||||
// variables available for this group. Note that this is slightly
|
||||
// different than other methods where a group is considered as the
|
||||
// "intersection" of all of its objects.
|
||||
const auto& objectNames =
|
||||
(*it)->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
if (!objectNames.empty()) {
|
||||
return HasObjectVariablesContainer(objectNames[0], variablesContainer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectsContainersList::HasObjectVariablesContainer(
|
||||
const gd::String& objectName,
|
||||
const gd::VariablesContainer& variablesContainer) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
return &variablesContainer ==
|
||||
&(*it)->GetObject(objectName).GetVariables();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,26 +163,154 @@ ObjectsContainersList::GetObjectOrGroupVariablesContainer(
|
||||
return &(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// Could be adapted if objects groups have variables in the future.
|
||||
// For groups, we consider that the first object of the group defines the
|
||||
// variables available for this group. Note that this is slightly
|
||||
// different than other methods where a group is considered as the
|
||||
// "intersection" of all of its objects.
|
||||
const auto& objectNames =
|
||||
(*it)->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
|
||||
|
||||
if (!objectNames.empty()) {
|
||||
return GetObjectVariablesContainer(objectNames[0]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachNameWithPrefix(
|
||||
const gd::String& prefix,
|
||||
const gd::VariablesContainer*
|
||||
ObjectsContainersList::GetObjectVariablesContainer(
|
||||
const gd::String& objectName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
return &(*it)->GetObject(objectName).GetVariables();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectOrGroupVariable(
|
||||
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.Get(variableName).GetType();
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that the first object having the variable will
|
||||
// define its type.
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
|
||||
for (const auto& objectName : objectNames) {
|
||||
if (HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
return GetTypeOfObjectVariable(objectName, variableName);
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
gd::Variable::Type ObjectsContainersList::GetTypeOfObjectVariable(
|
||||
const gd::String& objectName, const gd::String& variableName) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectName)) {
|
||||
const auto& variables = (*it)->GetObject(objectName).GetVariables();
|
||||
|
||||
return variables.Get(variableName).GetType();
|
||||
}
|
||||
}
|
||||
|
||||
return Variable::Type::Number;
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObjectOrGroupVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
variables.ForEachVariableMatchingSearch(search, fn);
|
||||
}
|
||||
if ((*it)->GetObjectGroups().Has(objectOrGroupName)) {
|
||||
// This could be adapted if objects groups have variables in the future.
|
||||
|
||||
// Currently, a group is considered as the "intersection" of all of its
|
||||
// objects. Search "groups is the intersection of its objects" in the
|
||||
// codebase. Consider that a group has a variable if all objects of the
|
||||
// group have it:
|
||||
const auto& objectGroup = (*it)->GetObjectGroups().Get(objectOrGroupName);
|
||||
const auto& objectNames = objectGroup.GetAllObjectsNames();
|
||||
|
||||
if (objectNames.empty()) return;
|
||||
const auto& firstObjectName = objectNames.front();
|
||||
ForEachObjectVariableMatchingSearch(
|
||||
firstObjectName,
|
||||
search,
|
||||
[&](const gd::String& variableName, const gd::Variable& variable) {
|
||||
for (const auto& objectName : objectGroup.GetAllObjectsNames()) {
|
||||
if (!HasObjectWithVariableNamed(objectName, variableName)) {
|
||||
return; // This variable is not shared by all objects of the
|
||||
// group.
|
||||
}
|
||||
}
|
||||
|
||||
// This variable is shared by all objects in the group. Note that
|
||||
// other objects can have it with a different type - we allow this.
|
||||
fn(variableName, variable);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachObjectVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const {
|
||||
for (auto it = objectsContainers.rbegin(); it != objectsContainers.rend();
|
||||
++it) {
|
||||
if ((*it)->HasObjectNamed(objectOrGroupName)) {
|
||||
const auto& variables =
|
||||
(*it)->GetObject(objectOrGroupName).GetVariables();
|
||||
variables.ForEachVariableMatchingSearch(search, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectsContainersList::ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
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)
|
||||
if (object->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(object->GetName(), &object->GetConfiguration());
|
||||
}
|
||||
(*it)->GetObjectGroups().ForEachNameWithPrefix(
|
||||
prefix, [&](const gd::String& name) { fn(name, nullptr); });
|
||||
(*it)->GetObjectGroups().ForEachNameMatchingSearch(
|
||||
search, [&](const gd::String& name) { fn(name, nullptr); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,8 +426,10 @@ gd::String ObjectsContainersList::GetTypeOfBehavior(
|
||||
"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);
|
||||
return gd::GetTypeOfBehavior(*objectsContainers[0],
|
||||
*objectsContainers[1],
|
||||
behaviorName,
|
||||
searchInGroups);
|
||||
}
|
||||
|
||||
std::vector<gd::String> ObjectsContainersList::GetBehaviorsOfObject(
|
||||
|
@@ -42,18 +42,26 @@ class GD_CORE_API ObjectsContainersList {
|
||||
*/
|
||||
bool HasObjectOrGroupNamed(const gd::String& name) const;
|
||||
|
||||
enum VariableExistence {
|
||||
DoesNotExist,
|
||||
Exists,
|
||||
GroupIsEmpty,
|
||||
ExistsOnlyOnSomeObjectsOfTheGroup
|
||||
};
|
||||
|
||||
/**
|
||||
* \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;
|
||||
VariableExistence 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(
|
||||
bool HasObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::VariablesContainer& variablesContainer) const;
|
||||
|
||||
@@ -64,6 +72,11 @@ class GD_CORE_API ObjectsContainersList {
|
||||
const gd::VariablesContainer* GetObjectOrGroupVariablesContainer(
|
||||
const gd::String& objectOrGroupName) const;
|
||||
|
||||
/**
|
||||
* \brief Get a type from an object/group variable.
|
||||
*/
|
||||
gd::Variable::Type GetTypeOfObjectOrGroupVariable(const gd::String& objectOrGroupName, const gd::String& variableName) 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
|
||||
@@ -81,18 +94,21 @@ class GD_CORE_API ObjectsContainersList {
|
||||
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 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;
|
||||
gd::String GetTypeOfBehavior(const gd::String& behaviorName,
|
||||
bool searchInGroups = true) const;
|
||||
|
||||
/**
|
||||
* \brief Get behaviors of an object/group
|
||||
@@ -101,9 +117,8 @@ class GD_CORE_API ObjectsContainersList {
|
||||
*
|
||||
* @return Vector containing names of behaviors
|
||||
*/
|
||||
std::vector<gd::String>
|
||||
GetBehaviorsOfObject(const gd::String& objectName,
|
||||
bool searchInGroups = true) const;
|
||||
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.
|
||||
@@ -121,9 +136,24 @@ class GD_CORE_API ObjectsContainersList {
|
||||
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.
|
||||
* \brief Call the callback for each object or group name matching the
|
||||
* search passed in parameter.
|
||||
*/
|
||||
void ForEachNameWithPrefix(const gd::String& prefix, std::function<void(const gd::String& name, const gd::ObjectConfiguration* objectConfiguration)> fn) const;
|
||||
void ForEachNameMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& name,
|
||||
const gd::ObjectConfiguration* objectConfiguration)>
|
||||
fn) const;
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each variable of the object (or group)
|
||||
* matching the search passed in parameter.
|
||||
*/
|
||||
void ForEachObjectOrGroupVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
@@ -132,6 +162,24 @@ class GD_CORE_API ObjectsContainersList {
|
||||
private:
|
||||
bool HasObjectNamed(const gd::String& name) const;
|
||||
|
||||
bool HasObjectWithVariableNamed(const gd::String& objectName,
|
||||
const gd::String& variableName) const;
|
||||
|
||||
bool HasObjectVariablesContainer(
|
||||
const gd::String& objectName,
|
||||
const gd::VariablesContainer& variablesContainer) const;
|
||||
|
||||
const gd::VariablesContainer* GetObjectVariablesContainer(
|
||||
const gd::String& objectName) const;
|
||||
|
||||
gd::Variable::Type GetTypeOfObjectVariable(const gd::String& objectName, const gd::String& variableName) const;
|
||||
|
||||
void ForEachObjectVariableMatchingSearch(
|
||||
const gd::String& objectOrGroupName,
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::String& variableName,
|
||||
const gd::Variable& variable)> fn) const;
|
||||
|
||||
void Add(const gd::ObjectsContainer& objectsContainer) {
|
||||
objectsContainers.push_back(&objectsContainer);
|
||||
};
|
||||
|
@@ -48,11 +48,6 @@ using namespace std;
|
||||
|
||||
namespace gd {
|
||||
|
||||
// By default, disallow unicode in identifiers, but this can be set to true
|
||||
// by the IDE. In the future, this will be set to true by default, keeping backward compatibility.
|
||||
// We keep it disabled by default to progressively ask users to test it in real projects.
|
||||
bool Project::allowUsageOfUnicodeIdentifierNames = false;
|
||||
|
||||
Project::Project()
|
||||
: name(_("Project")),
|
||||
version("1.0.0"),
|
||||
@@ -86,26 +81,24 @@ Project::~Project() {}
|
||||
|
||||
void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
|
||||
|
||||
std::unique_ptr<gd::Object>
|
||||
Project::CreateObject(const gd::String &objectType, const gd::String &name) const {
|
||||
std::unique_ptr<gd::Object> object =
|
||||
gd::make_unique<Object>(name, objectType, CreateObjectConfiguration(objectType));
|
||||
std::unique_ptr<gd::Object> Project::CreateObject(
|
||||
const gd::String& objectType, const gd::String& name) const {
|
||||
std::unique_ptr<gd::Object> object = gd::make_unique<Object>(
|
||||
name, objectType, CreateObjectConfiguration(objectType));
|
||||
|
||||
auto &platform = GetCurrentPlatform();
|
||||
auto &project = *this;
|
||||
auto addDefaultBehavior =
|
||||
[&platform,
|
||||
&project,
|
||||
&object,
|
||||
&objectType](const gd::String& behaviorType) {
|
||||
auto &behaviorMetadata =
|
||||
auto& platform = GetCurrentPlatform();
|
||||
auto& project = *this;
|
||||
auto addDefaultBehavior = [&platform, &project, &object, &objectType](
|
||||
const gd::String& behaviorType) {
|
||||
auto& behaviorMetadata =
|
||||
gd::MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
|
||||
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
|
||||
gd::LogWarning("Object: " + objectType + " has an unknown default behavior: " + behaviorType);
|
||||
gd::LogWarning("Object: " + objectType +
|
||||
" has an unknown default behavior: " + behaviorType);
|
||||
return;
|
||||
}
|
||||
auto* behavior = object->AddNewBehavior(project, behaviorType,
|
||||
behaviorMetadata.GetDefaultName());
|
||||
auto* behavior = object->AddNewBehavior(
|
||||
project, behaviorType, behaviorMetadata.GetDefaultName());
|
||||
behavior->SetDefaultBehavior(true);
|
||||
};
|
||||
|
||||
@@ -114,18 +107,17 @@ Project::CreateObject(const gd::String &objectType, const gd::String &name) cons
|
||||
addDefaultBehavior("ResizableCapability::ResizableBehavior");
|
||||
addDefaultBehavior("ScalableCapability::ScalableBehavior");
|
||||
addDefaultBehavior("FlippableCapability::FlippableBehavior");
|
||||
}
|
||||
else {
|
||||
auto &objectMetadata = gd::MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
} else {
|
||||
auto& objectMetadata =
|
||||
gd::MetadataProvider::GetObjectMetadata(platform, objectType);
|
||||
if (MetadataProvider::IsBadObjectMetadata(objectMetadata)) {
|
||||
gd::LogWarning("Object: " + name + " has an unknown type: " + objectType);
|
||||
}
|
||||
for (auto &behaviorType : objectMetadata.GetDefaultBehaviors()) {
|
||||
for (auto& behaviorType : objectMetadata.GetDefaultBehaviors()) {
|
||||
addDefaultBehavior(behaviorType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return std::move(object);
|
||||
}
|
||||
|
||||
@@ -849,6 +841,11 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
resourcesManager.UnserializeFrom(
|
||||
element.GetChild("resources", 0, "Resources"));
|
||||
UnserializeObjectsFrom(*this, element.GetChild("objects", 0, "Objects"));
|
||||
if (element.HasChild("objectsFolderStructure")) {
|
||||
UnserializeFoldersFrom(*this, element.GetChild("objectsFolderStructure", 0));
|
||||
}
|
||||
AddMissingObjectsInRootFolder();
|
||||
|
||||
GetVariables().UnserializeFrom(element.GetChild("variables", 0, "Variables"));
|
||||
|
||||
scenes.clear();
|
||||
@@ -1000,6 +997,7 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
|
||||
resourcesManager.SerializeTo(element.AddChild("resources"));
|
||||
SerializeObjectsTo(element.AddChild("objects"));
|
||||
SerializeFoldersTo(element.AddChild("objectsFolderStructure"));
|
||||
GetObjectGroups().SerializeTo(element.AddChild("objectsGroups"));
|
||||
GetVariables().SerializeTo(element.AddChild("variables"));
|
||||
|
||||
@@ -1038,28 +1036,18 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
externalSourceFilesElement.AddChild("sourceFile"));
|
||||
}
|
||||
|
||||
void Project::AllowUsageOfUnicodeIdentifierNames(bool enable) {
|
||||
allowUsageOfUnicodeIdentifierNames = enable;
|
||||
}
|
||||
|
||||
bool Project::IsNameSafe(const gd::String& name) {
|
||||
if (name.empty()) return false;
|
||||
|
||||
if (isdigit(name[0])) return false;
|
||||
|
||||
if (!allowUsageOfUnicodeIdentifierNames) {
|
||||
gd::String legacyAllowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
return !(name.find_first_not_of(legacyAllowedCharacters) != gd::String::npos);
|
||||
} else {
|
||||
for (auto character : name) {
|
||||
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
|
||||
return false;
|
||||
}
|
||||
for (auto character : name) {
|
||||
if (!GrammarTerminals::IsAllowedInIdentifier(character)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String Project::GetSafeName(const gd::String& name) {
|
||||
@@ -1069,18 +1057,13 @@ gd::String Project::GetSafeName(const gd::String& name) {
|
||||
|
||||
if (isdigit(name[0])) newName = "_" + newName;
|
||||
|
||||
gd::String legacyAllowedCharacters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
|
||||
for (size_t i = 0;i < newName.size();++i) {
|
||||
// Note that iterating on the characters is not super efficient (O(n^2), which
|
||||
// could be avoided with an iterator), but this function is not critical for performance
|
||||
// (only used to generate a name when a user creates a new entity or rename one).
|
||||
for (size_t i = 0; i < newName.size(); ++i) {
|
||||
// Note that iterating on the characters is not super efficient (O(n^2),
|
||||
// which could be avoided with an iterator), but this function is not
|
||||
// critical for performance (only used to generate a name when a user
|
||||
// creates a new entity or rename one).
|
||||
auto character = newName[i];
|
||||
bool isAllowed =
|
||||
allowUsageOfUnicodeIdentifierNames
|
||||
? GrammarTerminals::IsAllowedInIdentifier(character)
|
||||
: legacyAllowedCharacters.find(character) != gd::String::npos;
|
||||
bool isAllowed = GrammarTerminals::IsAllowedInIdentifier(character);
|
||||
|
||||
// Replace all unallowed letters by an underscore.
|
||||
if (!isAllowed) {
|
||||
|
@@ -11,12 +11,12 @@
|
||||
|
||||
#include "GDCore/Project/ExtensionProperties.h"
|
||||
#include "GDCore/Project/LoadingScreen.h"
|
||||
#include "GDCore/Project/Watermark.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
#include "GDCore/Project/PlatformSpecificAssets.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/Project/Watermark.h"
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
class Platform;
|
||||
@@ -82,7 +82,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Change the project description
|
||||
*/
|
||||
void SetDescription(const gd::String& description_) { description = description_; };
|
||||
void SetDescription(const gd::String& description_) {
|
||||
description = description_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the project description
|
||||
@@ -124,7 +126,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Get the author usernames of the project.
|
||||
*/
|
||||
const std::vector<gd::String>& GetAuthorUsernames() const { return authorUsernames; };
|
||||
const std::vector<gd::String>& GetAuthorUsernames() const {
|
||||
return authorUsernames;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Get the author usernames of the project, to modify them (non-const).
|
||||
@@ -135,7 +139,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable with a keyboard.
|
||||
* \param enable True to define the project as playable with a keyboard.
|
||||
*/
|
||||
void SetPlayableWithKeyboard(bool playable = true) { isPlayableWithKeyboard = playable; }
|
||||
void SetPlayableWithKeyboard(bool playable = true) {
|
||||
isPlayableWithKeyboard = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable with a keyboard.
|
||||
@@ -146,7 +152,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable with a gamepad.
|
||||
* \param enable True to define the project as playable with a gamepad.
|
||||
*/
|
||||
void SetPlayableWithGamepad(bool playable = true) { isPlayableWithGamepad = playable; }
|
||||
void SetPlayableWithGamepad(bool playable = true) {
|
||||
isPlayableWithGamepad = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable with a gamepad.
|
||||
@@ -157,7 +165,9 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
* Define the project as playable on a mobile.
|
||||
* \param enable True to define the project as playable on a mobile.
|
||||
*/
|
||||
void SetPlayableWithMobile(bool playable = true) { isPlayableWithMobile = playable; }
|
||||
void SetPlayableWithMobile(bool playable = true) {
|
||||
isPlayableWithMobile = playable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project is defined as playable on a mobile.
|
||||
@@ -391,17 +401,23 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* Set the antialiasing mode used by the game ("none" or "MSAA").
|
||||
*/
|
||||
void SetAntialiasingMode(const gd::String& antialiasingMode_) { antialiasingMode = antialiasingMode_; }
|
||||
void SetAntialiasingMode(const gd::String& antialiasingMode_) {
|
||||
antialiasingMode = antialiasingMode_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if antialising is enabled on mobiles.
|
||||
*/
|
||||
bool IsAntialisingEnabledOnMobile() const { return isAntialisingEnabledOnMobile; }
|
||||
bool IsAntialisingEnabledOnMobile() const {
|
||||
return isAntialisingEnabledOnMobile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether antialising is enabled on mobiles or not.
|
||||
*/
|
||||
void SetAntialisingEnabledOnMobile(bool enable) { isAntialisingEnabledOnMobile = enable; }
|
||||
void SetAntialisingEnabledOnMobile(bool enable) {
|
||||
isAntialisingEnabledOnMobile = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return if the project should set 0 as Z-order for objects created
|
||||
@@ -905,7 +921,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Return the events based object with a given type.
|
||||
*/
|
||||
const gd::EventsBasedObject& GetEventsBasedObject(const gd::String& type) const;
|
||||
const gd::EventsBasedObject& GetEventsBasedObject(
|
||||
const gd::String& type) const;
|
||||
|
||||
/**
|
||||
* \brief Check if events based behavior with a given type exists.
|
||||
@@ -920,7 +937,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
/**
|
||||
* \brief Return the events based behavior with a given type.
|
||||
*/
|
||||
const gd::EventsBasedBehavior& GetEventsBasedBehavior(const gd::String& type) const;
|
||||
const gd::EventsBasedBehavior& GetEventsBasedBehavior(
|
||||
const gd::String& type) const;
|
||||
|
||||
///@}
|
||||
|
||||
@@ -969,20 +987,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
*/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Check if unicode names are allowed in identifier names.
|
||||
* \see IsNameSafe
|
||||
* \see GetSafeName
|
||||
*/
|
||||
static bool IsUsageOfUnicodeIdentifierNamesAllowed() { return allowUsageOfUnicodeIdentifierNames; };
|
||||
|
||||
/**
|
||||
* Set if unicode names are allowed in identifier names.
|
||||
* \see IsNameSafe
|
||||
* \see GetSafeName
|
||||
*/
|
||||
static void AllowUsageOfUnicodeIdentifierNames(bool enable);
|
||||
|
||||
/**
|
||||
* Return true if \a name is valid (can be used safely for an object,
|
||||
* behavior, events function name, etc...).
|
||||
@@ -990,8 +994,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
static bool IsNameSafe(const gd::String& name);
|
||||
|
||||
/**
|
||||
* Return a name, based on the one passed in parameter, that can be safely used
|
||||
* for an object, behavior, events function name, etc...
|
||||
* Return a name, based on the one passed in parameter, that can be safely
|
||||
* used for an object, behavior, events function name, etc...
|
||||
*/
|
||||
static gd::String GetSafeName(const gd::String& name);
|
||||
///@}
|
||||
@@ -1068,8 +1072,8 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
bool adaptGameResolutionAtRuntime; ///< Should the game resolution be adapted
|
||||
///< to the window size at runtime
|
||||
gd::String
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
sizeOnStartupMode; ///< How to adapt the game size to the screen. Can be
|
||||
///< "adaptWidth", "adaptHeight" or empty
|
||||
gd::String antialiasingMode;
|
||||
bool isAntialisingEnabledOnMobile;
|
||||
gd::String projectUuid; ///< UUID useful to identify the game in online
|
||||
@@ -1095,19 +1099,18 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
externalSourceFiles; ///< List of external source files used.
|
||||
gd::String author; ///< Game author name, for publishing purpose.
|
||||
std::vector<gd::String>
|
||||
authorIds; ///< Game author ids, from GDevelop users DB.
|
||||
authorIds; ///< Game author ids, from GDevelop users DB.
|
||||
std::vector<gd::String>
|
||||
authorUsernames; ///< Game author usernames, from GDevelop users DB.
|
||||
std::vector<gd::String>
|
||||
categories; ///< Game categories
|
||||
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String templateSlug; ///< The slug of the template from which the game is
|
||||
///< created.
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
authorUsernames; ///< Game author usernames, from GDevelop users DB.
|
||||
std::vector<gd::String> categories; ///< Game categories
|
||||
bool isPlayableWithKeyboard; ///< The project is playable with a keyboard.
|
||||
bool isPlayableWithGamepad; ///< The project is playable with a gamepad.
|
||||
bool isPlayableWithMobile; ///< The project is playable on a mobile.
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String templateSlug; ///< The slug of the template from which the game is
|
||||
///< created.
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices).
|
||||
///< "default", "landscape" or "portrait".
|
||||
bool
|
||||
folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String
|
||||
@@ -1128,8 +1131,6 @@ class GD_CORE_API Project : public ObjectsContainer {
|
||||
///< time the project was saved.
|
||||
mutable unsigned int gdBuildVersion; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
|
||||
static bool allowUsageOfUnicodeIdentifierNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -97,8 +97,8 @@ class ProjectScopedContainers {
|
||||
return notFoundCallback();
|
||||
};
|
||||
|
||||
void ForEachIdentifierWithPrefix(
|
||||
const gd::String &prefix,
|
||||
void ForEachIdentifierMatchingSearch(
|
||||
const gd::String &search,
|
||||
std::function<void(const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration)>
|
||||
objectCallback,
|
||||
@@ -110,8 +110,8 @@ class ProjectScopedContainers {
|
||||
parameterCallback) const {
|
||||
std::set<gd::String> namesAlreadySeen;
|
||||
|
||||
objectsContainersList.ForEachNameWithPrefix(
|
||||
prefix,
|
||||
objectsContainersList.ForEachNameMatchingSearch(
|
||||
search,
|
||||
[&](const gd::String &name,
|
||||
const ObjectConfiguration *objectConfiguration) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
@@ -119,24 +119,24 @@ class ProjectScopedContainers {
|
||||
objectCallback(name, objectConfiguration);
|
||||
}
|
||||
});
|
||||
variablesContainersList.ForEachVariableWithPrefix(
|
||||
prefix, [&](const gd::String &name, const gd::Variable &variable) {
|
||||
variablesContainersList.ForEachVariableMatchingSearch(
|
||||
search, [&](const gd::String &name, const gd::Variable &variable) {
|
||||
if (namesAlreadySeen.count(name) == 0) {
|
||||
namesAlreadySeen.insert(name);
|
||||
variableCallback(name, variable);
|
||||
}
|
||||
});
|
||||
gd::ParameterMetadataTools::ForEachParameterWithPrefix(
|
||||
gd::ParameterMetadataTools::ForEachParameterMatchingSearch(
|
||||
parametersVectorsList,
|
||||
prefix,
|
||||
search,
|
||||
[&](const gd::ParameterMetadata ¶meter) {
|
||||
if (namesAlreadySeen.count(parameter.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(parameter.GetName());
|
||||
parameterCallback(parameter);
|
||||
}
|
||||
});
|
||||
propertiesContainersList.ForEachPropertyWithPrefix(
|
||||
prefix, [&](const gd::NamedPropertyDescriptor &property) {
|
||||
propertiesContainersList.ForEachPropertyMatchingSearch(
|
||||
search, [&](const gd::NamedPropertyDescriptor &property) {
|
||||
if (namesAlreadySeen.count(property.GetName()) == 0) {
|
||||
namesAlreadySeen.insert(property.GetName());
|
||||
propertyCallback(property);
|
||||
|
@@ -31,9 +31,13 @@ class PropertiesContainer
|
||||
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);
|
||||
void ForEachPropertyMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::NamedPropertyDescriptor& property)> fn)
|
||||
const {
|
||||
for (const auto& property : elements) {
|
||||
if (property->GetName().FindCaseInsensitive(search) != gd::String::npos)
|
||||
fn(*property);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -55,12 +55,12 @@ bool PropertiesContainersList::HasPropertiesContainer(const gd::PropertiesContai
|
||||
return false;
|
||||
}
|
||||
|
||||
void PropertiesContainersList::ForEachPropertyWithPrefix(
|
||||
const gd::String& prefix,
|
||||
void PropertiesContainersList::ForEachPropertyMatchingSearch(
|
||||
const gd::String& search,
|
||||
std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const {
|
||||
for (auto it = propertiesContainers.rbegin(); it != propertiesContainers.rend();
|
||||
++it) {
|
||||
(*it)->ForEachPropertyWithPrefix(prefix, fn);
|
||||
(*it)->ForEachPropertyMatchingSearch(search, fn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -67,9 +67,9 @@ class GD_CORE_API PropertiesContainersList {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Call the callback for each property having a name starting with the specified prefix.
|
||||
* \brief Call the callback for each property having a name matching the specified search.
|
||||
*/
|
||||
void ForEachPropertyWithPrefix(const gd::String& prefix, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const;
|
||||
void ForEachPropertyMatchingSearch(const gd::String& search, std::function<void(const gd::NamedPropertyDescriptor& property)> fn) const;
|
||||
|
||||
/** Do not use - should be private but accessible to let Emscripten create a
|
||||
* temporary. */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user