From f742b393e107e22080a545c6c7bc6b2e79cbea64 Mon Sep 17 00:00:00 2001 From: Florian Rival Date: Tue, 17 Sep 2024 10:53:26 +0200 Subject: [PATCH] Add buttons to edit animations for Sprite, Spine, tilemap --- .../SpriteExtension/SpriteExtension.cpp | 1 + .../Builtin/SpriteExtension/SpriteObject.cpp | 8 ---- .../Extensions/Metadata/ObjectMetadata.h | 10 ++++ Extensions/Spine/JsExtension.js | 11 +++-- Extensions/TileMap/JsExtension.js | 1 + GDevelop.js/Bindings/Bindings.idl | 3 ++ GDevelop.js/types.d.ts | 2 + GDevelop.js/types/gdobjectmetadata.js | 2 + .../app/src/CompactPropertiesEditor/index.js | 46 +++++++++++-------- .../CompactObjectPropertiesSchema.js | 40 ++++++++++++++++ .../CompactObjectPropertiesEditor/index.js | 26 +++++++++-- 11 files changed, 113 insertions(+), 37 deletions(-) create mode 100644 newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/CompactObjectPropertiesSchema.js diff --git a/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteExtension.cpp b/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteExtension.cpp index 4e7193a93860..9e9033aaf16e 100644 --- a/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteExtension.cpp +++ b/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteExtension.cpp @@ -33,6 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSpriteExtension( "most elements of a game."), "CppPlatform/Extensions/spriteicon.png") .SetCategoryFullName(_("General")) + .SetOpenFullEditorLabel(_("Edit animations")) .AddDefaultBehavior("EffectCapability::EffectBehavior") .AddDefaultBehavior("ResizableCapability::ResizableBehavior") .AddDefaultBehavior("ScalableCapability::ScalableBehavior") diff --git a/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.cpp b/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.cpp index 4fd7b0195952..cbc812c276a9 100644 --- a/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.cpp +++ b/Core/GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.cpp @@ -47,19 +47,11 @@ void SpriteObject::DoSerializeTo(gd::SerializerElement& element) const { std::map SpriteObject::GetProperties() const { std::map properties; - properties[_("Animate even if hidden or far from the screen")] - .SetValue(updateIfNotVisible ? "true" : "false") - .SetType("Boolean"); - properties["PLEASE_ALSO_SHOW_EDIT_BUTTON_THANKS"].SetValue(""); - return properties; } bool SpriteObject::UpdateProperty(const gd::String& name, const gd::String& value) { - if (name == _("Animate even if hidden or far from the screen")) - updateIfNotVisible = value == "1"; - return true; } diff --git a/Core/GDCore/Extensions/Metadata/ObjectMetadata.h b/Core/GDCore/Extensions/Metadata/ObjectMetadata.h index b13e77032b76..dfcc4eabf5c9 100644 --- a/Core/GDCore/Extensions/Metadata/ObjectMetadata.h +++ b/Core/GDCore/Extensions/Metadata/ObjectMetadata.h @@ -323,6 +323,15 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada */ bool IsRenderedIn3D() const { return isRenderedIn3D; } + ObjectMetadata &SetOpenFullEditorLabel(const gd::String& label) { + openFullEditorLabel = label; + return *this; + } + + const gd::String& GetOpenFullEditorLabel() const { + return openFullEditorLabel; + } + std::map conditionsInfos; std::map actionsInfos; std::map expressionsInfos; @@ -344,6 +353,7 @@ class GD_CORE_API ObjectMetadata : public InstructionOrExpressionContainerMetada std::set defaultBehaviorTypes; bool hidden = false; bool isRenderedIn3D = false; + gd::String openFullEditorLabel; std::shared_ptr blueprintObject; ///< The "blueprint" object to be copied when a new diff --git a/Extensions/Spine/JsExtension.js b/Extensions/Spine/JsExtension.js index 3ae70ec22c7e..73f95fd80069 100644 --- a/Extensions/Spine/JsExtension.js +++ b/Extensions/Spine/JsExtension.js @@ -54,7 +54,8 @@ module.exports = { .addIncludeFile('Extensions/Spine/pixi-spine/pixi-spine.js') .addIncludeFile('Extensions/Spine/managers/pixi-spine-atlas-manager.js') .addIncludeFile('Extensions/Spine/managers/pixi-spine-manager.js') - .setCategoryFullName(_('Advanced')); + .setCategoryFullName(_('Advanced')) + .setOpenFullEditorLabel(_('Edit animations')); object .addExpressionAndConditionAndAction( @@ -208,7 +209,7 @@ module.exports = { this.setAnimation(this._instance.getRawDoubleProperty('animation')); - const scale = Number(properties.get('scale').getValue()) || 1 + const scale = Number(properties.get('scale').getValue()) || 1; const spine = this._spine; if (spine) { @@ -265,7 +266,9 @@ module.exports = { */ setAnimation(index) { const { _spine: spine } = this; - const configuration = gd.asSpineConfiguration(this._associatedObjectConfiguration); + const configuration = gd.asSpineConfiguration( + this._associatedObjectConfiguration + ); if ( !spine || @@ -318,7 +321,7 @@ module.exports = { this._pixiResourcesLoader .getSpineData(this._project, this._spineResourceName) .then((spineDataOrLoadingError) => { - if (this._spine) this._pixiObject.removeChild(this._spine) + if (this._spine) this._pixiObject.removeChild(this._spine); if (!spineDataOrLoadingError.skeleton) { console.error( diff --git a/Extensions/TileMap/JsExtension.js b/Extensions/TileMap/JsExtension.js index e80702f63363..d5a92b05db93 100644 --- a/Extensions/TileMap/JsExtension.js +++ b/Extensions/TileMap/JsExtension.js @@ -720,6 +720,7 @@ const defineSimpleTileMap = function (extension, _, gd) { objectSimpleTileMap ) .setCategoryFullName(_('General')) + .setOpenFullEditorLabel(_('Edit tileset and collisions')) .addDefaultBehavior('ResizableCapability::ResizableBehavior') .addDefaultBehavior('ScalableCapability::ScalableBehavior') .addDefaultBehavior('OpacityCapability::OpacityBehavior') diff --git a/GDevelop.js/Bindings/Bindings.idl b/GDevelop.js/Bindings/Bindings.idl index 9e595a74e3ee..0b2c44d18d13 100644 --- a/GDevelop.js/Bindings/Bindings.idl +++ b/GDevelop.js/Bindings/Bindings.idl @@ -1893,6 +1893,9 @@ interface ObjectMetadata { [Ref] ObjectMetadata MarkAsRenderedIn3D(); boolean IsRenderedIn3D(); + + [Ref] ObjectMetadata SetOpenFullEditorLabel([Const] DOMString label); + [Const, Ref] DOMString GetOpenFullEditorLabel(); }; enum QuickCustomization_Visibility { diff --git a/GDevelop.js/types.d.ts b/GDevelop.js/types.d.ts index d45afb572890..714052c7774c 100644 --- a/GDevelop.js/types.d.ts +++ b/GDevelop.js/types.d.ts @@ -1514,6 +1514,8 @@ export class ObjectMetadata extends EmscriptenObject { isHidden(): boolean; markAsRenderedIn3D(): ObjectMetadata; isRenderedIn3D(): boolean; + setOpenFullEditorLabel(label: string): ObjectMetadata; + getOpenFullEditorLabel(): string; } export class QuickCustomization extends EmscriptenObject {static Default = 0; diff --git a/GDevelop.js/types/gdobjectmetadata.js b/GDevelop.js/types/gdobjectmetadata.js index 87c39d90045a..b6c6e8e9873e 100644 --- a/GDevelop.js/types/gdobjectmetadata.js +++ b/GDevelop.js/types/gdobjectmetadata.js @@ -28,6 +28,8 @@ declare class gdObjectMetadata { isHidden(): boolean; markAsRenderedIn3D(): gdObjectMetadata; isRenderedIn3D(): boolean; + setOpenFullEditorLabel(label: string): gdObjectMetadata; + getOpenFullEditorLabel(): string; delete(): void; ptr: number; }; \ No newline at end of file diff --git a/newIDE/app/src/CompactPropertiesEditor/index.js b/newIDE/app/src/CompactPropertiesEditor/index.js index e130151f8266..bc65b6ff656d 100644 --- a/newIDE/app/src/CompactPropertiesEditor/index.js +++ b/newIDE/app/src/CompactPropertiesEditor/index.js @@ -151,6 +151,7 @@ export type ActionButton = {| getValue: Instance => string, nonFieldType: 'button', getIcon?: ({| fontSize: string |}) => React.Node, + showRightIcon?: boolean, onClick: (instance: Instance) => void, |}; @@ -687,26 +688,31 @@ const CompactPropertiesEditor = ({ }) === DIFFERENT_VALUES; } return ( - - - ) - } - disabled={disabled} - label={field.label} - onClick={() => { - if (!instances[0]) return; - field.onClick(instances[0]); - }} - /> - - + + ) + } + rightIcon={ + !field.showRightIcon ? null : field.getIcon ? ( + field.getIcon({ fontSize: 'small' }) + ) : ( + + ) + } + disabled={disabled} + label={field.label} + onClick={() => { + if (!instances[0]) return; + field.onClick(instances[0]); + }} + /> ); }, [instances] diff --git a/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/CompactObjectPropertiesSchema.js b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/CompactObjectPropertiesSchema.js new file mode 100644 index 000000000000..ab6c9fbe48e3 --- /dev/null +++ b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/CompactObjectPropertiesSchema.js @@ -0,0 +1,40 @@ +// @flow +import * as React from 'react'; +import { type Schema, type ActionButton } from '../../CompactPropertiesEditor'; +import ShareExternal from '../../UI/CustomSvgIcons/ShareExternal'; + +export const getSchemaWithOpenFullEditorButton = ({ + schema, + fullEditorLabel, + object, + onEditObject, +}: {| + schema: Schema, + fullEditorLabel: ?string, + object: gdObject, + onEditObject: (object: gdObject) => void, +|}): Schema => { + if (!fullEditorLabel) return schema; + + const actionButton: ActionButton = { + label: fullEditorLabel, + disabled: 'onValuesDifferent', + nonFieldType: 'button', + showRightIcon: true, + getIcon: style => , + getValue: ({ object }) => object.getName(), + onClick: ({ object }) => onEditObject(object), + }; + + let added = false; + schema.forEach(field => { + if (field.children && field.name === '') { + field.children.push(actionButton); + added = true; + } + }); + + if (!added) schema.push(actionButton); + + return schema; +}; diff --git a/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js index 9768ebcd988b..67b8e024a40a 100644 --- a/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js +++ b/newIDE/app/src/ObjectEditor/CompactObjectPropertiesEditor/index.js @@ -42,6 +42,7 @@ import { import CompactSelectField from '../../UI/CompactSelectField'; import SelectOption from '../../UI/SelectOption'; import { ChildObjectPropertiesEditor } from './ChildObjectPropertiesEditor'; +import { getSchemaWithOpenFullEditorButton } from './CompactObjectPropertiesSchema'; const gd: libGDevelop = global.gd; @@ -145,6 +146,9 @@ export const CompactObjectPropertiesEditor = ({ object.getType() ); const is3DObject = !!objectMetadata && objectMetadata.isRenderedIn3D(); + const fullEditorLabel = objectMetadata + ? objectMetadata.getOpenFullEditorLabel() + : null; // TODO: Workaround a bad design of ObjectJsImplementation. When getProperties // and associated methods are redefined in JS, they have different arguments ( @@ -159,6 +163,10 @@ export const CompactObjectPropertiesEditor = ({ // Properties: const schema = React.useMemo( () => { + if (schemaRecomputeTrigger) { + // schemaRecomputeTrigger allows to invalidate the schema when required. + } + const properties = objectConfigurationAsGd.getProperties(); const schema = propertiesMapToSchema( properties, @@ -168,11 +176,20 @@ export const CompactObjectPropertiesEditor = ({ objectConfiguration.updateProperty(name, value) ); - return schema; + return getSchemaWithOpenFullEditorButton({ + schema, + fullEditorLabel, + object, + onEditObject, + }); }, - // schemaRecomputeTrigger allows to invalidate the schema when required. - // eslint-disable-next-line react-hooks/exhaustive-deps - [objectConfigurationAsGd, schemaRecomputeTrigger] + [ + objectConfigurationAsGd, + schemaRecomputeTrigger, + fullEditorLabel, + object, + onEditObject, + ] ); // Behaviors: @@ -307,7 +324,6 @@ export const CompactObjectPropertiesEditor = ({ /> ); })} -