mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
10 Commits
v5.0.146
...
feature/re
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d5f73bea0d | ||
![]() |
32c69ab78a | ||
![]() |
3dd5e2c242 | ||
![]() |
fbfcd3da5d | ||
![]() |
0db30f02c9 | ||
![]() |
a852e91690 | ||
![]() |
79d6281061 | ||
![]() |
eba6b2540c | ||
![]() |
0706a54305 | ||
![]() |
d929fd6e48 |
@@ -22,6 +22,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
"object or a position.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Camera")
|
||||
.SetExtensionHelpPath("/interface/scene-editor/layers-and-cameras");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Layers and cameras"))
|
||||
.SetIcon("res/conditions/camera24.png");
|
||||
@@ -589,6 +590,19 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
.SetDefaultValue("\"\"")
|
||||
.AddParameter("expression", _("New default Z order"));
|
||||
|
||||
extension
|
||||
.AddExpressionAndCondition("number",
|
||||
"LayerHighestZOrder",
|
||||
_("Layer highest Z order"),
|
||||
_("the highest Z order of objects in a layer"),
|
||||
_("the highest Z order of objects in the layer _PARAM1_"),
|
||||
"",
|
||||
"res/conditions/layer.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("layer", _("Layer (base layer if empty)"), "", true)
|
||||
.SetDefaultValue("\"\"")
|
||||
.UseStandardParameters("number");
|
||||
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerAmbientLightColor",
|
||||
|
@@ -21,7 +21,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsFileExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/storage")
|
||||
.SetCategory("Device");
|
||||
.SetCategory("Advanced");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Storage"))
|
||||
.SetIcon("res/conditions/fichier24.png");
|
||||
|
||||
|
@@ -22,6 +22,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension(
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/sprite");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Sprite"))
|
||||
.SetIcon("CppPlatform/Extensions/spriteicon.png");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
extension
|
||||
|
@@ -20,6 +20,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
"these features can be applied.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/all-features/window");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(
|
||||
|
@@ -28,7 +28,7 @@ module.exports = {
|
||||
'Arthur Pacaud (arthuro555)',
|
||||
'MIT'
|
||||
)
|
||||
.setCategory('Device');
|
||||
.setCategory('User interface');
|
||||
extension
|
||||
.addInstructionOrExpressionGroupMetadata(_('Advanced window management'))
|
||||
.setIcon('res/actions/window24.png');
|
||||
|
@@ -17,6 +17,7 @@ void DeclareAnchorBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Anchor objects to the window's bounds."),
|
||||
"Victor Levasseur",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/behaviors/anchor");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
|
@@ -33,7 +33,10 @@ module.exports = {
|
||||
'Todor Imreorov',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/objects/bbtext');
|
||||
.setExtensionHelpPath('/objects/bbtext')
|
||||
.setCategory('User interface');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("BBCode Text Object"))
|
||||
.setIcon("JsPlatform/Extensions/bbcode32.png");
|
||||
|
||||
var objectBBText = new gd.ObjectJsImplementation();
|
||||
// $FlowExpectedError
|
||||
@@ -168,7 +171,7 @@ module.exports = {
|
||||
.addIncludeFile(
|
||||
'Extensions/BBText/pixi-multistyle-text/dist/pixi-multistyle-text.umd.js'
|
||||
)
|
||||
.setCategoryFullName(_('Texts'));
|
||||
.setCategoryFullName(_('User interface'));
|
||||
|
||||
/**
|
||||
* Utility function to add both a setter and a getter to a property from a list.
|
||||
|
@@ -35,7 +35,10 @@ module.exports = {
|
||||
'Aurélien Vivet',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/objects/bitmap_text');
|
||||
.setExtensionHelpPath('/objects/bitmap_text')
|
||||
.setCategory('User interface');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Bitmap Text"))
|
||||
.setIcon("JsPlatform/Extensions/bitmapfont32.png");
|
||||
|
||||
const bitmapTextObject = new gd.ObjectJsImplementation();
|
||||
// $FlowExpectedError
|
||||
@@ -171,7 +174,7 @@ module.exports = {
|
||||
.addIncludeFile(
|
||||
'Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js'
|
||||
)
|
||||
.setCategoryFullName(_('Texts'));
|
||||
.setCategoryFullName(_('User interface'));
|
||||
|
||||
object
|
||||
.addExpressionAndConditionAndAction(
|
||||
|
@@ -20,6 +20,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"or other short-lived objects."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Game mechanic")
|
||||
.SetExtensionHelpPath("/behaviors/destroyoutside");
|
||||
|
||||
gd::BehaviorMetadata& aut =
|
||||
|
@@ -31,7 +31,7 @@ module.exports = {
|
||||
"Matthias Meike",
|
||||
"Open source (MIT License)"
|
||||
).setExtensionHelpPath("/all-features/device-sensors")
|
||||
.setCategory('Device');
|
||||
.setCategory('Input');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Device sensors"))
|
||||
.setIcon("JsPlatform/Extensions/orientation_active32.png");
|
||||
|
||||
|
@@ -35,7 +35,7 @@ module.exports = {
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/device-vibration')
|
||||
.setCategory('Device');
|
||||
.setCategory('User interface');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Device vibration"))
|
||||
.setIcon("JsPlatform/Extensions/vibration_start32.png");
|
||||
|
||||
|
@@ -34,7 +34,7 @@ module.exports = {
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/dialogue-tree')
|
||||
.setCategory('Advanced');
|
||||
.setCategory('Game mechanic');
|
||||
extension
|
||||
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree (experimental)'))
|
||||
.setIcon('JsPlatform/Extensions/yarn32.png');
|
||||
|
@@ -20,6 +20,7 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"or disable the behavior when needed."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/behaviors/draggable");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
|
@@ -31,7 +31,9 @@ module.exports = {
|
||||
'Lots of different effects to be used in your game.',
|
||||
'Various contributors from PixiJS, PixiJS filters and GDevelop',
|
||||
'MIT'
|
||||
).setExtensionHelpPath('/interface/scene-editor/layer-effects');
|
||||
)
|
||||
.setCategory('Visual effect')
|
||||
.setExtensionHelpPath('/interface/scene-editor/layer-effects');
|
||||
|
||||
// ℹ️ You can declare an effect here. Please order the effects by alphabetical order.
|
||||
// This file is for common effects that are well-known/"battle-tested". If you have an
|
||||
|
@@ -34,7 +34,7 @@ module.exports = {
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/filesystem')
|
||||
.setCategory('Device');
|
||||
.setCategory('Advanced');
|
||||
extension
|
||||
.addInstructionOrExpressionGroupMetadata(_('File system'))
|
||||
.setIcon('JsPlatform/Extensions/filesystem_create_folder32.png');
|
||||
|
@@ -19,7 +19,7 @@ void DeclareInventoryExtension(gd::PlatformExtension& extension) {
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/all-features/inventory")
|
||||
.SetCategory("Advanced");
|
||||
.SetCategory("Game mechanic");
|
||||
extension
|
||||
.AddInstructionOrExpressionGroupMetadata(_("Inventories"))
|
||||
.SetIcon("CppPlatform/Extensions/Inventoryicon.png");
|
||||
|
@@ -32,7 +32,8 @@ module.exports = {
|
||||
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||||
'Harsimran Virk',
|
||||
'MIT'
|
||||
);
|
||||
)
|
||||
.setCategory('Visual effect');
|
||||
|
||||
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||||
@@ -195,7 +196,7 @@ module.exports = {
|
||||
.setIncludeFile('Extensions/Lighting/lightruntimeobject.js')
|
||||
.addIncludeFile('Extensions/Lighting/lightruntimeobject-pixi-renderer.js')
|
||||
.addIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
|
||||
.setCategoryFullName(_('Lights'));
|
||||
.setCategoryFullName(_('Visual effect'));
|
||||
|
||||
object
|
||||
.addAction(
|
||||
|
@@ -24,6 +24,8 @@ void DeclarePanelSpriteObjectExtension(gd::PlatformExtension& extension) {
|
||||
"Victor Levasseur and Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/panel_sprite");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Panel Sprite (9-patch) Object"))
|
||||
.SetIcon("CppPlatform/Extensions/PanelSpriteIcon.png");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
extension
|
||||
|
@@ -25,7 +25,10 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
|
||||
"explosions, magical effects, etc...",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Visual effect")
|
||||
.SetExtensionHelpPath("/objects/particles_emitter");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Particle system"))
|
||||
.SetIcon("CppPlatform/Extensions/particleSystemicon.png");
|
||||
|
||||
// Declaration of all objects available
|
||||
{
|
||||
@@ -37,7 +40,7 @@ void DeclareParticleSystemExtension(gd::PlatformExtension& extension) {
|
||||
_("Displays a large number of small particles to create visual "
|
||||
"effects."),
|
||||
"CppPlatform/Extensions/particleSystemicon.png")
|
||||
.SetCategoryFullName(_("General"));
|
||||
.SetCategoryFullName(_("Visual effect"));
|
||||
|
||||
// Declaration is too big to be compiled by GCC in one file, unless you have
|
||||
// 4GB+ ram. :/
|
||||
|
@@ -21,7 +21,10 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"avoiding obstacles on the way.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Movement")
|
||||
.SetExtensionHelpPath("/behaviors/pathfinding");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Pathfinding behavior"))
|
||||
.SetIcon("CppPlatform/Extensions/AStaricon16.png");
|
||||
|
||||
{
|
||||
gd::BehaviorMetadata& aut =
|
||||
|
@@ -34,7 +34,7 @@ module.exports = {
|
||||
'MIT'
|
||||
)
|
||||
.setExtensionHelpPath('/behaviors/physics2')
|
||||
.setCategory('Advanced');
|
||||
.setCategory('Movement');
|
||||
extension
|
||||
.addInstructionOrExpressionGroupMetadata(_('Physics Engine 2.0'))
|
||||
.setIcon('res/physics32.png');
|
||||
|
@@ -21,7 +21,10 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"This is the old, deprecated physics engine. Prefer to use the Physics Engine 2.0.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Movement")
|
||||
.SetExtensionHelpPath("/behaviors/physics");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Physics Engine (deprecated)"))
|
||||
.SetIcon("res/physics16.png");
|
||||
|
||||
{
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
|
@@ -27,6 +27,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"could be used.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Movement")
|
||||
.SetExtensionHelpPath("/behaviors/platformer");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Platform behavior"))
|
||||
.SetIcon("CppPlatform/Extensions/platformerobjecticon.png");
|
||||
|
@@ -20,6 +20,8 @@ void DeclarePrimitiveDrawingExtension(gd::PlatformExtension& extension) {
|
||||
"Florian Rival and Aurélien Vivet",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/shape_painter");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Shape painter"))
|
||||
.SetIcon("CppPlatform/Extensions/primitivedrawingicon.png");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
extension
|
||||
|
@@ -34,7 +34,7 @@ module.exports = {
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setExtensionHelpPath('/all-features/screenshot')
|
||||
.setCategory('Device');
|
||||
.setCategory('Advanced');
|
||||
extension
|
||||
.addInstructionOrExpressionGroupMetadata(_('Screenshot'))
|
||||
.setIcon('JsPlatform/Extensions/take_screenshot32.png');
|
||||
|
@@ -15,7 +15,7 @@ void DeclareSystemInfoExtension(gd::PlatformExtension& extension) {
|
||||
_("Get information about the system and device running the game."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Device");
|
||||
.SetCategory("Advanced");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("System information"))
|
||||
.SetIcon("CppPlatform/Extensions/systeminfoicon.png");
|
||||
|
||||
|
@@ -18,6 +18,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
|
||||
"entered with a keyboard by a player."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/objects/text_entry");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
@@ -27,7 +28,7 @@ void DeclareTextEntryObjectExtension(gd::PlatformExtension& extension) {
|
||||
_("Invisible object used to get the text "
|
||||
"entered with the keyboard."),
|
||||
"CppPlatform/Extensions/textentry.png")
|
||||
.SetCategoryFullName(_("Advanced"));
|
||||
.SetCategoryFullName(_("User interface"));
|
||||
|
||||
obj.AddAction("String",
|
||||
_("Text in memory"),
|
||||
|
@@ -31,7 +31,10 @@ module.exports = {
|
||||
_('A text field the player can type text into.'),
|
||||
'Florian Rival',
|
||||
'MIT'
|
||||
);
|
||||
)
|
||||
.setCategory('User interface');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Text Input"))
|
||||
.setIcon("JsPlatform/Extensions/text_input.svg");
|
||||
|
||||
const textInputObject = new gd.ObjectJsImplementation();
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||||
@@ -277,7 +280,7 @@ module.exports = {
|
||||
'JsPlatform/Extensions/text_input.svg',
|
||||
textInputObject
|
||||
)
|
||||
.setCategoryFullName(_('Form control'))
|
||||
.setCategoryFullName(_('User interface'))
|
||||
.addUnsupportedBaseObjectCapability('effect')
|
||||
.setIncludeFile('Extensions/TextInput/textinputruntimeobject.js')
|
||||
.addIncludeFile(
|
||||
|
@@ -23,7 +23,10 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
|
||||
"some indicators, menu buttons, dialogues..."),
|
||||
"Florian Rival and Victor Levasseur",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("User interface")
|
||||
.SetExtensionHelpPath("/objects/text");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Text object"))
|
||||
.SetIcon("CppPlatform/Extensions/texticon.png");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
extension
|
||||
@@ -31,7 +34,7 @@ void DeclareTextObjectExtension(gd::PlatformExtension& extension) {
|
||||
_("Text"),
|
||||
_("Displays a text on the screen."),
|
||||
"CppPlatform/Extensions/texticon.png")
|
||||
.SetCategoryFullName(_("Texts"));
|
||||
.SetCategoryFullName(_("User interface"));
|
||||
|
||||
obj.AddAction("String",
|
||||
_("Modify the text"),
|
||||
|
@@ -858,7 +858,10 @@ module.exports = {
|
||||
'Todor Imreorov',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setCategory('Advanced')
|
||||
.setExtensionHelpPath('/objects/tilemap');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Tilemap"))
|
||||
.setIcon("JsPlatform/Extensions/tile_map.svg");
|
||||
|
||||
defineTileMap(extension, _, gd);
|
||||
defineCollisionMask(extension, _, gd);
|
||||
|
@@ -121,10 +121,10 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
|
||||
// TODO find a clean way to wait for the json to be read.
|
||||
for (
|
||||
let index = 0;
|
||||
index < 200 && tileMap._collisionTileMap.getDimensionX() === 0;
|
||||
index < 100 && tileMap._collisionTileMap.getDimensionX() === 0;
|
||||
index++
|
||||
) {
|
||||
await delay(10);
|
||||
await delay(20);
|
||||
}
|
||||
if (tileMap._collisionTileMap.getDimensionX() === 0) {
|
||||
throw new Error('Timeout reading the tile map JSON file.');
|
||||
|
@@ -24,6 +24,8 @@ void DeclareTiledSpriteObjectExtension(gd::PlatformExtension& extension) {
|
||||
"Victor Levasseur and Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetExtensionHelpPath("/objects/tiled_sprite");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Tiled Sprite Object"))
|
||||
.SetIcon("CppPlatform/Extensions/TiledSpriteIcon.png");
|
||||
|
||||
gd::ObjectMetadata& obj =
|
||||
extension
|
||||
|
@@ -20,7 +20,10 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"keyboard or using events."),
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Movement")
|
||||
.SetExtensionHelpPath("/behaviors/topdown");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Top-down movement"))
|
||||
.SetIcon("CppPlatform/Extensions/topdownmovementicon16.png");
|
||||
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"TopDownMovementBehavior",
|
||||
|
@@ -73,7 +73,10 @@ module.exports = {
|
||||
'Matthias Meike, Florian Rival',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setCategory('Visual effect')
|
||||
.setExtensionHelpPath('/behaviors/tween');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Tweening"))
|
||||
.setIcon("JsPlatform/Extensions/tween_behavior32.png");
|
||||
|
||||
extension
|
||||
.addExpression(
|
||||
|
@@ -28,12 +28,15 @@ module.exports = {
|
||||
extension
|
||||
.setExtensionInformation(
|
||||
'Video',
|
||||
'Video',
|
||||
'Provides an object to display a video on the scene. The recommended file format is MPEG4, with H264 video codec and AAC audio codec, to maximize the support of the video on different platform and browsers.',
|
||||
_('Video'),
|
||||
_('Provides an object to display a video on the scene. The recommended file format is MPEG4, with H264 video codec and AAC audio codec, to maximize the support of the video on different platform and browsers.'),
|
||||
'Aurélien Vivet',
|
||||
'Open source (MIT License)'
|
||||
)
|
||||
.setCategory('User interface')
|
||||
.setExtensionHelpPath('/objects/video');
|
||||
extension.addInstructionOrExpressionGroupMetadata(_("Video"))
|
||||
.setIcon("JsPlatform/Extensions/videoicon16.png");
|
||||
|
||||
var videoObject = new gd.ObjectJsImplementation();
|
||||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||||
@@ -133,7 +136,7 @@ module.exports = {
|
||||
)
|
||||
.setIncludeFile('Extensions/Video/videoruntimeobject.js')
|
||||
.addIncludeFile('Extensions/Video/videoruntimeobject-pixi-renderer.js')
|
||||
.setCategoryFullName(_('Multimedia'));
|
||||
.setCategoryFullName(_('User interface'));
|
||||
|
||||
object
|
||||
.addAction(
|
||||
|
@@ -128,6 +128,10 @@ CameraExtension::CameraExtension() {
|
||||
GetAllExpressions()["LayerTimeScale"].SetFunctionName(
|
||||
"gdjs.evtTools.camera.getLayerTimeScale");
|
||||
|
||||
GetAllConditions()["LayerHighestZOrder"].SetFunctionName(
|
||||
"gdjs.evtTools.layer.getLayerHighestZOrder");
|
||||
GetAllExpressions()["LayerHighestZOrder"].SetFunctionName(
|
||||
"gdjs.evtTools.layer.getLayerHighestZOrder");
|
||||
GetAllConditions()["LayerDefaultZOrder"].SetFunctionName(
|
||||
"gdjs.evtTools.camera.getLayerDefaultZOrder");
|
||||
GetAllActions()["SetLayerDefaultZOrder"].SetFunctionName(
|
||||
|
@@ -451,6 +451,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param runtimeScene The scene
|
||||
* @param layerName The lighting layer with the ambient color.
|
||||
* @param rgbColor The color, in RGB format ("128;200;255").
|
||||
*/
|
||||
@@ -477,6 +478,21 @@ namespace gdjs {
|
||||
parseInt(colors[2], 10)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param runtimeScene The scene
|
||||
* @param layer The name of the layer
|
||||
* @return the highest Z order of objects in the layer
|
||||
*/
|
||||
export const getLayerHighestZOrder = function (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
layer: string
|
||||
): number {
|
||||
if (!runtimeScene.hasLayer(layer)) {
|
||||
return 0;
|
||||
}
|
||||
return runtimeScene.getLayer(layer).getHighestZOrder();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ namespace gdjs {
|
||||
_zoomFactor: float = 1;
|
||||
_timeScale: float = 1;
|
||||
_defaultZOrder: integer = 0;
|
||||
_highestZOrder: integer = 0;
|
||||
_hidden: boolean;
|
||||
_initialEffectsData: Array<EffectData>;
|
||||
_cameraX: float;
|
||||
@@ -74,7 +75,7 @@ namespace gdjs {
|
||||
* Get the default Z order to be attributed to objects created on this layer
|
||||
* (usually from events generated code).
|
||||
*/
|
||||
getDefaultZOrder(): float {
|
||||
getDefaultZOrder(): integer {
|
||||
return this._defaultZOrder;
|
||||
}
|
||||
|
||||
@@ -86,6 +87,17 @@ namespace gdjs {
|
||||
this._defaultZOrder = defaultZOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the highest Z order occupied by the runtime objects on the layer
|
||||
*/
|
||||
getHighestZOrder(): integer {
|
||||
return this._highestZOrder;
|
||||
}
|
||||
|
||||
_setHighestZOrder(z: integer): void {
|
||||
this._highestZOrder = z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the RuntimeScene whenever the game resolution size is changed.
|
||||
* Updates the layer width/height and position.
|
||||
|
@@ -714,10 +714,13 @@ namespace gdjs {
|
||||
}
|
||||
this.zOrder = z;
|
||||
const rendererObject = this.getRendererObject();
|
||||
const theLayer = this._runtimeScene.getLayer(this.layer);
|
||||
if (rendererObject) {
|
||||
const theLayer = this._runtimeScene.getLayer(this.layer);
|
||||
theLayer.getRenderer().changeRendererObjectZOrder(rendererObject, z);
|
||||
}
|
||||
if (z > theLayer.getHighestZOrder()) {
|
||||
theLayer._setHighestZOrder(z);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -732,6 +732,10 @@ namespace gdjs {
|
||||
* Tool function filling _allInstancesList member with all the living object instances.
|
||||
*/
|
||||
_constructListOfAllInstances() {
|
||||
for (const name in this._layers.items) {
|
||||
this._layers.get(name)._setHighestZOrder(0);
|
||||
}
|
||||
|
||||
let currentListSize = 0;
|
||||
for (const name in this._instances.items) {
|
||||
if (this._instances.items.hasOwnProperty(name)) {
|
||||
@@ -739,10 +743,16 @@ namespace gdjs {
|
||||
const oldSize = currentListSize;
|
||||
currentListSize += list.length;
|
||||
for (let j = 0, lenj = list.length; j < lenj; ++j) {
|
||||
const instance = list[j];
|
||||
if (oldSize + j < this._allInstancesList.length) {
|
||||
this._allInstancesList[oldSize + j] = list[j];
|
||||
this._allInstancesList[oldSize + j] = instance;
|
||||
} else {
|
||||
this._allInstancesList.push(list[j]);
|
||||
this._allInstancesList.push(instance);
|
||||
}
|
||||
const layerName = instance.getLayer();
|
||||
const zOrder = instance.getZOrder();
|
||||
if (this.getLayer(layerName).getHighestZOrder() < zOrder) {
|
||||
this.getLayer(layerName)._setHighestZOrder(zOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -83,100 +83,165 @@ describe('gdjs.RuntimeScene integration tests', function () {
|
||||
});
|
||||
|
||||
describe('Layers (using a Sprite object)', function () {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
// @ts-expect-error ts-migrate(2740) FIXME: Type '{ windowWidth: number; windowHeight: number;... Remove this comment to see the full error message
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: { resources: [] },
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
{
|
||||
name: 'MyLayer',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [
|
||||
{
|
||||
type: 'Sprite',
|
||||
name: 'MyObject',
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ type: string; name: string; behaviors: nev... Remove this comment to see the full error message
|
||||
animations: [],
|
||||
updateIfNotVisible: false,
|
||||
},
|
||||
],
|
||||
instances: [],
|
||||
/** @type {gdjs.RuntimeGame} */
|
||||
let runtimeGame;
|
||||
/** @type {gdjs.RuntimeScene} */
|
||||
let runtimeScene;
|
||||
|
||||
beforeEach(() => {
|
||||
runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
// @ts-expect-error ts-migrate(2740) FIXME: Type '{ windowWidth: number; windowHeight: number;... Remove this comment to see the full error message
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: { resources: [] },
|
||||
});
|
||||
runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
{
|
||||
name: 'MyLayer',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 127,
|
||||
ambientLightColorB: 127,
|
||||
ambientLightColorG: 127,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [
|
||||
{
|
||||
type: 'Sprite',
|
||||
name: 'MyObject',
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
// @ts-expect-error ts-migrate(2322) FIXME: Type '{ type: string; name: string; behaviors: nev... Remove this comment to see the full error message
|
||||
animations: [],
|
||||
updateIfNotVisible: false,
|
||||
},
|
||||
],
|
||||
instances: [],
|
||||
});
|
||||
});
|
||||
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(false);
|
||||
it('should handle objects on layers', () => {
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(false);
|
||||
|
||||
const object1 = runtimeScene.createObject('MyObject');
|
||||
const object2 = runtimeScene.createObject('MyObject');
|
||||
const object3 = runtimeScene.createObject('MyObject');
|
||||
if (!object1 || !object2 || !object3) {
|
||||
throw new Error('object should have been created');
|
||||
}
|
||||
object2.setLayer('MyLayer');
|
||||
const object1 = runtimeScene.createObject('MyObject');
|
||||
const object2 = runtimeScene.createObject('MyObject');
|
||||
const object3 = runtimeScene.createObject('MyObject');
|
||||
if (!object1 || !object2 || !object3) {
|
||||
throw new Error('object should have been created');
|
||||
}
|
||||
object2.setLayer('MyLayer');
|
||||
|
||||
runtimeScene.addLayer({
|
||||
name: 'MyOtherLayer',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
ambientLightColorR: 128,
|
||||
ambientLightColorG: 128,
|
||||
ambientLightColorB: 128,
|
||||
runtimeScene.addLayer({
|
||||
name: 'MyOtherLayer',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
ambientLightColorR: 128,
|
||||
ambientLightColorG: 128,
|
||||
ambientLightColorB: 128,
|
||||
});
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
|
||||
object3.setLayer('MyOtherLayer');
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('MyLayer');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
|
||||
runtimeScene.removeLayer('MyLayer');
|
||||
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(false);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
});
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
|
||||
object3.setLayer('MyOtherLayer');
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('MyLayer');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
it('should compute layers highest z orders correctly', () => {
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(true);
|
||||
|
||||
runtimeScene.removeLayer('MyLayer');
|
||||
const object1 = runtimeScene.createObject('MyObject');
|
||||
const object2 = runtimeScene.createObject('MyObject');
|
||||
const object3 = runtimeScene.createObject('MyObject');
|
||||
if (!object1 || !object2 || !object3) {
|
||||
throw new Error('object should have been created');
|
||||
}
|
||||
object2.setLayer('MyLayer');
|
||||
|
||||
expect(object1.getLayer()).to.be('');
|
||||
expect(object2.getLayer()).to.be('');
|
||||
expect(object3.getLayer()).to.be('MyOtherLayer');
|
||||
expect(runtimeScene.hasLayer('')).to.be(true);
|
||||
expect(runtimeScene.hasLayer('MyLayer')).to.be(false);
|
||||
expect(runtimeScene.hasLayer('MyOtherLayer')).to.be(true);
|
||||
// Layers highest Z orders should stay at 0 if objects did not change their Z order
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('MyLayer').getHighestZOrder()).to.be(0);
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(0);
|
||||
|
||||
object2.setZOrder(8);
|
||||
|
||||
// Check highest Z order has been correctly updated for layers and they are stable
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('MyLayer').getHighestZOrder()).to.be(8);
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(0);
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('MyLayer').getHighestZOrder()).to.be(8);
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(0);
|
||||
|
||||
// Change default layer objects Z orders
|
||||
object1.setZOrder(13);
|
||||
object3.setZOrder(25);
|
||||
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('MyLayer').getHighestZOrder()).to.be(8);
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(25);
|
||||
|
||||
// Move object in front of the highest
|
||||
object1.setZOrder(30)
|
||||
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(30);
|
||||
|
||||
// Delete highest Z order object
|
||||
runtimeScene.markObjectForDeletion(object1);
|
||||
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(25);
|
||||
|
||||
// Check highest z orders come back to 0 after objects deletion
|
||||
runtimeScene.markObjectForDeletion(object2);
|
||||
runtimeScene.markObjectForDeletion(object3);
|
||||
|
||||
runtimeScene._updateObjectsPreRender();
|
||||
expect(runtimeScene.getLayer('MyLayer').getHighestZOrder()).to.be(0);
|
||||
expect(runtimeScene.getLayer('').getHighestZOrder()).to.be(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -78,314 +78,357 @@ describe('gdjs.SpriteRuntimeObject (using a PixiJS RuntimeGame with assets)', fu
|
||||
],
|
||||
});
|
||||
|
||||
it('returns the size of the object from the texture', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
it('returns the size of the object from the texture', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
expect(object.getWidth()).to.be(64);
|
||||
expect(object.getHeight()).to.be(64);
|
||||
});
|
||||
expect(object.getWidth()).to.be(64);
|
||||
expect(object.getHeight()).to.be(64);
|
||||
});
|
||||
|
||||
it('returns the object drawable X/Y', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
it('returns the object drawable X/Y', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Texture is shown on screen at -32;-16 because of the custom origin
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
// Texture is shown on screen at -32;-16 because of the custom origin
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-16
|
||||
// after vertical flip, as the center X position is 64 (on the very right of the texture):
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-16
|
||||
// after vertical flip, as the center X position is 64 (on the very right of the texture):
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-18
|
||||
// after vertical flip, as the center Y position is 31 (so new Y position is 2 pixels away from -16)
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
// Flipping is done relative to the center, so texture is shown on screen at 32;-18
|
||||
// after vertical flip, as the center Y position is 31 (so new Y position is 2 pixels away from -16)
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
|
||||
// Sanity check when flipping on both axes:
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
});
|
||||
// Sanity check when flipping on both axes:
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getDrawableX()).to.be(32);
|
||||
expect(object.getDrawableY()).to.be(-18);
|
||||
});
|
||||
|
||||
it('returns the object center X/Y', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
it('returns the object center X/Y', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// getDrawableX/Y is checked in another test:
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
// getDrawableX/Y is checked in another test:
|
||||
expect(object.getDrawableX()).to.be(-32);
|
||||
expect(object.getDrawableY()).to.be(-16);
|
||||
|
||||
// Check that the center X and Y is returned relative to getDrawableX/Y:
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
// Check that the center X and Y is returned relative to getDrawableX/Y:
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
|
||||
// Sanity test that center position in the scene coordinates is right.
|
||||
// It's a common pattern in the game engine:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
// Sanity test that center position in the scene coordinates is right.
|
||||
// It's a common pattern in the game engine:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(31);
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(false);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(64);
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
// Check that the center X and Y is always returned relative to the object texture origin.
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getCenterX()).to.be(0); // Center is at the very right of the texture, so after flipping is on the very left.
|
||||
expect(object.getCenterY()).to.be(33); // Center point was 1 pixel above the texture center, so is now 1 pixel below
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
object.setAngle(12.92);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
// As the center is the center for rotation and flipping, its position in the scene coordinates never changes:
|
||||
object.setAngle(12.92);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
|
||||
object.setAngle(-12345.67);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
});
|
||||
object.setAngle(-12345.67);
|
||||
expect(object.getDrawableX() + object.getCenterX()).to.be(32);
|
||||
expect(object.getDrawableY() + object.getCenterY()).to.be(15);
|
||||
});
|
||||
|
||||
it('properly computes hitboxes and point positions (after flipping or rotation)', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
it('properly computes hitboxes and point positions (after flipping or rotation)', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with rotation
|
||||
object.setAngle(90);
|
||||
expect(object.getHitBoxes()[0].vertices[0][0]).to.be.within(
|
||||
61.9999,
|
||||
62.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[0][1]).to.be.within(
|
||||
-36.5001,
|
||||
-36.49999
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][0]).to.be.within(
|
||||
31.999,
|
||||
32.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][1]).to.be.within(
|
||||
6.4999,
|
||||
6.5001
|
||||
);
|
||||
// Hitbox with rotation
|
||||
object.setAngle(90);
|
||||
expect(object.getHitBoxes()[0].vertices[0][0]).to.be.within(
|
||||
61.9999,
|
||||
62.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[0][1]).to.be.within(
|
||||
-36.5001,
|
||||
-36.49999
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][0]).to.be.within(
|
||||
31.999,
|
||||
32.0001
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2][1]).to.be.within(
|
||||
6.4999,
|
||||
6.5001
|
||||
);
|
||||
|
||||
// Center is unchanged with rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Center is unchanged with rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Hitbox with flipping (X axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is flipped on X axis, relative to the center, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (32) O (64) P (83.5)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
expect(centerPointX - 12.5 + centerPointX - originPointX).to.be(83.5); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, 30 - originPointY]);
|
||||
// Hitbox with flipping (X axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is flipped on X axis, relative to the center, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (32) O (64) P (83.5)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
expect(centerPointX - 12.5 + centerPointX - originPointX).to.be(83.5); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, 30 - originPointY]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with flipping (X and Y axis)
|
||||
//
|
||||
// Same calculations as before for the point Y positions.
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(centerPointY - 1 + centerPointY - originPointY).to.be(45); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, centerPointY - 1 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, centerPointY - 2 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, centerPointY - 31 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, centerPointY - 30 + centerPointY - originPointY]);
|
||||
// Hitbox with flipping (X and Y axis)
|
||||
//
|
||||
// Same calculations as before for the point Y positions.
|
||||
object.setAngle(0);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(centerPointY - 1 + centerPointY - originPointY).to.be(45); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([centerPointX - 12.5 + centerPointX - originPointX, centerPointY - 1 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([centerPointX - 41.5 + centerPointX - originPointX, centerPointY - 2 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([centerPointX - 55.5 + centerPointX - originPointX, centerPointY - 31 + centerPointY - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([centerPointX - 24.5 + centerPointX - originPointX, centerPointY - 30 + centerPointY - originPointY]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(30);
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(64);
|
||||
expect(object.getPointY("Origin")).to.be(30);
|
||||
|
||||
// Hitbox with flipping (X and Y axis) and rotation
|
||||
object.setAngle(-90);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([62, -36.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[1][0]).to.be(
|
||||
61
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[1][1]).to.be.within(
|
||||
-7.5,
|
||||
-7.49
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([32, 6.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([33, -24.5]);
|
||||
// Hitbox with flipping (X and Y axis) and rotation
|
||||
object.setAngle(-90);
|
||||
object.flipX(true);
|
||||
object.flipY(true);
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([62, -36.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[1][0]).to.be(
|
||||
61
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[1][1]).to.be.within(
|
||||
-7.5,
|
||||
-7.49
|
||||
);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([32, 6.5]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([33, -24.5]);
|
||||
|
||||
// Center is unchanged with flipping and rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Center is unchanged with flipping and rotation
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Origin *point* is flipped and rotated
|
||||
expect(object.getPointX("Origin")).to.be(47);
|
||||
expect(object.getPointY("Origin")).to.be(-17);
|
||||
});
|
||||
// Origin *point* is flipped and rotated
|
||||
expect(object.getPointX("Origin")).to.be(47);
|
||||
expect(object.getPointY("Origin")).to.be(-17);
|
||||
});
|
||||
|
||||
it('properly computes hitboxes and point positions after scaling', function() {
|
||||
return gdjs.getPixiRuntimeGameWithAssets().then(runtimeGame => {
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
it('properly computes hitboxes and point positions after scaling', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
// Check the hitboxes without any rotation (only the non default origin
|
||||
// which is at 32;16 is to be used).
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
// Sanity check the center position
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
// Sanity check the origin position
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with 0.5 scaling (X and Y axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is scaled, relative to the origin, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-9.75) O (0) C (16)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(false);
|
||||
object.flipY(false);
|
||||
expect((12.5 - originPointX)/2).to.be(-9.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(12.5 - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(41.5 - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(55.5 - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(24.5 - originPointX)/2, (30 - originPointY)/2]);
|
||||
// Hitbox with 0.5 scaling (X and Y axis)
|
||||
//
|
||||
// On the X axis, points are like this (P = the first vertex of the hitboxes, O = origin, C = center):
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (12.5) O (32) C (64)
|
||||
//
|
||||
// Object X position is 0, so the origin is at 0 in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-19.5) O (0) C (32)
|
||||
//
|
||||
// Object is scaled, relative to the origin, so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// P (-9.75) O (0) C (16)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(false);
|
||||
object.flipY(false);
|
||||
expect((12.5 - originPointX)/2).to.be(-9.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(12.5 - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(41.5 - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(55.5 - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(24.5 - originPointX)/2, (30 - originPointY)/2]);
|
||||
|
||||
// Center is moved after scaling
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
// Center is moved after scaling
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
|
||||
// Origin is unchanged after scaling
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
// Origin is unchanged after scaling
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Hitbox with 0.5 scaling (X and Y axis) and flipping (X axis)
|
||||
// Hitbox with 0.5 scaling (X and Y axis) and flipping (X axis)
|
||||
|
||||
// Object is scaled, relative to the origin, and flipped on X axis so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (16) O (32) P (41.75)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect((centerPointX - 12.5 + centerPointX - originPointX)/2).to.be(41.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(centerPointX - 12.5 + centerPointX - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(centerPointX - 41.5 + centerPointX - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(centerPointX - 55.5 + centerPointX - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(centerPointX - 24.5 + centerPointX - originPointX)/2, (30 - originPointY)/2]);
|
||||
// Object is scaled, relative to the origin, and flipped on X axis so points are like this now in the scene coordinates:
|
||||
// -20 -10 0 10 20 30 40 50 60 70 80 90
|
||||
// |---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|---------|
|
||||
// C (16) O (32) P (41.75)
|
||||
//
|
||||
object.setAngle(0);
|
||||
object.setScale(0.5);
|
||||
object.flipX(true);
|
||||
object.flipY(false);
|
||||
expect((centerPointX - 12.5 + centerPointX - originPointX)/2).to.be(41.75); // Sanity check of the first expected position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([(centerPointX - 12.5 + centerPointX - originPointX)/2, (1 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([(centerPointX - 41.5 + centerPointX - originPointX)/2, (2 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([(centerPointX - 55.5 + centerPointX - originPointX)/2, (31 - originPointY)/2]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([(centerPointX - 24.5 + centerPointX - originPointX)/2, (30 - originPointY)/2]);
|
||||
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
// Center is unchanged with flipping
|
||||
expect(object.getPointX("Center")).to.be(16);
|
||||
expect(object.getPointY("Center")).to.be(7.5);
|
||||
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(32);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
// Origin *point* is flipped
|
||||
expect(object.getPointX("Origin")).to.be(32);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
});
|
||||
|
||||
it('properly computes hitboxes and point positions after the layer camera has moved', async () => {
|
||||
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.addLayer({
|
||||
name: '',
|
||||
visibility: true,
|
||||
cameras: [],
|
||||
effects: [],
|
||||
ambientLightColorR: 0,
|
||||
ambientLightColorG: 0,
|
||||
ambientLightColorB: 0,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: false,
|
||||
});
|
||||
const defaultLayer = runtimeScene.getLayer('');
|
||||
|
||||
// Create an object with a custom hitbox
|
||||
const object = makeSpriteRuntimeObjectWithCustomHitBox(runtimeScene);
|
||||
|
||||
// Check the hitboxes and positions with default camera position
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
|
||||
// Move the layer camera.
|
||||
defaultLayer.setCameraX(2000);
|
||||
defaultLayer.setCameraY(4000);
|
||||
|
||||
// The object hitboxes and positions stay the same.
|
||||
expect(object.getHitBoxes()[0].vertices[0]).to.eql([12.5 - originPointX, 1 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[1]).to.eql([41.5 - originPointX, 2 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[2]).to.eql([55.5 - originPointX, 31 - originPointY]);
|
||||
expect(object.getHitBoxes()[0].vertices[3]).to.eql([24.5 - originPointX, 30 - originPointY]);
|
||||
|
||||
expect(object.getPointX("Center")).to.be(32);
|
||||
expect(object.getPointY("Center")).to.be(15);
|
||||
|
||||
expect(object.getPointX("Origin")).to.be(0);
|
||||
expect(object.getPointY("Origin")).to.be(0);
|
||||
});
|
||||
});
|
||||
|
@@ -16,7 +16,7 @@ import EventsFunctionsExtensionsContext from '../EventsFunctionsExtensionsLoader
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import LinearProgress from '../UI/LinearProgress';
|
||||
import { AssetStoreContext } from './AssetStoreContext';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
|
||||
type Props = {|
|
||||
assetPack: AssetPack,
|
||||
@@ -27,7 +27,7 @@ type Props = {|
|
||||
project: gdProject,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
onObjectAddedFromAsset: (object: gdObject) => void,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
export const AssetPackDialog = ({
|
||||
@@ -39,7 +39,7 @@ export const AssetPackDialog = ({
|
||||
project,
|
||||
objectsContainer,
|
||||
onObjectAddedFromAsset,
|
||||
onFetchNewlyAddedResources,
|
||||
resourceManagementProps,
|
||||
}: Props) => {
|
||||
const missingAssetShortHeaders = assetShortHeaders.filter(
|
||||
assetShortHeader => !addedAssetIds.includes(assetShortHeader.id)
|
||||
@@ -82,7 +82,7 @@ export const AssetPackDialog = ({
|
||||
});
|
||||
});
|
||||
|
||||
await onFetchNewlyAddedResources();
|
||||
await resourceManagementProps.onFetchNewlyAddedResources();
|
||||
|
||||
setAreAssetsBeingInstalled(false);
|
||||
onAssetsAdded();
|
||||
@@ -104,7 +104,7 @@ export const AssetPackDialog = ({
|
||||
onObjectAddedFromAsset,
|
||||
onAssetsAdded,
|
||||
environment,
|
||||
onFetchNewlyAddedResources,
|
||||
resourceManagementProps,
|
||||
]
|
||||
);
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import GridList from '@material-ui/core/GridList';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import { CorsAwareImage } from '../UI/CorsAwareImage';
|
||||
import Text from '../UI/Text';
|
||||
import PriceTag from '../UI/PriceTag';
|
||||
import type { AssetPacks, AssetPack } from '../Utils/GDevelopServices/Asset';
|
||||
import { type PrivateAssetPackListingData } from '../Utils/GDevelopServices/Shop';
|
||||
import { shouldValidate } from '../UI/KeyboardShortcuts/InteractionKeys';
|
||||
@@ -21,12 +22,19 @@ const cellSpacing = 2;
|
||||
|
||||
const styles = {
|
||||
grid: { margin: '0 10px' },
|
||||
priceTagContainer: {
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: 10,
|
||||
cursor: 'default',
|
||||
},
|
||||
previewImage: {
|
||||
width: '100%',
|
||||
// Prevent cumulative layout shift by enforcing
|
||||
// the 16:9 ratio.
|
||||
aspectRatio: '16 / 9',
|
||||
objectFit: 'cover',
|
||||
position: 'relative',
|
||||
},
|
||||
cardContainer: {
|
||||
overflow: 'hidden',
|
||||
@@ -150,6 +158,9 @@ const PrivateAssetPackTile = ({
|
||||
src={assetPack.thumbnailUrls[0]}
|
||||
alt={`Preview image of asset pack ${assetPack.name}`}
|
||||
/>
|
||||
<div style={styles.priceTagContainer}>
|
||||
<PriceTag value={assetPack.prices[0].value} withOverlay />
|
||||
</div>
|
||||
<Column>
|
||||
<Line justifyContent="space-between" noMargin>
|
||||
<Text style={styles.packTitle} size="body2">
|
||||
|
@@ -15,12 +15,7 @@ import HelpButton from '../UI/HelpButton';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import { Tabs, Tab } from '../UI/Tabs';
|
||||
import { AssetStore } from '.';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import {
|
||||
sendAssetAddedToProject,
|
||||
sendNewObjectCreated,
|
||||
@@ -71,10 +66,7 @@ type Props = {|
|
||||
project: gdProject,
|
||||
layout: ?gdLayout,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onClose: () => void,
|
||||
onCreateNewObject: (type: string) => void,
|
||||
onObjectAddedFromAsset: gdObject => void,
|
||||
@@ -84,10 +76,7 @@ export default function NewObjectDialog({
|
||||
project,
|
||||
layout,
|
||||
objectsContainer,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
onFetchNewlyAddedResources,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
onClose,
|
||||
onCreateNewObject,
|
||||
onObjectAddedFromAsset,
|
||||
@@ -180,7 +169,7 @@ export default function NewObjectDialog({
|
||||
onObjectAddedFromAsset(object);
|
||||
});
|
||||
|
||||
await onFetchNewlyAddedResources();
|
||||
await resourceManagementProps.onFetchNewlyAddedResources();
|
||||
} catch (error) {
|
||||
console.error('Error while installing the asset:', error);
|
||||
showErrorBox({
|
||||
@@ -202,7 +191,7 @@ export default function NewObjectDialog({
|
||||
onObjectAddedFromAsset,
|
||||
openedAssetShortHeader,
|
||||
environment,
|
||||
onFetchNewlyAddedResources,
|
||||
resourceManagementProps,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -342,7 +331,7 @@ export default function NewObjectDialog({
|
||||
project={project}
|
||||
objectsContainer={objectsContainer}
|
||||
onObjectAddedFromAsset={onObjectAddedFromAsset}
|
||||
onFetchNewlyAddedResources={onFetchNewlyAddedResources}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@@ -9,11 +9,16 @@ import {
|
||||
import Text from '../UI/Text';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import PriceTag from '../UI/PriceTag';
|
||||
import TextButton from '../UI/TextButton';
|
||||
import AlertMessage from '../UI/AlertMessage';
|
||||
import PlaceholderLoader from '../UI/PlaceholderLoader';
|
||||
import { ResponsiveLineStackLayout } from '../UI/Layout';
|
||||
import { Column, LargeSpacer, Line, Spacer } from '../UI/Grid';
|
||||
import {
|
||||
ColumnStackLayout,
|
||||
ResponsiveLineStackLayout,
|
||||
LineStackLayout,
|
||||
} from '../UI/Layout';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import {
|
||||
getUserPublicProfile,
|
||||
type UserPublicProfile,
|
||||
@@ -22,7 +27,7 @@ import PublicProfileDialog from '../Profile/PublicProfileDialog';
|
||||
import Link from '../UI/Link';
|
||||
import Mark from '../UI/CustomSvgIcons/Mark';
|
||||
import Cross from '../UI/CustomSvgIcons/Cross';
|
||||
import { Paper } from '@material-ui/core';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import ResponsiveImagesGallery from '../UI/ResponsiveImagesGallery';
|
||||
import { useResponsiveWindowWidth } from '../UI/Reponsive/ResponsiveWindowMeasurer';
|
||||
|
||||
@@ -51,8 +56,12 @@ const contentTypeToMessageDescriptor = {
|
||||
partial: t`Other`,
|
||||
};
|
||||
|
||||
const styles = {
|
||||
disabledText: { opacity: 0.6 },
|
||||
};
|
||||
|
||||
const PrivateAssetPackDialog = ({
|
||||
privateAssetPack: { id, name, description, sellerId },
|
||||
privateAssetPack: { id, name, description, sellerId, prices },
|
||||
onClose,
|
||||
}: Props) => {
|
||||
const [
|
||||
@@ -123,9 +132,7 @@ const PrivateAssetPackDialog = ({
|
||||
fullHeight
|
||||
>
|
||||
{errorText ? (
|
||||
<AlertMessage kind="error">
|
||||
<Text>{errorText}</Text>
|
||||
</AlertMessage>
|
||||
<AlertMessage kind="error">{errorText}</AlertMessage>
|
||||
) : isFetchingDetails ? (
|
||||
<>
|
||||
<Text size="title">{name}</Text>
|
||||
@@ -165,10 +172,11 @@ const PrivateAssetPackDialog = ({
|
||||
variant="outlined"
|
||||
style={{ padding: windowWidth === 'small' ? 20 : 30 }}
|
||||
>
|
||||
<Column noMargin>
|
||||
<LargeSpacer /> {/* To be replaced by prices */}
|
||||
<ColumnStackLayout noMargin useLargeSpacer>
|
||||
<Line noMargin>
|
||||
<PriceTag value={prices[0].value} />
|
||||
</Line>
|
||||
<Text noMargin>{assetPackDetails.longDescription}</Text>
|
||||
<LargeSpacer />
|
||||
<ResponsiveLineStackLayout noMargin noColumnMargin>
|
||||
<Column noMargin expand>
|
||||
<Text size="sub-title">
|
||||
@@ -194,37 +202,40 @@ const PrivateAssetPackDialog = ({
|
||||
<Text size="sub-title">
|
||||
<Trans>Licensing</Trans>
|
||||
</Text>
|
||||
<Line noMargin alignItems="center">
|
||||
<LineStackLayout noMargin alignItems="center">
|
||||
<Mark fontSize="small" />
|
||||
<Spacer />
|
||||
<Text displayInlineAsSpan noMargin>
|
||||
<Trans>Personal projects</Trans>
|
||||
</Text>
|
||||
</Line>
|
||||
<Line noMargin alignItems="center">
|
||||
</LineStackLayout>
|
||||
<LineStackLayout noMargin alignItems="center">
|
||||
<Mark fontSize="small" />
|
||||
<Spacer />
|
||||
<Text displayInlineAsSpan noMargin>
|
||||
<Trans>Professional projects</Trans>
|
||||
</Text>
|
||||
</Line>
|
||||
<Line noMargin alignItems="center">
|
||||
</LineStackLayout>
|
||||
<LineStackLayout noMargin alignItems="center">
|
||||
<Mark fontSize="small" />
|
||||
<Spacer />
|
||||
<Text displayInlineAsSpan noMargin>
|
||||
<Trans>Asset modification</Trans>
|
||||
</Text>
|
||||
</Line>
|
||||
<Line noMargin alignItems="center">
|
||||
<Cross fontSize="small" />
|
||||
<Spacer />
|
||||
<Text displayInlineAsSpan noMargin>
|
||||
</LineStackLayout>
|
||||
<LineStackLayout noMargin alignItems="center">
|
||||
<Cross
|
||||
fontSize="small"
|
||||
style={styles.disabledText}
|
||||
/>
|
||||
<Text
|
||||
displayInlineAsSpan
|
||||
noMargin
|
||||
style={styles.disabledText}
|
||||
>
|
||||
<Trans>Redistribution & reselling</Trans>
|
||||
</Text>
|
||||
</Line>
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
</ResponsiveLineStackLayout>
|
||||
</Column>
|
||||
</ColumnStackLayout>
|
||||
</Paper>
|
||||
</Column>
|
||||
</ResponsiveLineStackLayout>
|
||||
|
@@ -34,6 +34,8 @@ import { AssetDetails } from './AssetDetails';
|
||||
import PlaceholderLoader from '../UI/PlaceholderLoader';
|
||||
import Home from '@material-ui/icons/Home';
|
||||
import PrivateAssetPackDialog from './PrivateAssetPackDialog';
|
||||
import PlaceholderError from '../UI/PlaceholderError';
|
||||
import AlertMessage from '../UI/AlertMessage';
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
@@ -265,9 +267,9 @@ export const AssetStore = ({ project }: Props) => {
|
||||
)}
|
||||
</Background>
|
||||
)}
|
||||
{isOnHomePage && !(assetPacks && privateAssetPacks) && (
|
||||
<PlaceholderLoader />
|
||||
)}
|
||||
{isOnHomePage &&
|
||||
!(assetPacks && privateAssetPacks) &&
|
||||
!error && <PlaceholderLoader />}
|
||||
{isOnHomePage &&
|
||||
assetPacks &&
|
||||
privateAssetPacks &&
|
||||
@@ -300,6 +302,16 @@ export const AssetStore = ({ project }: Props) => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{isOnHomePage && error && (
|
||||
<PlaceholderError onRetry={fetchAssetsAndFilters}>
|
||||
<AlertMessage kind="error">
|
||||
<Trans>
|
||||
An error occurred when fetching the asset store content.
|
||||
Please try again later.
|
||||
</Trans>
|
||||
</AlertMessage>
|
||||
</PlaceholderError>
|
||||
)}
|
||||
{openedAssetShortHeader && (
|
||||
<AssetDetails
|
||||
project={project}
|
||||
|
@@ -1,9 +1,5 @@
|
||||
// @flow
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
|
||||
/**
|
||||
* The props given to any behavior editor
|
||||
@@ -12,7 +8,5 @@ export type BehaviorEditorProps = {|
|
||||
behavior: gdBehavior,
|
||||
project: gdProject,
|
||||
object: gdObject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
@@ -292,9 +292,7 @@ const Physics2Editor = (props: Props) => {
|
||||
</Trans>
|
||||
}
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
resourcesLoader={resourcesLoader}
|
||||
resourceKind={'image'}
|
||||
initialResourceName={''}
|
||||
|
@@ -14,11 +14,7 @@ import BehaviorsEditorService from './BehaviorsEditorService';
|
||||
import Window from '../Utils/Window';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import RaisedButton from '../UI/RaisedButton';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import DismissableTutorialMessage from '../Hints/DismissableTutorialMessage';
|
||||
import { ColumnStackLayout } from '../UI/Layout';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
@@ -41,9 +37,7 @@ type Props = {|
|
||||
object: gdObject,
|
||||
onUpdateBehaviorsSharedData: () => void,
|
||||
onSizeUpdated?: ?() => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
const BehaviorsEditor = (props: Props) => {
|
||||
@@ -256,10 +250,8 @@ const BehaviorsEditor = (props: Props) => {
|
||||
behavior={behavior}
|
||||
project={project}
|
||||
object={object}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={
|
||||
props.resourceExternalEditors
|
||||
resourceManagementProps={
|
||||
props.resourceManagementProps
|
||||
}
|
||||
/>
|
||||
</Line>
|
||||
|
@@ -26,11 +26,7 @@ import {
|
||||
type EnumeratedEffectMetadata,
|
||||
setEffectDefaultParameters,
|
||||
} from './EnumerateEffects';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import ScrollView from '../UI/ScrollView';
|
||||
import { EmptyPlaceholder } from '../UI/EmptyPlaceholder';
|
||||
import {
|
||||
@@ -59,9 +55,7 @@ const styles = {
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
effectsContainer: gdEffectsContainer,
|
||||
onEffectsUpdated: () => void,
|
||||
target: 'object' | 'layer',
|
||||
@@ -367,14 +361,8 @@ export default function EffectsList(props: Props) {
|
||||
effectMetadata.parametersSchema
|
||||
}
|
||||
project={props.project}
|
||||
resourceSources={
|
||||
props.resourceSources
|
||||
}
|
||||
onChooseResource={
|
||||
props.onChooseResource
|
||||
}
|
||||
resourceExternalEditors={
|
||||
props.resourceExternalEditors
|
||||
resourceManagementProps={
|
||||
props.resourceManagementProps
|
||||
}
|
||||
renderExtraDescriptionText={
|
||||
showEffectParameterNames
|
||||
|
@@ -12,13 +12,11 @@ import type { ObjectWithContext } from '../ObjectsList/EnumerateObjects';
|
||||
import Window from '../Utils/Window';
|
||||
import ObjectEditorDialog from '../ObjectEditor/ObjectEditorDialog';
|
||||
import { type ObjectEditorTab } from '../ObjectEditor/ObjectEditorDialog';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
@@ -212,9 +210,12 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
|
||||
objectsContainer={eventsBasedObject}
|
||||
layout={null}
|
||||
// TODO EBO Allow to use project resources as place holders
|
||||
resourceSources={[]}
|
||||
resourceExternalEditors={[]}
|
||||
onChooseResource={() => Promise.resolve([])}
|
||||
resourceManagementProps={{
|
||||
resourceSources: [],
|
||||
resourceExternalEditors: [],
|
||||
onChooseResource: async () => [],
|
||||
onFetchNewlyAddedResources: async () => {},
|
||||
}}
|
||||
selectedObjectNames={this.state.selectedObjectNames}
|
||||
onEditObject={this.editObject}
|
||||
onDeleteObject={this._onDeleteObject(i18n)}
|
||||
@@ -243,9 +244,6 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
|
||||
launchProjectDataOnlyPreview: () => {},
|
||||
launchProjectWithLoadingScreenPreview: () => {},
|
||||
}}
|
||||
onFetchNewlyAddedResources={
|
||||
this.props.onFetchNewlyAddedResources
|
||||
}
|
||||
/>
|
||||
</Line>
|
||||
{this.state.editedObjectWithContext && (
|
||||
@@ -254,9 +252,12 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
|
||||
object={this.state.editedObjectWithContext.object}
|
||||
initialTab={this.state.editedObjectInitialTab}
|
||||
project={project}
|
||||
resourceSources={[]}
|
||||
resourceExternalEditors={[]}
|
||||
onChooseResource={() => Promise.resolve([])}
|
||||
resourceManagementProps={{
|
||||
resourceSources: [],
|
||||
resourceExternalEditors: [],
|
||||
onChooseResource: async () => [],
|
||||
onFetchNewlyAddedResources: async () => {},
|
||||
}}
|
||||
onComputeAllVariableNames={() => {
|
||||
return [];
|
||||
// TODO EBO Find undeclared variables in the parent events.
|
||||
|
@@ -4,12 +4,10 @@ import * as React from 'react';
|
||||
import Dialog, { DialogPrimaryButton } from '../UI/Dialog';
|
||||
import EventsBasedObjectEditor from './index';
|
||||
import HelpButton from '../UI/HelpButton';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
|
||||
type Props = {|
|
||||
onApply: () => void,
|
||||
project: gdProject,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
@@ -55,7 +53,6 @@ export default class EventsBasedObjectEditorDialog extends React.Component<
|
||||
>
|
||||
<EventsBasedObjectEditor
|
||||
project={project}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
|
@@ -12,16 +12,13 @@ import EventsBasedObjectPropertiesEditor from './EventsBasedObjectPropertiesEdit
|
||||
import EventBasedObjectChildrenEditor from './EventBasedObjectChildrenEditor';
|
||||
import { ColumnStackLayout } from '../UI/Layout';
|
||||
import { Line } from '../UI/Grid';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
import { showWarningBox } from '../UI/Messages/MessageBox';
|
||||
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
type TabName = 'configuration' | 'properties' | 'children';
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventsBasedObject: gdEventsBasedObject,
|
||||
@@ -164,7 +161,6 @@ export default class EventsBasedObjectEditor extends React.Component<
|
||||
{currentTab === 'children' && (
|
||||
<EventBasedObjectChildrenEditor
|
||||
project={project}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={eventsBasedObject}
|
||||
|
@@ -224,10 +224,6 @@ export const ExtensionOptionsEditor = ({
|
||||
text: 'Camera',
|
||||
value: 'Camera',
|
||||
},
|
||||
{
|
||||
text: 'Device',
|
||||
value: 'Device',
|
||||
},
|
||||
{
|
||||
text: 'Input',
|
||||
value: 'Input',
|
||||
|
@@ -19,11 +19,7 @@ import OptionsEditorDialog from './OptionsEditorDialog';
|
||||
import { showWarningBox } from '../UI/Messages/MessageBox';
|
||||
import EventsBasedBehaviorEditorDialog from '../EventsBasedBehaviorEditor/EventsBasedBehaviorEditorDialog';
|
||||
import EventsBasedObjectEditorDialog from '../EventsBasedObjectEditor/EventsBasedObjectEditorDialog';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import BehaviorMethodSelectorDialog from './BehaviorMethodSelectorDialog';
|
||||
import ObjectMethodSelectorDialog from './ObjectMethodSelectorDialog';
|
||||
import ExtensionFunctionSelectorDialog from './ExtensionFunctionSelectorDialog';
|
||||
@@ -47,7 +43,6 @@ import PreferencesContext from '../MainFrame/Preferences/PreferencesContext';
|
||||
import { ParametersIndexOffsets } from '../EventsFunctionsExtensionsLoader';
|
||||
import { sendEventsExtractedAsFunction } from '../Utils/Analytics/EventSender';
|
||||
import Window from '../Utils/Window';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const isDev = Window.isDev();
|
||||
@@ -56,10 +51,7 @@ type Props = {|
|
||||
project: gdProject,
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
setToolbar: (?React.Node) => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
openInstructionOrExpression: (
|
||||
extension: gdPlatformExtension,
|
||||
type: string
|
||||
@@ -1095,9 +1087,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
events={selectedEventsFunction.getEvents()}
|
||||
onOpenExternalEvents={() => {}}
|
||||
onOpenLayout={() => {}}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={
|
||||
this.props.openInstructionOrExpression
|
||||
}
|
||||
@@ -1488,7 +1478,6 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
|
||||
{editedEventsBasedObject && this._globalObjectsContainer && (
|
||||
<EventsBasedObjectEditorDialog
|
||||
project={project}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
globalObjectsContainer={this._globalObjectsContainer}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
eventsBasedObject={editedEventsBasedObject}
|
||||
|
@@ -2,11 +2,7 @@
|
||||
import * as React from 'react';
|
||||
import InlinePopover from './InlinePopover';
|
||||
import ParameterRenderingService from './ParameterRenderingService';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import { type EventsScope } from '../InstructionOrExpression/EventsScope.flow';
|
||||
import { setupInstructionParameters } from '../InstructionOrExpression/SetupInstructionParameters';
|
||||
import { getObjectParameterIndex } from '../InstructionOrExpression/EnumerateInstructions';
|
||||
@@ -29,9 +25,7 @@ type Props = {|
|
||||
|
||||
anchorEl: ?any,
|
||||
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -164,9 +158,7 @@ export default class InlineParameterEditor extends React.Component<
|
||||
ref={field => (this._field = field)}
|
||||
parameterRenderingService={ParameterRenderingService}
|
||||
isInline
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
/>
|
||||
</InlinePopover>
|
||||
);
|
||||
|
@@ -5,11 +5,7 @@ import * as React from 'react';
|
||||
import Dialog, { DialogPrimaryButton } from '../../UI/Dialog';
|
||||
import FlatButton from '../../UI/FlatButton';
|
||||
import InstructionEditor from '.';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import { type EventsScope } from '../../InstructionOrExpression/EventsScope.flow';
|
||||
|
||||
type Props = {|
|
||||
@@ -19,9 +15,7 @@ type Props = {|
|
||||
objectsContainer: gdObjectsContainer,
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
style?: Object,
|
||||
isNewInstruction: boolean,
|
||||
onCancel: () => void,
|
||||
|
@@ -10,11 +10,7 @@ import { mapFor } from '../../Utils/MapFor';
|
||||
import EmptyMessage from '../../UI/EmptyMessage';
|
||||
import ParameterRenderingService from '../ParameterRenderingService';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import { Column, Line, Spacer } from '../../UI/Grid';
|
||||
import AlertMessage from '../../UI/AlertMessage';
|
||||
import DismissableAlertMessage from '../../UI/DismissableAlertMessage';
|
||||
@@ -74,9 +70,7 @@ type Props = {|
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
focusOnMount?: boolean,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
style?: Object,
|
||||
openInstructionOrExpression: (
|
||||
extension: gdPlatformExtension,
|
||||
@@ -116,9 +110,7 @@ const InstructionParametersEditor = React.forwardRef<
|
||||
focusOnMount,
|
||||
style,
|
||||
openInstructionOrExpression,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
@@ -373,9 +365,7 @@ const InstructionParametersEditor = React.forwardRef<
|
||||
objectsContainer={objectsContainer}
|
||||
key={i}
|
||||
parameterRenderingService={ParameterRenderingService}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
ref={field => {
|
||||
if (isFirstVisibleParameterField) {
|
||||
firstVisibleField.current = field;
|
||||
|
@@ -4,11 +4,7 @@ import { Trans } from '@lingui/macro';
|
||||
import * as React from 'react';
|
||||
import Dialog, { DialogPrimaryButton } from '../../UI/Dialog';
|
||||
import FlatButton from '../../UI/FlatButton';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import InstructionParametersEditor, {
|
||||
type InstructionParametersEditorInterface,
|
||||
} from './InstructionParametersEditor';
|
||||
@@ -57,9 +53,7 @@ type Props = {|
|
||||
objectsContainer: gdObjectsContainer,
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
style?: Object,
|
||||
isNewInstruction: boolean,
|
||||
onCancel: () => void,
|
||||
@@ -102,9 +96,7 @@ export default function NewInstructionEditorDialog({
|
||||
isNewInstruction,
|
||||
scope,
|
||||
onSubmit,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
openInstructionOrExpression,
|
||||
}: Props) {
|
||||
const forceUpdate = useForceUpdate();
|
||||
@@ -262,9 +254,7 @@ export default function NewInstructionEditorDialog({
|
||||
objectName={chosenObjectName}
|
||||
isCondition={isCondition}
|
||||
instruction={instruction}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
openInstructionOrExpression={openInstructionOrExpression}
|
||||
ref={instructionParametersEditor}
|
||||
focusOnMount={!!instructionType}
|
||||
|
@@ -2,11 +2,7 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Popover from '@material-ui/core/Popover';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import {
|
||||
useNewInstructionEditor,
|
||||
getInstructionMetadata,
|
||||
@@ -42,9 +38,7 @@ type Props = {|
|
||||
objectsContainer: gdObjectsContainer,
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
style?: Object,
|
||||
anchorEl: ?HTMLElement,
|
||||
isNewInstruction: boolean,
|
||||
|
@@ -5,11 +5,7 @@ import InstructionSelector from './InstructionOrExpressionSelector/InstructionSe
|
||||
import InstructionParametersEditor, {
|
||||
type InstructionParametersEditorInterface,
|
||||
} from './InstructionParametersEditor';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import { type EventsScope } from '../../InstructionOrExpression/EventsScope.flow';
|
||||
|
||||
const styles = {
|
||||
@@ -31,9 +27,7 @@ type Props = {|
|
||||
objectsContainer: gdObjectsContainer,
|
||||
instruction: gdInstruction,
|
||||
isCondition: boolean,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
style?: Object,
|
||||
openInstructionOrExpression: (
|
||||
extension: gdPlatformExtension,
|
||||
@@ -83,9 +77,7 @@ export default class InstructionEditor extends React.Component<Props, State> {
|
||||
objectsContainer={objectsContainer}
|
||||
isCondition={isCondition}
|
||||
instruction={instruction}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={this.props.openInstructionOrExpression}
|
||||
ref={instructionParametersEditor =>
|
||||
(this._instructionParametersEditor = instructionParametersEditor)
|
||||
|
@@ -17,14 +17,9 @@ export default class AudioResourceField extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.props.resourceSources ||
|
||||
!this.props.onChooseResource ||
|
||||
!this.props.resourceExternalEditors ||
|
||||
!this.props.project
|
||||
) {
|
||||
if (!this.props.resourceManagementProps || !this.props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for AudioResourceField'
|
||||
'Missing project or resourceManagementProps for AudioResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -33,9 +28,7 @@ export default class AudioResourceField extends Component<
|
||||
<ResourceSelector
|
||||
margin={this.props.isInline ? 'none' : 'dense'}
|
||||
project={this.props.project}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="audio"
|
||||
fullWidth
|
||||
|
@@ -17,14 +17,9 @@ export default class FontResourceField extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.props.resourceSources ||
|
||||
!this.props.onChooseResource ||
|
||||
!this.props.resourceExternalEditors ||
|
||||
!this.props.project
|
||||
) {
|
||||
if (!this.props.resourceManagementProps || !this.props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for BitmapFontResourceField'
|
||||
'Missing project or resourceManagementProps for BitmapFontResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -33,9 +28,7 @@ export default class FontResourceField extends Component<
|
||||
<ResourceSelector
|
||||
margin={this.props.isInline ? 'none' : 'dense'}
|
||||
project={this.props.project}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="bitmapFont"
|
||||
fullWidth
|
||||
|
@@ -17,14 +17,9 @@ export default class BitmapFontResourceField extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.props.resourceSources ||
|
||||
!this.props.onChooseResource ||
|
||||
!this.props.resourceExternalEditors ||
|
||||
!this.props.project
|
||||
) {
|
||||
if (!this.props.resourceManagementProps || !this.props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for FontResourceField'
|
||||
'Missing project or resourceManagementProps for FontResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -33,9 +28,7 @@ export default class BitmapFontResourceField extends Component<
|
||||
<ResourceSelector
|
||||
margin={this.props.isInline ? 'none' : 'dense'}
|
||||
project={this.props.project}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="font"
|
||||
fullWidth
|
||||
|
@@ -23,14 +23,9 @@ const ImageResourceField = React.forwardRef<
|
||||
focus,
|
||||
}));
|
||||
|
||||
if (
|
||||
!props.resourceSources ||
|
||||
!props.onChooseResource ||
|
||||
!props.resourceExternalEditors ||
|
||||
!props.project
|
||||
) {
|
||||
if (!props.resourceManagementProps || !props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for ImageResourceField'
|
||||
'Missing project or resourceManagementProps for ImageResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -39,9 +34,7 @@ const ImageResourceField = React.forwardRef<
|
||||
<ResourceSelector
|
||||
margin={props.isInline ? 'none' : 'dense'}
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="image"
|
||||
fullWidth
|
||||
|
@@ -17,14 +17,9 @@ export default class JsonResourceField extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.props.resourceSources ||
|
||||
!this.props.onChooseResource ||
|
||||
!this.props.resourceExternalEditors ||
|
||||
!this.props.project
|
||||
) {
|
||||
if (!this.props.resourceManagementProps || !this.props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for JsonResourceField'
|
||||
'Missing project or resourceManagementProps for JsonResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -33,9 +28,7 @@ export default class JsonResourceField extends Component<
|
||||
<ResourceSelector
|
||||
margin={this.props.isInline ? 'none' : 'dense'}
|
||||
project={this.props.project}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="json"
|
||||
fullWidth
|
||||
|
@@ -1,9 +1,5 @@
|
||||
// @flow
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import { type EventsScope } from '../../InstructionOrExpression/EventsScope.flow';
|
||||
import { type MessageDescriptor } from '../../Utils/i18n/MessageDescriptor.flow';
|
||||
|
||||
@@ -27,9 +23,7 @@ type CommonProps = {|
|
||||
isInline?: boolean,
|
||||
onRequestClose?: () => void,
|
||||
onApply?: () => void,
|
||||
resourceSources?: Array<ResourceSource>,
|
||||
onChooseResource?: ChooseResourceFunction,
|
||||
resourceExternalEditors?: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps?: ResourceManagementProps,
|
||||
|
||||
// Pass the ParameterRenderingService to allow to render nested parameters
|
||||
parameterRenderingService?: ParameterRenderingServiceType,
|
||||
|
@@ -17,14 +17,9 @@ export default class VideoResourceField extends Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
if (
|
||||
!this.props.resourceSources ||
|
||||
!this.props.onChooseResource ||
|
||||
!this.props.resourceExternalEditors ||
|
||||
!this.props.project
|
||||
) {
|
||||
if (!this.props.resourceManagementProps || !this.props.project) {
|
||||
console.error(
|
||||
'Missing project, resourceSources, onChooseResource or resourceExternalEditors for VideoResourceField'
|
||||
'Missing project or resourceManagementProps for VideoResourceField'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -33,9 +28,7 @@ export default class VideoResourceField extends Component<
|
||||
<ResourceSelector
|
||||
margin={this.props.isInline ? 'none' : 'dense'}
|
||||
project={this.props.project}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="video"
|
||||
fullWidth
|
||||
|
@@ -57,11 +57,7 @@ import EventsContextAnalyzerDialog, {
|
||||
toEventsContextResult,
|
||||
} from './EventsContextAnalyzerDialog';
|
||||
import SearchPanel, { type SearchPanelInterface } from './SearchPanel';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import EventsSearcher, {
|
||||
type ReplaceInEventsInputs,
|
||||
type SearchInEventsInputs,
|
||||
@@ -121,9 +117,7 @@ type Props = {|
|
||||
onOpenSettings?: ?() => void,
|
||||
onOpenExternalEvents: string => void,
|
||||
onOpenLayout: string => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
openInstructionOrExpression: (
|
||||
extension: gdPlatformExtension,
|
||||
type: string
|
||||
@@ -1578,9 +1572,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
ensureSingleOnceInstructions(instrsList);
|
||||
if (this._eventsTree) this._eventsTree.forceEventsUpdate();
|
||||
}}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={(extension, type) => {
|
||||
this.closeInstructionEditor();
|
||||
this.props.openInstructionOrExpression(extension, type);
|
||||
@@ -1648,9 +1640,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
globalObjectsContainer,
|
||||
objectsContainer,
|
||||
preferences,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
onCreateEventsFunction,
|
||||
tutorials,
|
||||
} = this.props;
|
||||
@@ -1807,9 +1797,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
|
||||
if (this._searchPanel)
|
||||
this._searchPanel.markSearchResultsDirty();
|
||||
}}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
<ContextMenu
|
||||
ref={eventContextMenu =>
|
||||
|
@@ -273,13 +273,16 @@ describe('EnumerateExpressions', () => {
|
||||
});
|
||||
|
||||
// Check that some behavior expressions are there
|
||||
expect(generalTreeNode).toHaveProperty('Platform behavior');
|
||||
const movementTreeNode: TreeNode<EnumeratedExpressionMetadata> =
|
||||
// $FlowFixMe
|
||||
allExpressionsTree['Movement'];
|
||||
expect(movementTreeNode).toHaveProperty('Platform behavior');
|
||||
// $FlowFixMe
|
||||
expect(generalTreeNode['Platform behavior']).toMatchObject({
|
||||
expect(movementTreeNode['Platform behavior']).toMatchObject({
|
||||
Options: {
|
||||
'Maximum horizontal speed': {
|
||||
displayedName: 'Maximum horizontal speed',
|
||||
fullGroupName: 'General/Platform behavior/Options',
|
||||
fullGroupName: 'Movement/Platform behavior/Options',
|
||||
iconFilename: 'CppPlatform/Extensions/platformerobjecticon.png',
|
||||
isPrivate: false,
|
||||
name: 'MaxSpeed',
|
||||
|
@@ -14,11 +14,7 @@ import { useSerializableObjectCancelableEditor } from '../Utils/SerializableObje
|
||||
import DismissableAlertMessage from '../UI/DismissableAlertMessage';
|
||||
import Text from '../UI/Text';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import HotReloadPreviewButton, {
|
||||
type HotReloadPreviewButtonProps,
|
||||
} from '../HotReload/HotReloadPreviewButton';
|
||||
@@ -30,9 +26,7 @@ const gd: libGDevelop = global.gd;
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
layer: gdLayer,
|
||||
initialInstances: gdInitialInstancesContainer,
|
||||
|
||||
@@ -200,9 +194,7 @@ const LayerEditorDialog = (props: Props) => {
|
||||
<EffectsList
|
||||
target="layer"
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
effectsContainer={layer.getEffects()}
|
||||
onEffectsUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positioned*/
|
||||
|
@@ -8,11 +8,7 @@ import LayerRow from './LayerRow';
|
||||
import BackgroundColorRow from './BackgroundColorRow';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import Add from '@material-ui/icons/Add';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import { type UnsavedChanges } from '../MainFrame/UnsavedChangesContext';
|
||||
import ScrollView from '../UI/ScrollView';
|
||||
import { FullSizeMeasurer } from '../UI/FullSizeMeasurer';
|
||||
@@ -112,9 +108,7 @@ const SortableLayersListBody = SortableContainer(LayersListBody);
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
layersContainer: gdLayout,
|
||||
onEditLayerEffects: (layer: ?gdLayer) => void,
|
||||
onEditLayer: (layer: ?gdLayer) => void,
|
||||
|
@@ -1,17 +1,12 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { type UnsavedChanges } from '../UnsavedChangesContext';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import type { StorageProvider } from '../../ProjectsStorage';
|
||||
import { type PreviewDebuggerServer } from '../../Export/PreviewLauncher.flow';
|
||||
import { type HotReloadPreviewButtonProps } from '../../HotReload/HotReloadPreviewButton';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type FileMetadataAndStorageProviderName } from '../../ProjectsStorage';
|
||||
import { type ExampleShortHeader } from '../../Utils/GDevelopServices/Example';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../../ProjectsStorage/ResourceFetcher';
|
||||
|
||||
export type EditorContainerExtraProps = {|
|
||||
// Events function extension editor
|
||||
@@ -32,10 +27,7 @@ export type RenderEditorContainerProps = {|
|
||||
extraEditorProps: ?EditorContainerExtraProps,
|
||||
|
||||
// Resources:
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|
||||
unsavedChanges: ?UnsavedChanges,
|
||||
|
||||
|
@@ -83,9 +83,7 @@ export class EventsEditorContainer extends React.Component<RenderEditorContainer
|
||||
ref={editor => (this.editor = editor)}
|
||||
setToolbar={this.props.setToolbar}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={this.props.openInstructionOrExpression}
|
||||
onCreateEventsFunction={this.onCreateEventsFunction}
|
||||
onBeginCreateEventsFunction={this.onBeginCreateEventsFunction}
|
||||
|
@@ -133,10 +133,7 @@ export class EventsFunctionsExtensionEditorContainer extends React.Component<Ren
|
||||
project={project}
|
||||
eventsFunctionsExtension={eventsFunctionsExtension}
|
||||
setToolbar={this.props.setToolbar}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={this.props.openInstructionOrExpression}
|
||||
onCreateEventsFunction={this.props.onCreateEventsFunction}
|
||||
initiallyFocusedFunctionName={initiallyFocusedFunctionName}
|
||||
|
@@ -143,9 +143,7 @@ export class ExternalEventsEditorContainer extends React.Component<
|
||||
ref={editor => (this.editor = editor)}
|
||||
setToolbar={this.props.setToolbar}
|
||||
onOpenLayout={this.props.onOpenLayout}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
openInstructionOrExpression={this.props.openInstructionOrExpression}
|
||||
onCreateEventsFunction={this.onCreateEventsFunction}
|
||||
onBeginCreateEventsFunction={this.onBeginCreateEventsFunction}
|
||||
|
@@ -165,10 +165,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
|
||||
{layout && (
|
||||
<SceneEditor
|
||||
setToolbar={this.props.setToolbar}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
unsavedChanges={this.props.unsavedChanges}
|
||||
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
|
||||
ref={editor => (this.editor = editor)}
|
||||
|
@@ -50,8 +50,7 @@ export class ResourcesEditorContainer extends React.Component<RenderEditorContai
|
||||
setToolbar={this.props.setToolbar}
|
||||
onDeleteResource={this.props.onDeleteResource}
|
||||
onRenameResource={this.props.onRenameResource}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
ref={editor => (this.editor = editor)}
|
||||
project={project}
|
||||
/>
|
||||
|
@@ -83,10 +83,7 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
|
||||
return (
|
||||
<SceneEditor
|
||||
setToolbar={this.props.setToolbar}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
onFetchNewlyAddedResources={this.props.onFetchNewlyAddedResources}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
unsavedChanges={this.props.unsavedChanges}
|
||||
ref={editor => (this.editor = editor)}
|
||||
project={project}
|
||||
|
@@ -515,6 +515,9 @@ const MainFrame = (props: Props) => {
|
||||
if (initialDialog === 'subscription') {
|
||||
openSubscriptionDialog(true);
|
||||
}
|
||||
if (initialDialog === 'onboarding') {
|
||||
openOnboardingDialog(true);
|
||||
}
|
||||
},
|
||||
[initialDialog]
|
||||
);
|
||||
@@ -2177,17 +2180,18 @@ const MainFrame = (props: Props) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const onChooseResource: ChooseResourceFunction = (
|
||||
options: ChooseResourceOptions
|
||||
) => {
|
||||
return new Promise(resolve => {
|
||||
setChooseResourceOptions(options);
|
||||
const onResourceChosenSetter: () => (
|
||||
Promise<Array<gdResource>> | Array<gdResource>
|
||||
) => void = () => resolve;
|
||||
setOnResourceChosen(onResourceChosenSetter);
|
||||
});
|
||||
};
|
||||
const onChooseResource: ChooseResourceFunction = React.useCallback(
|
||||
(options: ChooseResourceOptions) => {
|
||||
return new Promise(resolve => {
|
||||
setChooseResourceOptions(options);
|
||||
const onResourceChosenSetter: () => (
|
||||
Promise<Array<gdResource>> | Array<gdResource>
|
||||
) => void = () => resolve;
|
||||
setOnResourceChosen(onResourceChosenSetter);
|
||||
});
|
||||
},
|
||||
[setOnResourceChosen, setChooseResourceOptions]
|
||||
);
|
||||
|
||||
const setElectronUpdateStatus = (updateStatus: ElectronUpdateStatus) => {
|
||||
setState(state => ({ ...state, updateStatus }));
|
||||
@@ -2423,6 +2427,21 @@ const MainFrame = (props: Props) => {
|
||||
),
|
||||
});
|
||||
|
||||
const resourceManagementProps = React.useMemo(
|
||||
() => ({
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
onFetchNewlyAddedResources,
|
||||
}),
|
||||
[
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
onFetchNewlyAddedResources,
|
||||
]
|
||||
);
|
||||
|
||||
const showLoader = isLoadingProject || previewLoading;
|
||||
|
||||
return (
|
||||
@@ -2519,9 +2538,7 @@ const MainFrame = (props: Props) => {
|
||||
freezeUpdate={!projectManagerOpen}
|
||||
unsavedChanges={unsavedChanges}
|
||||
hotReloadPreviewButtonProps={hotReloadPreviewButtonProps}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
)}
|
||||
{!state.currentProject && (
|
||||
@@ -2600,10 +2617,7 @@ const MainFrame = (props: Props) => {
|
||||
openEventsEditor: true,
|
||||
openSceneEditor: false,
|
||||
}),
|
||||
resourceSources: props.resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
onFetchNewlyAddedResources,
|
||||
resourceManagementProps,
|
||||
onCreateEventsFunction,
|
||||
openInstructionOrExpression,
|
||||
unsavedChanges: unsavedChanges,
|
||||
@@ -2710,9 +2724,7 @@ const MainFrame = (props: Props) => {
|
||||
open
|
||||
onApply={() => openPlatformSpecificAssetsDialog(false)}
|
||||
onClose={() => openPlatformSpecificAssetsDialog(false)}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
)}
|
||||
{!!renderPreviewLauncher &&
|
||||
|
@@ -32,9 +32,7 @@ const CustomObjectPropertiesEditor = (props: Props) => {
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
unsavedChanges,
|
||||
} = props;
|
||||
|
||||
@@ -91,9 +89,7 @@ const CustomObjectPropertiesEditor = (props: Props) => {
|
||||
schema={propertiesSchema}
|
||||
instances={[customObjectConfiguration]}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
{eventBasedObject &&
|
||||
mapFor(0, eventBasedObject.getObjectsCount(), i => {
|
||||
@@ -163,10 +159,8 @@ const CustomObjectPropertiesEditor = (props: Props) => {
|
||||
<EditorComponent
|
||||
objectConfiguration={childObjectConfiguration}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={
|
||||
resourceExternalEditors
|
||||
resourceManagementProps={
|
||||
resourceManagementProps
|
||||
}
|
||||
onSizeUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positionned*/
|
||||
|
@@ -1,9 +1,5 @@
|
||||
// @flow
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../ResourcesList/ResourceSource';
|
||||
import { type UnsavedChanges } from '../../MainFrame/UnsavedChangesContext';
|
||||
|
||||
/**
|
||||
@@ -12,9 +8,7 @@ import { type UnsavedChanges } from '../../MainFrame/UnsavedChangesContext';
|
||||
export type EditorProps = {|
|
||||
objectConfiguration: gdObjectConfiguration,
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onSizeUpdated: () => void,
|
||||
objectName: string,
|
||||
unsavedChanges?: UnsavedChanges,
|
||||
|
@@ -22,9 +22,7 @@ const ObjectPropertiesEditor = (props: Props) => {
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
unsavedChanges,
|
||||
} = props;
|
||||
|
||||
@@ -79,9 +77,7 @@ const ObjectPropertiesEditor = (props: Props) => {
|
||||
schema={propertiesSchema}
|
||||
instances={[objectConfigurationAsGd]}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
|
@@ -17,9 +17,7 @@ export default class PanelSpriteEditor extends React.Component<
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
} = this.props;
|
||||
const panelSpriteConfiguration = gd.asPanelSpriteConfiguration(
|
||||
objectConfiguration
|
||||
@@ -29,9 +27,7 @@ export default class PanelSpriteEditor extends React.Component<
|
||||
<ColumnStackLayout>
|
||||
<ResourceSelectorWithThumbnail
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={panelSpriteConfiguration.getTexture()}
|
||||
onChange={resourceName => {
|
||||
|
@@ -27,9 +27,7 @@ export default class ParticleEmitterEditor extends React.Component<
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
} = this.props;
|
||||
const particleEmitterConfiguration = gd.asParticleEmitterConfiguration(
|
||||
objectConfiguration
|
||||
@@ -119,11 +117,9 @@ export default class ParticleEmitterEditor extends React.Component<
|
||||
gd.ParticleEmitterObject.Quad && (
|
||||
<ResourceSelectorWithThumbnail
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={particleEmitterConfiguration.getParticleTexture()}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
onChange={resourceName => {
|
||||
particleEmitterConfiguration.setParticleTexture(resourceName);
|
||||
this.forceUpdate();
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { type I18n as I18nType } from '@lingui/core';
|
||||
import React, { Component } from 'react';
|
||||
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
||||
import { mapFor } from '../../../Utils/MapFor';
|
||||
@@ -19,12 +19,13 @@ import {
|
||||
import ResourcesLoader from '../../../ResourcesLoader';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
type ResourceManagementProps,
|
||||
} from '../../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { applyResourceDefaults } from '../../../ResourcesList/ResourceUtils';
|
||||
import FlatButton from '../../../UI/FlatButton';
|
||||
import ThemeConsumer from '../../../UI/Theme/ThemeConsumer';
|
||||
import ElementWithMenu from '../../../UI/Menu/ElementWithMenu';
|
||||
const gd: libGDevelop = global.gd;
|
||||
const path = require('path');
|
||||
|
||||
@@ -45,26 +46,47 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
const AddSpriteButton = SortableElement(({ displayHint, onAdd }) => {
|
||||
return (
|
||||
<ThemeConsumer>
|
||||
{muiTheme => (
|
||||
<div
|
||||
style={{
|
||||
...thumbnailContainerStyle,
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
}}
|
||||
>
|
||||
<FlatButton
|
||||
onClick={onAdd}
|
||||
label={<Trans>Add</Trans>}
|
||||
leftIcon={<Add />}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</ThemeConsumer>
|
||||
);
|
||||
});
|
||||
type AddSpriteButtonProps = {|
|
||||
onAdd: (resourceSource: ResourceSource) => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
|};
|
||||
|
||||
const AddSpriteButton = SortableElement(
|
||||
({ onAdd, resourceSources }: AddSpriteButtonProps) => {
|
||||
return (
|
||||
<ThemeConsumer>
|
||||
{muiTheme => (
|
||||
<div
|
||||
style={{
|
||||
...thumbnailContainerStyle,
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
}}
|
||||
>
|
||||
<ElementWithMenu
|
||||
element={
|
||||
<FlatButton
|
||||
onClick={() => {
|
||||
/* Will be replaced by ElementWithMenu. */
|
||||
}}
|
||||
label={<Trans>Add</Trans>}
|
||||
leftIcon={<Add />}
|
||||
/>
|
||||
}
|
||||
buildMenuTemplate={(i18n: I18nType) =>
|
||||
resourceSources
|
||||
.filter(source => source.kind === 'image')
|
||||
.map(source => ({
|
||||
label: i18n._(source.displayName),
|
||||
click: () => onAdd(source),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</ThemeConsumer>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const SortableSpriteThumbnail = SortableElement(
|
||||
({ sprite, project, resourcesLoader, selected, onSelect, onContextMenu }) => {
|
||||
@@ -89,6 +111,7 @@ const SortableList = SortableContainer(
|
||||
project,
|
||||
resourcesLoader,
|
||||
onAddSprite,
|
||||
resourceSources,
|
||||
selectedSprites,
|
||||
onSelectSprite,
|
||||
onSpriteContextMenu,
|
||||
@@ -118,6 +141,7 @@ const SortableList = SortableContainer(
|
||||
disabled
|
||||
index={spritesCount}
|
||||
onAdd={onAddSprite}
|
||||
resourceSources={resourceSources}
|
||||
/>,
|
||||
]}
|
||||
</div>
|
||||
@@ -152,9 +176,7 @@ type Props = {|
|
||||
direction: gdDirection,
|
||||
project: gdProject,
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onSpriteContextMenu: (x: number, y: number, sprite: gdSprite) => void,
|
||||
selectedSprites: {
|
||||
[number]: boolean,
|
||||
@@ -178,51 +200,43 @@ export default class SpritesList extends Component<Props, void> {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
onAddSprite = () => {
|
||||
const {
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
project,
|
||||
direction,
|
||||
} = this.props;
|
||||
if (!resourceSources) return;
|
||||
const sources = resourceSources.filter(source => source.kind === 'image');
|
||||
if (!sources.length) return;
|
||||
onAddSprite = async (resourceSource: ResourceSource) => {
|
||||
const { resourceManagementProps, project, direction } = this.props;
|
||||
|
||||
const {
|
||||
allDirectionSpritesHaveSameCollisionMasks,
|
||||
allDirectionSpritesHaveSamePoints,
|
||||
} = checkDirectionPointsAndCollisionsMasks(direction);
|
||||
|
||||
onChooseResource({
|
||||
// Should be updated once new sources are introduced in the desktop app.
|
||||
// Search for "sources[0]" in the codebase for other places like this.
|
||||
initialSourceName: sources[0].name,
|
||||
const resources = await resourceManagementProps.onChooseResource({
|
||||
initialSourceName: resourceSource.name,
|
||||
multiSelection: true,
|
||||
resourceKind: 'image',
|
||||
}).then(resources => {
|
||||
resources.forEach(resource => {
|
||||
applyResourceDefaults(project, resource);
|
||||
project.getResourcesManager().addResource(resource);
|
||||
|
||||
const sprite = new gd.Sprite();
|
||||
sprite.setImageName(resource.getName());
|
||||
if (allDirectionSpritesHaveSamePoints) {
|
||||
copySpritePoints(direction.getSprite(0), sprite);
|
||||
}
|
||||
if (allDirectionSpritesHaveSameCollisionMasks) {
|
||||
copySpritePolygons(direction.getSprite(0), sprite);
|
||||
}
|
||||
direction.addSprite(sprite);
|
||||
sprite.delete();
|
||||
});
|
||||
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak, as calling addResource is making a copy of the resource.
|
||||
resources.forEach(resource => resource.delete());
|
||||
|
||||
this.forceUpdate();
|
||||
});
|
||||
|
||||
resources.forEach(resource => {
|
||||
applyResourceDefaults(project, resource);
|
||||
project.getResourcesManager().addResource(resource);
|
||||
|
||||
const sprite = new gd.Sprite();
|
||||
sprite.setImageName(resource.getName());
|
||||
if (allDirectionSpritesHaveSamePoints) {
|
||||
copySpritePoints(direction.getSprite(0), sprite);
|
||||
}
|
||||
if (allDirectionSpritesHaveSameCollisionMasks) {
|
||||
copySpritePolygons(direction.getSprite(0), sprite);
|
||||
}
|
||||
direction.addSprite(sprite);
|
||||
sprite.delete();
|
||||
});
|
||||
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak, as calling addResource is making a copy of the resource.
|
||||
resources.forEach(resource => resource.delete());
|
||||
|
||||
this.forceUpdate();
|
||||
|
||||
await resourceManagementProps.onFetchNewlyAddedResources();
|
||||
};
|
||||
|
||||
editWith = (externalEditor: ResourceExternalEditor) => {
|
||||
@@ -324,7 +338,9 @@ export default class SpritesList extends Component<Props, void> {
|
||||
direction={this.props.direction}
|
||||
resourcesLoader={this.props.resourcesLoader}
|
||||
project={this.props.project}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceExternalEditors={
|
||||
this.props.resourceManagementProps.resourceExternalEditors
|
||||
}
|
||||
onEditWith={this.editWith}
|
||||
/>
|
||||
</MiniToolbar>
|
||||
@@ -334,6 +350,7 @@ export default class SpritesList extends Component<Props, void> {
|
||||
project={this.props.project}
|
||||
onSortEnd={this.onSortEnd}
|
||||
onAddSprite={this.onAddSprite}
|
||||
resourceSources={this.props.resourceManagementProps.resourceSources}
|
||||
selectedSprites={this.props.selectedSprites}
|
||||
onSelectSprite={this.props.onSelectSprite}
|
||||
onSpriteContextMenu={this.props.onSpriteContextMenu}
|
||||
|
@@ -29,11 +29,7 @@ import {
|
||||
duplicateSpritesInAnimation,
|
||||
} from './Utils/SpriteObjectHelper';
|
||||
import { type EditorProps } from '../EditorProps.flow';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../../../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../../../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../../../ResourcesList/ResourceSource';
|
||||
import { Column } from '../../../UI/Grid';
|
||||
import { ResponsiveLineStackLayout } from '../../../UI/Layout';
|
||||
import ScrollView from '../../../UI/ScrollView';
|
||||
@@ -48,9 +44,7 @@ type AnimationProps = {|
|
||||
animation: gdAnimation,
|
||||
id: number,
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onRemove: () => void,
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
onSpriteContextMenu: (x: number, y: number, sprite: gdSprite) => void,
|
||||
@@ -72,10 +66,8 @@ class Animation extends React.Component<AnimationProps, void> {
|
||||
animation,
|
||||
id,
|
||||
project,
|
||||
resourceSources,
|
||||
onRemove,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
resourcesLoader,
|
||||
onSpriteContextMenu,
|
||||
selectedSprites,
|
||||
@@ -113,9 +105,7 @@ class Animation extends React.Component<AnimationProps, void> {
|
||||
key={i}
|
||||
project={project}
|
||||
resourcesLoader={resourcesLoader}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onSpriteContextMenu={onSpriteContextMenu}
|
||||
selectedSprites={selectedSprites}
|
||||
onSelectSprite={onSelectSprite}
|
||||
@@ -144,9 +134,7 @@ const SortableAnimationsList = SortableContainer(
|
||||
onChangeAnimationName,
|
||||
project,
|
||||
resourcesLoader,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
extraBottomTools,
|
||||
onSpriteContextMenu,
|
||||
selectedSprites,
|
||||
@@ -168,9 +156,7 @@ const SortableAnimationsList = SortableContainer(
|
||||
animation={animation}
|
||||
project={project}
|
||||
resourcesLoader={resourcesLoader}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onRemove={() => onRemoveAnimation(i)}
|
||||
onChangeName={newName => onChangeAnimationName(i, newName)}
|
||||
onSpriteContextMenu={onSpriteContextMenu}
|
||||
@@ -191,9 +177,7 @@ const SortableAnimationsList = SortableContainer(
|
||||
type AnimationsListContainerProps = {|
|
||||
spriteConfiguration: gdSpriteObject,
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
extraBottomTools: React.Node,
|
||||
onSizeUpdated: () => void,
|
||||
@@ -343,9 +327,7 @@ class AnimationsListContainer extends React.Component<
|
||||
selectedSprites={this.state.selectedSprites}
|
||||
onSelectSprite={this.selectSprite}
|
||||
resourcesLoader={this.props.resourcesLoader}
|
||||
resourceSources={this.props.resourceSources}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
useDragHandle
|
||||
lockAxis="y"
|
||||
axis="y"
|
||||
@@ -389,9 +371,7 @@ class AnimationsListContainer extends React.Component<
|
||||
export default function SpriteEditor({
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
onSizeUpdated,
|
||||
objectName,
|
||||
}: EditorProps) {
|
||||
@@ -409,9 +389,7 @@ export default function SpriteEditor({
|
||||
<AnimationsListContainer
|
||||
spriteConfiguration={spriteConfiguration}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
project={project}
|
||||
objectName={objectName}
|
||||
onSizeUpdated={onSizeUpdated}
|
||||
|
@@ -32,9 +32,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
} = this.props;
|
||||
const textObjectConfiguration = gd.asTextObjectConfiguration(
|
||||
objectConfiguration
|
||||
@@ -104,9 +102,7 @@ export default class TextEditor extends React.Component<EditorProps, void> {
|
||||
<ResourceSelector
|
||||
margin="none"
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind="font"
|
||||
fullWidth
|
||||
|
@@ -16,9 +16,7 @@ export default class TiledSpriteEditor extends React.Component<
|
||||
const {
|
||||
objectConfiguration,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
} = this.props;
|
||||
const tiledSpriteConfiguration = gd.asTiledSpriteConfiguration(
|
||||
objectConfiguration
|
||||
@@ -28,11 +26,9 @@ export default class TiledSpriteEditor extends React.Component<
|
||||
<ColumnStackLayout>
|
||||
<ResourceSelectorWithThumbnail
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={tiledSpriteConfiguration.getTexture()}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
onChange={resourceName => {
|
||||
tiledSpriteConfiguration.setTexture(resourceName);
|
||||
this.forceUpdate();
|
||||
|
@@ -12,11 +12,7 @@ import { useSerializableObjectCancelableEditor } from '../Utils/SerializableObje
|
||||
import SemiControlledTextField from '../UI/SemiControlledTextField';
|
||||
import { Column, Line } from '../UI/Grid';
|
||||
import { type EditorProps } from './Editors/EditorProps.flow';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import { type UnsavedChanges } from '../MainFrame/UnsavedChangesContext';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import HotReloadPreviewButton, {
|
||||
@@ -48,9 +44,7 @@ type Props = {|
|
||||
// Passed down to object editors:
|
||||
project: gdProject,
|
||||
onComputeAllVariableNames: () => Array<string>,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
unsavedChanges?: UnsavedChanges,
|
||||
onUpdateBehaviorsSharedData: () => void,
|
||||
initialTab: ?ObjectEditorTab,
|
||||
@@ -209,9 +203,7 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
<EditorComponent
|
||||
objectConfiguration={props.object.getConfiguration()}
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
onSizeUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positionned*/
|
||||
}
|
||||
@@ -223,9 +215,7 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
<BehaviorsEditor
|
||||
object={props.object}
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
onSizeUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positionned*/
|
||||
}
|
||||
@@ -260,9 +250,7 @@ const InnerDialog = (props: InnerDialogProps) => {
|
||||
<EffectsList
|
||||
target="object"
|
||||
project={props.project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
effectsContainer={props.object.getEffects()}
|
||||
onEffectsUpdated={
|
||||
forceUpdate /*Force update to ensure dialog is properly positionned*/
|
||||
|
@@ -41,13 +41,8 @@ import {
|
||||
import { type UnsavedChanges } from '../MainFrame/UnsavedChangesContext';
|
||||
import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewButton';
|
||||
import { useScreenType } from '../UI/Reponsive/ScreenTypeMeasurer';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type OnFetchNewlyAddedResourcesFunction } from '../ProjectsStorage/ResourceFetcher';
|
||||
import { getInstanceCountInLayoutForObject } from '../Utils/Layout';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const styles = {
|
||||
@@ -106,11 +101,8 @@ type Props = {|
|
||||
project: gdProject,
|
||||
layout: ?gdLayout,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
onFetchNewlyAddedResources: OnFetchNewlyAddedResourcesFunction,
|
||||
onSelectAllInstancesOfObjectInLayout?: string => void,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onDeleteObject: (
|
||||
objectWithContext: ObjectWithContext,
|
||||
cb: (boolean) => void
|
||||
@@ -639,11 +631,8 @@ export default class ObjectsList extends React.Component<Props, State> {
|
||||
project,
|
||||
layout,
|
||||
objectsContainer,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
selectedObjectTags,
|
||||
onFetchNewlyAddedResources,
|
||||
} = this.props;
|
||||
const { searchText, tagEditedObject } = this.state;
|
||||
|
||||
@@ -737,10 +726,7 @@ export default class ObjectsList extends React.Component<Props, State> {
|
||||
project={project}
|
||||
layout={layout}
|
||||
objectsContainer={objectsContainer}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
onFetchNewlyAddedResources={onFetchNewlyAddedResources}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
)}
|
||||
{tagEditedObject && (
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { t } from '@lingui/macro';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { type I18n as I18nType } from '@lingui/core';
|
||||
|
||||
import * as React from 'react';
|
||||
import FlatButton from '../UI/FlatButton';
|
||||
@@ -12,14 +13,14 @@ import ResourcesLoader from '../ResourcesLoader';
|
||||
import ResourceSelectorWithThumbnail from '../ResourcesList/ResourceSelectorWithThumbnail';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
type ResourceManagementProps,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { resizeImage, isResizeSupported } from './ImageResizer';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Text from '../UI/Text';
|
||||
import { ColumnStackLayout } from '../UI/Layout';
|
||||
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
|
||||
const path = optionalRequire('path');
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
@@ -28,9 +29,7 @@ type Props = {|
|
||||
open: boolean,
|
||||
onClose: Function,
|
||||
onApply: Function,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -109,123 +108,132 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
}
|
||||
}
|
||||
|
||||
_generateFromFile = () => {
|
||||
const { project, resourceSources, onChooseResource } = this.props;
|
||||
_generateFromFile = (resourceSource: ResourceSource) => {
|
||||
const { project, resourceManagementProps } = this.props;
|
||||
|
||||
const sources = resourceSources.filter(source => source.kind === 'image');
|
||||
if (!sources.length) return;
|
||||
if (!resourceSource.name.startsWith('local-file-opener')) {
|
||||
throw new Error('Only local files are supported for generating icons.');
|
||||
}
|
||||
|
||||
onChooseResource({
|
||||
// Should be updated once new sources are introduced in the desktop app.
|
||||
// Search for "sources[0]" in the codebase for other places like this.
|
||||
initialSourceName: sources[0].name,
|
||||
multiSelection: false,
|
||||
resourceKind: 'image',
|
||||
}).then(resources => {
|
||||
if (!resources.length || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourcesManager = project.getResourcesManager();
|
||||
const projectPath = path.dirname(project.getProjectFile());
|
||||
const fullPath = path.resolve(projectPath, resources[0].getFile());
|
||||
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak.
|
||||
resources.forEach(resource => resource.delete());
|
||||
|
||||
Promise.all([
|
||||
...desktopSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `desktop-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
...androidSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `android-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, 'android-windowSplashScreenAnimatedIcon.png'),
|
||||
{
|
||||
width: androidWindowSplashScreenAnimatedIconRecommendedSize,
|
||||
height: androidWindowSplashScreenAnimatedIconRecommendedSize,
|
||||
transparentBorderSize:
|
||||
androidWindowSplashScreenAnimatedIconRecommendedSize / 6,
|
||||
}
|
||||
),
|
||||
...iosSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `ios-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
]).then(results => {
|
||||
if (results.indexOf(false) !== -1) {
|
||||
showErrorBox({
|
||||
message: 'Some icons could not be generated!',
|
||||
rawError: undefined,
|
||||
errorId: 'icon-generation-error',
|
||||
doNotReport: true,
|
||||
});
|
||||
resourceManagementProps
|
||||
.onChooseResource({
|
||||
initialSourceName: resourceSource.name,
|
||||
multiSelection: false,
|
||||
resourceKind: 'image',
|
||||
})
|
||||
.then(resources => {
|
||||
if (!resources.length || !path) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add resources to the game
|
||||
const allResourcesNames = [
|
||||
...desktopSizes.map(size => `desktop-icon-${size}.png`),
|
||||
...androidSizes.map(size => `android-icon-${size}.png`),
|
||||
'android-windowSplashScreenAnimatedIcon.png',
|
||||
...iosSizes.map(size => `ios-icon-${size}.png`),
|
||||
];
|
||||
allResourcesNames.forEach(resourceName => {
|
||||
if (!resourcesManager.hasResource(resourceName)) {
|
||||
const imageResource = new gd.ImageResource();
|
||||
imageResource.setFile(resourceName);
|
||||
imageResource.setName(resourceName);
|
||||
const resourcesManager = project.getResourcesManager();
|
||||
const projectPath = path.dirname(project.getProjectFile());
|
||||
const fullPath = path.resolve(projectPath, resources[0].getFile());
|
||||
|
||||
resourcesManager.addResource(imageResource);
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak.
|
||||
resources.forEach(resource => resource.delete());
|
||||
|
||||
// Important, we are responsible for deleting the resources that we created
|
||||
// Otherwise we have a memory leak, as calling addResource is making a copy of the resource.
|
||||
imageResource.delete();
|
||||
} else {
|
||||
resourcesManager.getResource(resourceName).setFile(resourceName);
|
||||
Promise.all([
|
||||
...desktopSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `desktop-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
...androidSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `android-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(
|
||||
projectPath,
|
||||
'android-windowSplashScreenAnimatedIcon.png'
|
||||
),
|
||||
{
|
||||
width: androidWindowSplashScreenAnimatedIconRecommendedSize,
|
||||
height: androidWindowSplashScreenAnimatedIconRecommendedSize,
|
||||
transparentBorderSize:
|
||||
androidWindowSplashScreenAnimatedIconRecommendedSize / 6,
|
||||
}
|
||||
),
|
||||
...iosSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `ios-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
]).then(results => {
|
||||
if (results.indexOf(false) !== -1) {
|
||||
showErrorBox({
|
||||
message: 'Some icons could not be generated!',
|
||||
rawError: undefined,
|
||||
errorId: 'icon-generation-error',
|
||||
doNotReport: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure the resources are (re)loaded.
|
||||
ResourcesLoader.burstUrlsCacheForResources(project, allResourcesNames);
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
desktopIconResourceNames: desktopSizes.map(
|
||||
size => `desktop-icon-${size}.png`
|
||||
),
|
||||
androidIconResourceNames: androidSizes.map(
|
||||
size => `android-icon-${size}.png`
|
||||
),
|
||||
androidWindowSplashScreenAnimatedIconResourceName:
|
||||
'android-windowSplashScreenAnimatedIcon.png',
|
||||
iosIconResourceNames: iosSizes.map(size => `ios-icon-${size}.png`),
|
||||
// Add resources to the game
|
||||
const allResourcesNames = [
|
||||
...desktopSizes.map(size => `desktop-icon-${size}.png`),
|
||||
...androidSizes.map(size => `android-icon-${size}.png`),
|
||||
'android-windowSplashScreenAnimatedIcon.png',
|
||||
...iosSizes.map(size => `ios-icon-${size}.png`),
|
||||
];
|
||||
allResourcesNames.forEach(resourceName => {
|
||||
if (!resourcesManager.hasResource(resourceName)) {
|
||||
const imageResource = new gd.ImageResource();
|
||||
imageResource.setFile(resourceName);
|
||||
imageResource.setName(resourceName);
|
||||
|
||||
resourcesManager.addResource(imageResource);
|
||||
|
||||
// Important, we are responsible for deleting the resources that we created
|
||||
// Otherwise we have a memory leak, as calling addResource is making a copy of the resource.
|
||||
imageResource.delete();
|
||||
} else {
|
||||
resourcesManager.getResource(resourceName).setFile(resourceName);
|
||||
}
|
||||
});
|
||||
}, 200 /* Let a bit of time so that image files can be found */);
|
||||
|
||||
// Make sure the resources are (re)loaded.
|
||||
ResourcesLoader.burstUrlsCacheForResources(
|
||||
project,
|
||||
allResourcesNames
|
||||
);
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
desktopIconResourceNames: desktopSizes.map(
|
||||
size => `desktop-icon-${size}.png`
|
||||
),
|
||||
androidIconResourceNames: androidSizes.map(
|
||||
size => `android-icon-${size}.png`
|
||||
),
|
||||
androidWindowSplashScreenAnimatedIconResourceName:
|
||||
'android-windowSplashScreenAnimatedIcon.png',
|
||||
iosIconResourceNames: iosSizes.map(
|
||||
size => `ios-icon-${size}.png`
|
||||
),
|
||||
});
|
||||
}, 200 /* Let a bit of time so that image files can be found */);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onApply = () => {
|
||||
@@ -287,12 +295,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
onClick={this.onApply}
|
||||
/>,
|
||||
];
|
||||
const {
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
} = this.props;
|
||||
const { project, resourceManagementProps } = this.props;
|
||||
const {
|
||||
thumbnailResourceName,
|
||||
desktopIconResourceNames,
|
||||
@@ -310,29 +313,29 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
onApply={this.onApply}
|
||||
>
|
||||
<ColumnStackLayout noMargin>
|
||||
<Text size="sub-title">
|
||||
<Trans>Liluo.io thumbnail</Trans>
|
||||
</Text>
|
||||
<ResourceSelectorWithThumbnail
|
||||
floatingLabelText={`Liluo.io thumbnail (1920x1080 px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceKind="image"
|
||||
resourceName={thumbnailResourceName}
|
||||
onChange={resourceName => {
|
||||
this.setState({
|
||||
thumbnailResourceName: resourceName,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Line justifyContent="center">
|
||||
<Line justifyContent="center" noMargin>
|
||||
{isResizeSupported() ? (
|
||||
<RaisedButton
|
||||
primary
|
||||
label={<Trans>Generate icons from a file</Trans>}
|
||||
onClick={this._generateFromFile}
|
||||
<ElementWithMenu
|
||||
element={
|
||||
<RaisedButton
|
||||
primary
|
||||
label={<Trans>Generate icons from a file</Trans>}
|
||||
onClick={() => {
|
||||
/* Will be replaced by ElementWithMenu */
|
||||
}}
|
||||
/>
|
||||
}
|
||||
buildMenuTemplate={(i18n: I18nType) =>
|
||||
resourceManagementProps.resourceSources
|
||||
.filter(source => source.kind === 'image')
|
||||
.filter(source =>
|
||||
source.name.startsWith('local-file-opener')
|
||||
)
|
||||
.map(source => ({
|
||||
label: i18n._(source.displayName),
|
||||
click: () => this._generateFromFile(source),
|
||||
}))
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Text>
|
||||
@@ -343,6 +346,21 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
</Text>
|
||||
)}
|
||||
</Line>
|
||||
<Text size="sub-title">
|
||||
<Trans>Liluo.io thumbnail</Trans>
|
||||
</Text>
|
||||
<ResourceSelectorWithThumbnail
|
||||
floatingLabelText={`Liluo.io thumbnail (1920x1080 px)`}
|
||||
project={project}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={thumbnailResourceName}
|
||||
onChange={resourceName => {
|
||||
this.setState({
|
||||
thumbnailResourceName: resourceName,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Text size="sub-title">
|
||||
<Trans>Desktop (Windows, macOS and Linux) icon</Trans>
|
||||
</Text>
|
||||
@@ -351,9 +369,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
key={size}
|
||||
floatingLabelText={`Desktop icon (${size}x${size} px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={desktopIconResourceNames[index]}
|
||||
onChange={resourceName => {
|
||||
@@ -373,9 +389,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
<ResourceSelectorWithThumbnail
|
||||
floatingLabelText={`Android 12+ splashscreen icon (576x576 px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={androidWindowSplashScreenAnimatedIconResourceName}
|
||||
onChange={resourceName => {
|
||||
@@ -394,9 +408,7 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
key={size}
|
||||
floatingLabelText={`Android icon (${size}x${size} px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={androidIconResourceNames[index]}
|
||||
onChange={resourceName => {
|
||||
@@ -416,11 +428,9 @@ export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
key={size}
|
||||
floatingLabelText={`iOS icon (${size}x${size} px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={iosIconResourceNames[index]}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
onChange={resourceName => {
|
||||
const newIcons = [...iosIconResourceNames];
|
||||
newIcons[index] = resourceName;
|
||||
|
@@ -14,11 +14,7 @@ import {
|
||||
} from '../Utils/ColorTransformer';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import ResourceSelectorWithThumbnail from '../ResourcesList/ResourceSelectorWithThumbnail';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import SelectField from '../UI/SelectField';
|
||||
import SelectOption from '../UI/SelectOption';
|
||||
import Text from '../UI/Text';
|
||||
@@ -29,18 +25,14 @@ type Props = {
|
||||
|
||||
// For resources:
|
||||
project: gdProject,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
};
|
||||
|
||||
export const LoadingScreenEditor = ({
|
||||
loadingScreen,
|
||||
onChangeSubscription,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
}: Props) => {
|
||||
const subscriptionChecker = React.useRef<?SubscriptionChecker>(null);
|
||||
const forceUpdate = useForceUpdate();
|
||||
@@ -56,9 +48,7 @@ export const LoadingScreenEditor = ({
|
||||
<ResourceSelectorWithThumbnail
|
||||
floatingLabelText={<Trans>Background image</Trans>}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourceKind="image"
|
||||
resourceName={loadingScreen.getBackgroundImageResourceName()}
|
||||
onChange={resourceName => {
|
||||
|
@@ -27,11 +27,7 @@ import AlertMessage from '../UI/AlertMessage';
|
||||
import { GameRegistration } from '../GameDashboard/GameRegistration';
|
||||
import { Tab, Tabs } from '../UI/Tabs';
|
||||
import { LoadingScreenEditor } from './LoadingScreenEditor';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import {
|
||||
type HotReloadPreviewButtonProps,
|
||||
NewPreviewIcon,
|
||||
@@ -49,9 +45,7 @@ type Props = {|
|
||||
hotReloadPreviewButtonProps?: ?HotReloadPreviewButtonProps,
|
||||
|
||||
// For resources:
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
type ProjectProperties = {|
|
||||
@@ -597,9 +591,7 @@ function ProjectPropertiesDialog(props: Props) {
|
||||
props.onChangeSubscription();
|
||||
}}
|
||||
project={project}
|
||||
resourceSources={props.resourceSources}
|
||||
onChooseResource={props.onChooseResource}
|
||||
resourceExternalEditors={props.resourceExternalEditors}
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
|
@@ -47,11 +47,7 @@ import ProjectManagerCommands from './ProjectManagerCommands';
|
||||
import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewButton';
|
||||
import { type ExtensionShortHeader } from '../Utils/GDevelopServices/Extension';
|
||||
import EventsRootVariablesFinder from '../Utils/EventsRootVariablesFinder';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
import InstalledExtensionDetails from './InstalledExtensionDetails';
|
||||
import {
|
||||
Item,
|
||||
@@ -111,9 +107,7 @@ type Props = {|
|
||||
onInstallExtension: ExtensionShortHeader => void,
|
||||
|
||||
// For resources:
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceExternalEditors: Array<ResourceExternalEditor>,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -1112,9 +1106,7 @@ export default class ProjectManager extends React.Component<Props, State> {
|
||||
onApply={this.props.onSaveProjectProperties}
|
||||
onPropertiesApplied={this._onProjectPropertiesApplied}
|
||||
onChangeSubscription={this.props.onChangeSubscription}
|
||||
resourceSources={this.props.resourceSources}
|
||||
onChooseResource={this.props.onChooseResource}
|
||||
resourceExternalEditors={this.props.resourceExternalEditors}
|
||||
resourceManagementProps={this.props.resourceManagementProps}
|
||||
hotReloadPreviewButtonProps={
|
||||
this.props.hotReloadPreviewButtonProps
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { type I18n as I18nType } from '@lingui/core';
|
||||
import * as React from 'react';
|
||||
import SemiControlledTextField from '../UI/SemiControlledTextField';
|
||||
import InlineCheckbox from '../UI/InlineCheckbox';
|
||||
@@ -13,13 +14,11 @@ import ColorField from '../UI/ColorField';
|
||||
import { MarkdownText } from '../UI/MarkdownText';
|
||||
import { rgbOrHexToRGBString } from '../Utils/ColorTransformer';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
|
||||
import { type MenuItemTemplate } from '../UI/Menu/Menu.flow';
|
||||
import {
|
||||
type ResourceKind,
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
type ResourceManagementProps,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { type ResourceExternalEditor } from '../ResourcesList/ResourceExternalEditor.flow';
|
||||
import {
|
||||
TextFieldWithButtonLayout,
|
||||
ResponsiveLineStackLayout,
|
||||
@@ -32,6 +31,7 @@ import UnsavedChangesContext, {
|
||||
import { Line, Spacer } from '../UI/Grid';
|
||||
import Text from '../UI/Text';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import ElementWithMenu from '../UI/Menu/ElementWithMenu';
|
||||
|
||||
// An "instance" here is the objects for which properties are shown
|
||||
export type Instance = Object; // This could be improved using generics.
|
||||
@@ -44,7 +44,7 @@ export type ValueFieldCommonProperties = {|
|
||||
getDescription?: Instance => string,
|
||||
getExtraDescription?: Instance => string,
|
||||
disabled?: boolean | ((instances: Array<gdInitialInstance>) => boolean),
|
||||
onEditButtonClick?: Instance => void,
|
||||
onEditButtonBuildMenuTemplate?: (i18n: I18nType) => Array<MenuItemTemplate>,
|
||||
|};
|
||||
|
||||
// "Primitive" value fields are "simple" fields.
|
||||
@@ -141,9 +141,7 @@ type Props = {|
|
||||
|
||||
// Optional context:
|
||||
project?: ?gdProject,
|
||||
resourceSources?: ?Array<ResourceSource>,
|
||||
onChooseResource?: ?ChooseResourceFunction,
|
||||
resourceExternalEditors?: ?Array<ResourceExternalEditor>,
|
||||
resourceManagementProps?: ?ResourceManagementProps,
|
||||
|};
|
||||
|
||||
const styles = {
|
||||
@@ -230,9 +228,7 @@ const PropertiesEditor = ({
|
||||
renderExtraDescriptionText,
|
||||
unsavedChanges,
|
||||
project,
|
||||
resourceSources,
|
||||
onChooseResource,
|
||||
resourceExternalEditors,
|
||||
resourceManagementProps,
|
||||
}: Props) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
@@ -358,7 +354,7 @@ const PropertiesEditor = ({
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
const { onEditButtonClick, setValue } = field;
|
||||
const { onEditButtonBuildMenuTemplate, setValue } = field;
|
||||
return (
|
||||
<TextFieldWithButtonLayout
|
||||
key={field.name}
|
||||
@@ -378,14 +374,21 @@ const PropertiesEditor = ({
|
||||
/>
|
||||
)}
|
||||
renderButton={style =>
|
||||
onEditButtonClick ? (
|
||||
<RaisedButton
|
||||
style={style}
|
||||
primary
|
||||
disabled={instances.length !== 1}
|
||||
icon={<Edit />}
|
||||
label={<Trans>Edit</Trans>}
|
||||
onClick={() => onEditButtonClick(instances[0])}
|
||||
onEditButtonBuildMenuTemplate ? (
|
||||
<ElementWithMenu
|
||||
element={
|
||||
<RaisedButton
|
||||
style={style}
|
||||
primary
|
||||
disabled={instances.length !== 1}
|
||||
icon={<Edit />}
|
||||
label={<Trans>Edit</Trans>}
|
||||
onClick={() => {
|
||||
/* Will be replaced by ElementWithMenu */
|
||||
}}
|
||||
/>
|
||||
}
|
||||
buildMenuTemplate={onEditButtonBuildMenuTemplate}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
@@ -479,14 +482,9 @@ const PropertiesEditor = ({
|
||||
);
|
||||
|
||||
const renderResourceField = (field: ResourceField) => {
|
||||
if (
|
||||
!project ||
|
||||
!resourceSources ||
|
||||
!onChooseResource ||
|
||||
!resourceExternalEditors
|
||||
) {
|
||||
if (!project || !resourceManagementProps) {
|
||||
console.error(
|
||||
'You tried to display a resource field in a PropertiesEditor that does not support display resources. If you need to display resources, pass additional props (project, resourceSources, onChooseResource, resourceExternalEditors).'
|
||||
'You tried to display a resource field in a PropertiesEditor that does not support display resources. If you need to display resources, pass additional props (project, resourceManagementProps).'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -496,9 +494,7 @@ const PropertiesEditor = ({
|
||||
<ResourceSelector
|
||||
key={field.name}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
resourceKind={field.resourceKind}
|
||||
fullWidth
|
||||
@@ -582,9 +578,7 @@ const PropertiesEditor = ({
|
||||
{unsavedChanges => (
|
||||
<PropertiesEditor
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
schema={field.children}
|
||||
instances={instances}
|
||||
mode="row"
|
||||
@@ -613,9 +607,7 @@ const PropertiesEditor = ({
|
||||
{unsavedChanges => (
|
||||
<PropertiesEditor
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceExternalEditors={resourceExternalEditors}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
schema={field.children}
|
||||
instances={instances}
|
||||
mode="column"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { type I18n as I18nType } from '@lingui/core';
|
||||
|
||||
import * as React from 'react';
|
||||
import Background from '../../UI/Background';
|
||||
@@ -12,7 +13,7 @@ import { type Schema } from '../../PropertiesEditor';
|
||||
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
type ResourceManagementProps,
|
||||
} from '../../ResourcesList/ResourceSource';
|
||||
|
||||
const styles = {
|
||||
@@ -29,8 +30,7 @@ type Props = {|
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
resources: Array<gdResource>,
|
||||
onResourcePathUpdated: () => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
export default class ResourcePropertiesEditor extends React.Component<
|
||||
@@ -52,7 +52,13 @@ export default class ResourcePropertiesEditor extends React.Component<
|
||||
getValue: (resource: gdResource) => resource.getFile(),
|
||||
setValue: (resource: gdResource, newValue: string) =>
|
||||
resource.setFile(newValue),
|
||||
onEditButtonClick: () => this._chooseResourcePath(),
|
||||
onEditButtonBuildMenuTemplate: (i18n: I18nType) =>
|
||||
this.props.resourceManagementProps.resourceSources
|
||||
.filter(source => source.kind === this.props.resources[0].getKind())
|
||||
.map(source => ({
|
||||
label: i18n._(source.displayName),
|
||||
click: () => this._chooseResourcePath(source),
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -68,35 +74,30 @@ export default class ResourcePropertiesEditor extends React.Component<
|
||||
);
|
||||
}
|
||||
|
||||
_chooseResourcePath = () => {
|
||||
_chooseResourcePath = async (resourceSource: ResourceSource) => {
|
||||
const {
|
||||
resources,
|
||||
onResourcePathUpdated,
|
||||
onChooseResource,
|
||||
resourceSources,
|
||||
resourceManagementProps,
|
||||
} = this.props;
|
||||
const resource = resources[0];
|
||||
const sources = resourceSources.filter(
|
||||
source => source.kind === resource.getKind()
|
||||
);
|
||||
if (!sources.length) return;
|
||||
onChooseResource({
|
||||
// Should be updated once new sources are introduced in the desktop app.
|
||||
// Search for "sources[0]" in the codebase for other places like this.
|
||||
initialSourceName: sources[0].name,
|
||||
multiSelection: true,
|
||||
|
||||
const newResources = await resourceManagementProps.onChooseResource({
|
||||
initialSourceName: resourceSource.name,
|
||||
multiSelection: false,
|
||||
resourceKind: resource.getKind(),
|
||||
}).then(resources => {
|
||||
if (!resources.length) return; // No path was chosen by the user.
|
||||
resource.setFile(resources[0].getFile());
|
||||
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak.
|
||||
resources.forEach(resource => resource.delete());
|
||||
|
||||
onResourcePathUpdated();
|
||||
this.forceUpdate();
|
||||
});
|
||||
if (!newResources.length) return; // No path was chosen by the user.
|
||||
resource.setFile(newResources[0].getFile());
|
||||
|
||||
// Important, we are responsible for deleting the resources that were given to us.
|
||||
// Otherwise we have a memory leak.
|
||||
newResources.forEach(resource => resource.delete());
|
||||
|
||||
onResourcePathUpdated();
|
||||
this.forceUpdate();
|
||||
|
||||
await resourceManagementProps.onFetchNewlyAddedResources();
|
||||
};
|
||||
|
||||
_renderResourcesProperties() {
|
||||
|
@@ -13,8 +13,7 @@ import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Window from '../Utils/Window';
|
||||
import PreferencesContext from '../MainFrame/Preferences/PreferencesContext';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
type ResourceManagementProps,
|
||||
type ResourceKind,
|
||||
} from '../ResourcesList/ResourceSource';
|
||||
import { getResourceFilePathStatus } from '../ResourcesList/ResourceUtils';
|
||||
@@ -47,8 +46,7 @@ type Props = {|
|
||||
newName: string,
|
||||
cb: (boolean) => void
|
||||
) => void,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
|};
|
||||
|
||||
const initialMosaicEditorNodes = {
|
||||
@@ -207,12 +205,7 @@ export default class ResourcesEditor extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
project,
|
||||
onRenameResource,
|
||||
onChooseResource,
|
||||
resourceSources,
|
||||
} = this.props;
|
||||
const { project, onRenameResource, resourceManagementProps } = this.props;
|
||||
const { selectedResource } = this.state;
|
||||
|
||||
const editors = {
|
||||
@@ -233,8 +226,7 @@ export default class ResourcesEditor extends React.Component<Props, State> {
|
||||
this._resourcesList.checkMissingPaths();
|
||||
}
|
||||
}}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceSources={resourceSources}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
@@ -1,30 +1,74 @@
|
||||
// @flow
|
||||
import { t } from '@lingui/macro';
|
||||
import { t, Trans } from '@lingui/macro';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
type ChooseResourceOptions,
|
||||
type ChooseResourceProps,
|
||||
type ResourceSourceComponentProps,
|
||||
type ResourceSource,
|
||||
allResourceKindsAndMetadata,
|
||||
} from './ResourceSource';
|
||||
import { ResourceStore } from '../AssetStore/ResourceStore';
|
||||
import { isPathInProjectFolder, copyAllToProjectFolder } from './ResourceUtils';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Window from '../Utils/Window';
|
||||
import { Line } from '../UI/Grid';
|
||||
import RaisedButton from '../UI/RaisedButton';
|
||||
const remote = optionalRequire('@electron/remote');
|
||||
const dialog = remote ? remote.dialog : null;
|
||||
const path = optionalRequire('path');
|
||||
|
||||
type ResourceStoreChooserProps = {
|
||||
options: ChooseResourceOptions,
|
||||
onChooseResources: (resources: Array<gdResource>) => void,
|
||||
createNewResource: () => gdResource,
|
||||
};
|
||||
|
||||
const ResourceStoreChooser = ({
|
||||
options,
|
||||
onChooseResources,
|
||||
createNewResource,
|
||||
}: ResourceStoreChooserProps) => {
|
||||
return (
|
||||
<ResourceStore
|
||||
onChoose={resource => {
|
||||
const chosenResourceUrl = resource.url;
|
||||
const newResource = createNewResource();
|
||||
newResource.setFile(chosenResourceUrl);
|
||||
newResource.setName(path.basename(chosenResourceUrl));
|
||||
newResource.setOrigin('gdevelop-asset-store', chosenResourceUrl);
|
||||
|
||||
onChooseResources([newResource]);
|
||||
}}
|
||||
resourceKind={options.resourceKind}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const localResourceSources: Array<ResourceSource> = [
|
||||
...allResourceKindsAndMetadata.map(({ kind, createNewResource }) => ({
|
||||
name: `resource-store-${kind}`,
|
||||
displayName: t`Choose from asset store`,
|
||||
displayTab: 'standalone',
|
||||
kind,
|
||||
renderComponent: (props: ResourceSourceComponentProps) => (
|
||||
<ResourceStoreChooser
|
||||
createNewResource={createNewResource}
|
||||
onChooseResources={props.onChooseResources}
|
||||
options={props.options}
|
||||
key={`resource-store-${kind}`}
|
||||
/>
|
||||
),
|
||||
})),
|
||||
...allResourceKindsAndMetadata.map(
|
||||
({ kind, displayName, fileExtensions, createNewResource }) => ({
|
||||
name: 'local-file-opener-' + kind,
|
||||
displayName: t`Choose a file`,
|
||||
displayTab: 'import',
|
||||
kind,
|
||||
selectResourcesHeadless: async ({
|
||||
({ kind, displayName, fileExtensions, createNewResource }) => {
|
||||
const selectLocalFileResources = async ({
|
||||
i18n,
|
||||
getLastUsedPath,
|
||||
setLastUsedPath,
|
||||
project,
|
||||
options,
|
||||
}) => {
|
||||
}: ChooseResourceProps) => {
|
||||
if (!dialog)
|
||||
throw new Error('Electron dialog not supported in this environment.');
|
||||
|
||||
@@ -72,9 +116,43 @@ const localResourceSources: Array<ResourceSource> = [
|
||||
|
||||
return newResource;
|
||||
});
|
||||
},
|
||||
renderComponent: () => null,
|
||||
})
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'local-file-opener-' + kind,
|
||||
displayName: t`Choose a file`,
|
||||
displayTab: 'import',
|
||||
kind,
|
||||
selectResourcesHeadless: selectLocalFileResources,
|
||||
renderComponent: (props: ResourceSourceComponentProps) => (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
primary
|
||||
label={
|
||||
props.options.multiSelection ? (
|
||||
<Trans>Choose one or more files</Trans>
|
||||
) : (
|
||||
<Trans>Choose a file</Trans>
|
||||
)
|
||||
}
|
||||
onClick={async () => {
|
||||
const resources = await selectLocalFileResources({
|
||||
i18n: props.i18n,
|
||||
project: props.project,
|
||||
fileMetadata: props.fileMetadata,
|
||||
getStorageProvider: props.getStorageProvider,
|
||||
getLastUsedPath: props.getLastUsedPath,
|
||||
setLastUsedPath: props.setLastUsedPath,
|
||||
options: props.options,
|
||||
});
|
||||
|
||||
props.onChooseResources(resources);
|
||||
}}
|
||||
/>
|
||||
</Line>
|
||||
),
|
||||
};
|
||||
}
|
||||
),
|
||||
];
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user