Compare commits

...

23 Commits

Author SHA1 Message Date
Florian Rival
c6c586459c Update the subscriptions plans to reflect the new ones and their updated benefits (#4290)
* Read [the blog post](https://gdevelop.io/blog/new-premium-subscriptions-online-services) for a full description of the updates and online services unlocked by the updated (and the existing) plans.
2022-09-14 11:33:37 +02:00
AlexandreS
a930011d8d Bump newIDE version 2022-09-14 10:24:54 +02:00
github-actions[bot]
51d723bd3d Update translations [skip ci] (#4284) 2022-09-14 10:21:23 +02:00
D8H
b63f968011 Fix resource refactoring for child-objects (#4289)
* Don't show in changelogs.
2022-09-14 09:41:07 +02:00
Florian Rival
3387c553d8 Fix previews not loading audio/font/json resources uploaded with the web-app (#4285)
Don't show in changelog
2022-09-13 16:41:33 +02:00
D8H
441cd20846 Fix a crash when renaming a child-object (#4281)
* Add tests for object renaming in events.
* Don't show in changelogs.
2022-09-13 12:46:20 +02:00
AlexandreS
f9871bd63d Add supported formats mimtypes to resource selector input accept attributes (#4283)
[skip-ci]
2022-09-13 11:51:22 +02:00
github-actions[bot]
bac11b3818 Update translations [skip ci] (#4230)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2022-09-13 10:37:01 +02:00
D8H
3e32cb8cea Fix the events-based objects editor that were never showing up in dev mode (#4279)
* Don't show in changelogs
2022-09-12 10:48:22 +02:00
Florian Rival
db74a59730 Add support for markdown in announcements (#4280)
Don't show in changelog
2022-09-12 10:28:32 +02:00
D8H
e7d09531b7 Fix event-based behavior properties default values that were ignored (#4276)
* Don't show in changelogs
2022-09-09 17:56:27 +02:00
Florian Rival
97cf19180b Rename Behaviors/Functions to Extensions in the project manager (#4272)
Don't show in changelog
2022-09-09 14:08:13 +02:00
Florian Rival
5cc999c0a3 Add announcements and news in the homepage community tab (#4273)
* Also display "urgent" announcements at the top of the home page (which can be dismissed).
2022-09-09 10:50:49 +02:00
D8H
5eb0aa9e14 Add an instance render for custom objects (#4251)
* Don't show in changelogs
2022-09-06 12:41:38 +02:00
D8H
7f528649d7 Revert the RendererInstance parameter for the tile map extension (#4260)
* Don't show in changelogs
2022-09-05 19:38:47 +02:00
D8H
c4f44daa8c Expose a gdObject constructor for the asset script (#4268)
* Don't show in changelogs.
2022-09-05 16:14:54 +02:00
AlexandreS
7d00e78628 Change the name of exports to be based on the name and version of the game 2022-09-05 12:12:39 +02:00
Aurélien Vivet
887ced270a Add the possibility to receive the GDevelop newsletter from the Profile and on Signup (#4256) 2022-09-05 10:32:30 +02:00
D8H
b8e9bc801a Add a properties editor for custom objects (#4227)
* Don't show in changelogs
2022-09-02 14:13:41 +02:00
D8H
7f023e1a58 Remove useless SetIncludeFile call. (#4255)
- Most of them has a wrong path anyway.
- Don't show in changelogs.
2022-09-01 14:30:24 +02:00
AlexandreS
6606ddb260 Improve SVG display in Resource Store for dark themes 2022-09-01 09:31:04 +02:00
Clément Pasteau
a682c1baa8 Update "Submit New Extension" link with new format (#4254) 2022-08-31 14:47:32 +02:00
Clément Pasteau
d581af20e1 Improve Signup and Edit profile to show when a username is not available 2022-08-31 13:59:14 +02:00
238 changed files with 7923 additions and 2778 deletions

View File

@@ -28,7 +28,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
extension.AddInstructionOrExpressionGroupMetadata(_("Movement using forces"))
.SetIcon("res/actions/force24.png");
gd::ObjectMetadata& obj = extension.AddObject<gd::Object>(
gd::ObjectMetadata& obj = extension.AddObject<gd::ObjectConfiguration>(
"", _("Base object"), _("Base object"), "res/objeticon24.png");
obj.AddCondition("PosX",

View File

@@ -25,8 +25,7 @@ namespace gd {
Animation SpriteObject::badAnimation;
SpriteObject::SpriteObject(gd::String name_)
: Object(name_), updateIfNotVisible(false) {}
SpriteObject::SpriteObject() : updateIfNotVisible(false) {}
SpriteObject::~SpriteObject(){};

View File

@@ -36,11 +36,11 @@ namespace gd {
* \see gd::BuiltinExtensionsImplementer::ImplementsSpriteExtension
* \ingroup SpriteObjectExtension
*/
class GD_CORE_API SpriteObject : public gd::Object {
class GD_CORE_API SpriteObject : public gd::ObjectConfiguration {
public:
SpriteObject(gd::String name_);
SpriteObject();
virtual ~SpriteObject();
std::unique_ptr<gd::Object> Clone() const override {
std::unique_ptr<gd::ObjectConfiguration> Clone() const override {
return gd::make_unique<SpriteObject>(*this);
}

View File

@@ -23,23 +23,20 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24,
std::shared_ptr<gd::Object> blueprintObject_)
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_)
: ObjectMetadata(extensionNamespace_,
name_,
fullname_,
description_,
icon24x24,
[blueprintObject_](gd::String name) -> std::unique_ptr<gd::Object> {
if (blueprintObject_ == std::shared_ptr<gd::Object>()) {
[blueprintObject_]() -> std::unique_ptr<gd::ObjectConfiguration> {
if (blueprintObject_ == std::shared_ptr<gd::ObjectConfiguration>()) {
gd::LogFatalError(
"Error: Unable to create object. Have you declared an extension "
"(or ObjectMetadata) without specifying an object as blueprint?");
return nullptr;
}
std::unique_ptr<gd::Object> newObject = blueprintObject_->Clone();
newObject->SetName(name);
return newObject;
return blueprintObject_->Clone();
}) {
blueprintObject = blueprintObject_;
}
@@ -54,7 +51,7 @@ ObjectMetadata::ObjectMetadata(const gd::String& extensionNamespace_,
fullname_,
description_,
icon24x24,
[](gd::String name) -> std::unique_ptr<gd::Object> {
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
gd::LogFatalError(
"Error: Event-based objects don't have blueprint. "
"This method should not never be called.");

View File

@@ -13,6 +13,7 @@
#include "GDCore/Extensions/Metadata/ExpressionMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/String.h"
namespace gd {
class InstructionMetadata;
@@ -20,7 +21,7 @@ class MultipleInstructionMetadata;
class ExpressionMetadata;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
namespace gd {
@@ -42,7 +43,7 @@ class GD_CORE_API ObjectMetadata {
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon24x24_,
std::shared_ptr<gd::Object> blueprintObject_);
std::shared_ptr<gd::ObjectConfiguration> blueprintObject_);
/**
* \brief Construct an object metadata, without "blueprint" object
*
@@ -314,7 +315,7 @@ class GD_CORE_API ObjectMetadata {
gd::String categoryFullName;
std::set<gd::String> unsupportedBaseObjectCapabilities;
std::shared_ptr<gd::Object>
std::shared_ptr<gd::ObjectConfiguration>
blueprintObject; ///< The "blueprint" object to be copied when a new
///< object is asked. Can be null in case a creation
///< function is passed or for events based objects

View File

@@ -7,6 +7,7 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/String.h"
#include "GDCore/Tools/Log.h"
@@ -92,8 +93,8 @@ std::shared_ptr<gd::PlatformExtension> Platform::GetExtension(
return std::shared_ptr<gd::PlatformExtension>();
}
std::unique_ptr<gd::Object> Platform::CreateObject(
gd::String type, const gd::String& name) const {
std::unique_ptr<gd::ObjectConfiguration> Platform::CreateObjectConfiguration(
gd::String type) const {
if (creationFunctionTable.find(type) == creationFunctionTable.end()) {
gd::LogWarning("Tried to create an object with an unknown type: " + type
+ " for platform " + GetName() + "!");
@@ -105,11 +106,9 @@ std::unique_ptr<gd::Object> Platform::CreateObject(
}
// Create a new object with the type we want.
std::unique_ptr<gd::Object> object =
(creationFunctionTable.find(type)->second)(name);
object->SetType(type);
return std::unique_ptr<gd::Object>(std::move(object));
auto objectConfiguration = (creationFunctionTable.find(type)->second)();
objectConfiguration->SetType(type);
return objectConfiguration;
}
#if defined(GD_IDE_ONLY)

View File

@@ -16,6 +16,7 @@ namespace gd {
class InstructionsMetadataHolder;
class Project;
class Object;
class ObjectConfiguration;
class Behavior;
class BehaviorMetadata;
class ObjectMetadata;
@@ -26,7 +27,7 @@ class LayoutEditorCanvas;
class ProjectExporter;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
#undef CreateEvent
@@ -146,8 +147,8 @@ class GD_CORE_API Platform {
/**
* \brief Create an object of given type with the specified name.
*/
std::unique_ptr<gd::Object> CreateObject(gd::String type,
const gd::String& name) const;
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
gd::String type) const;
/**
* \brief Create an event of given type

View File

@@ -18,6 +18,7 @@
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/BehaviorsSharedData.h"
#include "GDCore/Tools/Localization.h"
@@ -231,7 +232,7 @@ gd::ObjectMetadata& PlatformExtension::AddObject(
const gd::String& fullname,
const gd::String& description,
const gd::String& icon24x24,
std::shared_ptr<gd::Object> instance) {
std::shared_ptr<gd::ObjectConfiguration> instance) {
gd::String nameWithNamespace = GetNameSpace() + name;
objectsInfos[nameWithNamespace] = ObjectMetadata(GetNameSpace(),
nameWithNamespace,

View File

@@ -37,9 +37,10 @@ class ArbitraryResourceWorker;
class BehaviorsSharedData;
class Behavior;
class Object;
class ObjectConfiguration;
} // namespace gd
typedef std::function<std::unique_ptr<gd::Object>(gd::String name)>
typedef std::function<std::unique_ptr<gd::ObjectConfiguration>()>
CreateFunPtr;
namespace gd {
@@ -242,7 +243,7 @@ class GD_CORE_API PlatformExtension {
const gd::String& fullname_,
const gd::String& description_,
const gd::String& icon_,
std::shared_ptr<gd::Object> instance);
std::shared_ptr<gd::ObjectConfiguration> instance);
/**
* \brief Declare a new events based object as being part of the extension.

View File

@@ -25,8 +25,8 @@ gd::ObjectMetadata& PlatformExtension::AddObject(const gd::String& name,
fullname,
description,
icon24x24,
[](gd::String name) -> std::unique_ptr<gd::Object> {
return gd::make_unique<T>(name);
[]() -> std::unique_ptr<gd::ObjectConfiguration> {
return gd::make_unique<T>();
})
.SetHelpPath(GetHelpPath());

View File

@@ -1628,19 +1628,18 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInEventsFunction(
void WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
gd::Project& project,
gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::EventsBasedObject& eventsBasedObject,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup) {
for (auto &functionUniquePtr : eventsBasedObject.GetEventsFunctions().GetInternalVector()) {
auto function = functionUniquePtr.get();
auto *function = functionUniquePtr.get();
WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project,
*function,
globalObjectsContainer,
objectsContainer,
eventsBasedObject,
oldName,
newName,
isObjectGroup);

View File

@@ -333,9 +333,8 @@ class GD_CORE_API WholeProjectRefactorer {
*/
static void ObjectOrGroupRenamedInEventsBasedObject(
gd::Project& project,
gd::EventsBasedObject& eventsBasedObject,
gd::ObjectsContainer& globalObjectsContainer,
gd::ObjectsContainer& objectsContainer,
gd::EventsBasedObject& eventsBasedObject,
const gd::String& oldName,
const gd::String& newName,
bool isObjectGroup);

View File

@@ -1,3 +1,8 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomBehavior.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
@@ -99,7 +104,7 @@ void CustomBehavior::InitializeContent(gd::SerializerElement &behaviorContent) {
const auto &eventsBasedBehavior = project.GetEventsBasedBehavior(GetTypeName());
const auto &properties = eventsBasedBehavior.GetPropertyDescriptors();
for (auto &&property : properties.GetInternalVector()) {
auto element = behaviorContent.AddChild(property->GetName());
auto &element = behaviorContent.AddChild(property->GetName());
auto propertyType = property->GetType();
if (propertyType == "String" || propertyType == "Choice" ||

View File

@@ -1,3 +1,11 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMBEHAVIOR_H
#define GDCORE_CUSTOMBEHAVIOR_H
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsBasedBehavior.h"
#include "GDCore/Project/Project.h"
@@ -7,6 +15,7 @@
using namespace gd;
namespace gd {
/**
* \brief A gd::Behavior that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
@@ -37,3 +46,6 @@ private:
const Project &project; ///< The project is used to get the
///< EventBasedBehavior from the fullType.
};
} // namespace gd
#endif // GDCORE_CUSTOMBEHAVIOR_H

View File

@@ -1,49 +0,0 @@
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
/**
* \brief A gd::Object that stores its content in JSON and forward the
* properties related functions to Javascript with Emscripten.
*
* It also implements "ExposeResources" to expose the properties of type
* "resource".
*/
class CustomObject : public gd::Object {
public:
CustomObject(const gd::String &name, const Project& project_, const gd::String &fullType)
: Object(name),
project(project_) {
SetType(fullType);
}
std::unique_ptr<gd::Object> Clone() const override;
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) override;
bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
private:
const Project& project; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
};

View File

@@ -1,4 +1,9 @@
#include "CustomObject.h"
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "CustomObjectConfiguration.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/Object.h"
@@ -6,27 +11,62 @@
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include <map>
#include "GDCore/Tools/Log.h"
using namespace gd;
std::unique_ptr<gd::Object> CustomObject::Clone() const {
CustomObject* clone = new CustomObject(*this);
return std::unique_ptr<gd::Object>(clone);
void CustomObjectConfiguration::Init(const gd::CustomObjectConfiguration& objectConfiguration) {
project = objectConfiguration.project;
objectContent = objectConfiguration.objectContent;
// There is no default copy for a map of unique_ptr like childObjectConfigurations.
childObjectConfigurations.clear();
for (auto& it : objectConfiguration.childObjectConfigurations) {
childObjectConfigurations[it.first] =
gd::make_unique<gd::ObjectConfiguration>(*it.second);
}
}
// TODO EBO Extract a class from Object for the object configuration.
// This will allow CustomObject to have a ObjectConfiguration composed of
// ObjectConfiguration for their children in addition to its own properties.
// This will be used by the GUI to display custom editors (for sprites for
// instance)
std::map<gd::String, gd::PropertyDescriptor> CustomObject::GetProperties() const {
gd::ObjectConfiguration CustomObjectConfiguration::badObjectConfiguration;
std::unique_ptr<gd::ObjectConfiguration> CustomObjectConfiguration::Clone() const {
CustomObjectConfiguration* clone = new CustomObjectConfiguration(*this);
return std::unique_ptr<gd::ObjectConfiguration>(clone);
}
gd::ObjectConfiguration &CustomObjectConfiguration::GetChildObjectConfiguration(const gd::String &objectName) {
if (!project->HasEventsBasedObject(GetType())) {
return badObjectConfiguration;
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
if (!eventsBasedObject.HasObjectNamed(objectName)) {
gd::LogError("Tried to get the configuration of a child-object:" + objectName
+ " that doesn't exist in the event-based object: " + GetType());
return badObjectConfiguration;
}
auto &childObject = eventsBasedObject.GetObject(objectName);
auto configurationPosition = childObjectConfigurations.find(objectName);
if (configurationPosition == childObjectConfigurations.end()) {
childObjectConfigurations.insert(std::make_pair(
objectName,
project->CreateObjectConfiguration(childObject.GetType())));
return *(childObjectConfigurations[objectName]);
}
else {
auto &pair = *configurationPosition;
auto &configuration = pair.second;
return *configuration;
}
}
std::map<gd::String, gd::PropertyDescriptor> CustomObjectConfiguration::GetProperties() const {
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project.HasEventsBasedObject(GetType())) {
if (!project->HasEventsBasedObject(GetType())) {
return objectProperties;
}
const auto &eventsBasedObject = project.GetEventsBasedObject(GetType());
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
for (auto &property : properties.GetInternalVector()) {
@@ -75,12 +115,12 @@ std::map<gd::String, gd::PropertyDescriptor> CustomObject::GetProperties() const
return objectProperties;
}
bool CustomObject::UpdateProperty(const gd::String& propertyName,
bool CustomObjectConfiguration::UpdateProperty(const gd::String& propertyName,
const gd::String& newValue) {
if (!project.HasEventsBasedObject(GetType())) {
if (!project->HasEventsBasedObject(GetType())) {
return false;
}
const auto &eventsBasedObject = project.GetEventsBasedObject(GetType());
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
const auto &properties = eventsBasedObject.GetPropertyDescriptors();
if (!properties.Has(propertyName)) {
return false;
@@ -106,14 +146,14 @@ bool CustomObject::UpdateProperty(const gd::String& propertyName,
}
std::map<gd::String, gd::PropertyDescriptor>
CustomObject::GetInitialInstanceProperties(
CustomObjectConfiguration::GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) {
return std::map<gd::String, gd::PropertyDescriptor>();
}
bool CustomObject::UpdateInitialInstanceProperty(
bool CustomObjectConfiguration::UpdateInitialInstanceProperty(
gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
@@ -122,15 +162,29 @@ bool CustomObject::UpdateInitialInstanceProperty(
return false;
}
void CustomObject::DoSerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = objectContent;
void CustomObjectConfiguration::DoSerializeTo(SerializerElement& element) const {
element.AddChild("content") = objectContent;
auto &childrenContentElement = element.AddChild("childrenContent");
for (auto &pair : childObjectConfigurations) {
auto &childName = pair.first;
auto &childConfiguration = pair.second;
auto &childElement = childrenContentElement.AddChild(childName);
childConfiguration->SerializeTo(childElement);
}
}
void CustomObject::DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) {
objectContent = arg1.GetChild("content");
void CustomObjectConfiguration::DoUnserializeFrom(Project& project,
const SerializerElement& element) {
objectContent = element.GetChild("content");
auto &childrenContentElement = element.GetChild("childrenContent");
for (auto &pair : childrenContentElement.GetAllChildren()) {
auto &childName = pair.first;
auto &childElement = pair.second;
auto &childConfiguration = GetChildObjectConfiguration(childName);
childConfiguration.UnserializeFrom(project, *childElement);
}
}
void CustomObject::ExposeResources(
void CustomObjectConfiguration::ExposeResources(
gd::ArbitraryResourceWorker& worker) {
std::map<gd::String, gd::PropertyDescriptor> properties = GetProperties();
@@ -162,4 +216,15 @@ void CustomObject::ExposeResources(
}
}
}
auto objectProperties = std::map<gd::String, gd::PropertyDescriptor>();
if (!project->HasEventsBasedObject(GetType())) {
return;
}
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
for (auto& childObject : eventsBasedObject.GetObjects()) {
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
configuration.ExposeResources(worker);
}
}

View File

@@ -0,0 +1,100 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_CUSTOMOBJECTCONFIGURATION_H
#define GDCORE_CUSTOMOBJECTCONFIGURATION_H
#include "GDCore/Project/ObjectConfiguration.h"
#include <map>
#include <memory>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/EventsBasedObject.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
using namespace gd;
namespace gd {
/**
* \brief A gd::ObjectConfiguration that stores its content in JSON and is
* composed of other configuration according to it's object children.
*
* It also implements "ExposeResources" to expose the properties of type
* "resource".
*/
class CustomObjectConfiguration : public gd::ObjectConfiguration {
public:
CustomObjectConfiguration(const Project& project_, const String& type_)
: project(&project_) {
SetType(type_);
}
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
/**
* Copy constructor. Calls Init().
*/
CustomObjectConfiguration(const gd::CustomObjectConfiguration& object)
: ObjectConfiguration(object) {
Init(object);
};
/**
* Assignment operator. Calls Init().
*/
CustomObjectConfiguration& operator=(const gd::CustomObjectConfiguration& object){
if ((this) != &object) {
ObjectConfiguration::operator=(object);
Init(object);
}
return *this;
}
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;
std::map<gd::String, gd::PropertyDescriptor> GetInitialInstanceProperties(
const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& scene) override;
bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& scene) override;
void ExposeResources(gd::ArbitraryResourceWorker& worker) override;
gd::ObjectConfiguration &GetChildObjectConfiguration(const gd::String& objectName);
protected:
void DoSerializeTo(SerializerElement& element) const override;
void DoUnserializeFrom(Project& project, const SerializerElement& element) override;
private:
const Project* project; ///< The project is used to get the
///< EventBasedObject from the fullType.
gd::SerializerElement objectContent;
std::map<gd::String, std::unique_ptr<gd::ObjectConfiguration>> childObjectConfigurations;
static gd::ObjectConfiguration badObjectConfiguration;
/**
* Initialize configuration using another configuration. Used by copy-ctor
* and assign-op.
*
* Don't forget to update me if members were changed!
*
* It's needed because there is no default copy for childObjectConfigurations
* and it must be a deep copy.
*/
void Init(const gd::CustomObjectConfiguration& object);
};
} // namespace gd
#endif // GDCORE_CUSTOMOBJECTCONFIGURATION_H

View File

@@ -16,7 +16,7 @@ class Project;
} // namespace gd
namespace gd {
// TODO EBO Add a way to mark some parts of children configuration as readonly.
/**
* \brief Represents an object that is implemented with events.
*

View File

@@ -117,10 +117,10 @@ std::map<gd::String, gd::PropertyDescriptor>
InitialInstance::GetCustomProperties(gd::Project& project, gd::Layout& layout) {
// Find an object
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName())
return layout.GetObject(GetObjectName()).GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName())
return project.GetObject(GetObjectName()).GetConfiguration()
.GetInitialInstanceProperties(*this, project, layout);
std::map<gd::String, gd::PropertyDescriptor> nothing;
@@ -132,10 +132,10 @@ bool InitialInstance::UpdateCustomProperty(const gd::String& name,
gd::Project& project,
gd::Layout& layout) {
if (layout.HasObjectNamed(GetObjectName()))
return layout.GetObject(GetObjectName())
return layout.GetObject(GetObjectName()).GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
else if (project.HasObjectNamed(GetObjectName()))
return project.GetObject(GetObjectName())
return project.GetObject(GetObjectName()).GetConfiguration()
.UpdateInitialInstanceProperty(*this, name, value, project, layout);
return false;

View File

@@ -20,12 +20,23 @@ namespace gd {
Object::~Object() {}
Object::Object(const gd::String& name_) : name(name_) {}
Object::Object(const gd::String& name_,
const gd::String& type_,
std::unique_ptr<gd::ObjectConfiguration> configuration_)
: name(name_), configuration(std::move(configuration_)) {
SetType(type_);
}
Object::Object(const gd::String& name_,
const gd::String& type_,
gd::ObjectConfiguration* configuration_)
: name(name_), configuration(configuration_) {
SetType(type_);
}
void Object::Init(const gd::Object& object) {
name = object.name;
assetStoreId = object.assetStoreId;
type = object.type;
objectVariables = object.objectVariables;
tags = object.tags;
effectsContainer = object.effectsContainer;
@@ -34,6 +45,16 @@ void Object::Init(const gd::Object& object) {
for (auto& it : object.behaviors) {
behaviors[it.first] = gd::make_unique<gd::Behavior>(*it.second);
}
configuration = object.configuration->Clone();
}
gd::ObjectConfiguration& Object::GetConfiguration() {
return *configuration;
}
const gd::ObjectConfiguration& Object::GetConfiguration() const {
return *configuration;
}
std::vector<gd::String> Object::GetAllBehaviorNames() const {
@@ -72,11 +93,6 @@ bool Object::HasBehaviorNamed(const gd::String& name) const {
return behaviors.find(name) != behaviors.end();
}
std::map<gd::String, gd::PropertyDescriptor> Object::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
const gd::String& type,
const gd::String& name) {
@@ -109,17 +125,9 @@ gd::Behavior* Object::AddNewBehavior(const gd::Project& project,
}
}
std::map<gd::String, gd::PropertyDescriptor>
Object::GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout) {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
void Object::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
type = element.GetStringAttribute("type");
SetType(element.GetStringAttribute("type"));
assetStoreId = element.GetStringAttribute("assetStoreId");
name = element.GetStringAttribute("name", name, "nom");
tags = element.GetStringAttribute("tags");
@@ -186,7 +194,7 @@ void Object::UnserializeFrom(gd::Project& project,
}
}
DoUnserializeFrom(project, element);
configuration->UnserializeFrom(project, element);
}
void Object::SerializeTo(SerializerElement& element) const {
@@ -212,7 +220,7 @@ void Object::SerializeTo(SerializerElement& element) const {
behaviorElement.SetAttribute("name", behavior.GetName());
}
DoSerializeTo(element);
configuration->SerializeTo(element);
}
} // namespace gd

View File

@@ -11,10 +11,12 @@
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class PropertyDescriptor;
class Project;
@@ -28,7 +30,7 @@ class EffectsContainer;
namespace gd {
/**
* \brief Base class used to represent an object of a platform
* \brief Represent an object of a platform
*
* \ingroup PlatformDefinition
*/
@@ -36,9 +38,19 @@ class GD_CORE_API Object {
public:
/**
* Create a new object with the name passed as argument.
* \param name Object's name
*/
Object(const gd::String& name);
Object(const gd::String& name,
const gd::String& type,
std::unique_ptr<gd::ObjectConfiguration> configuration);
/**
* Create a new object with the name passed as argument.
*
* Object takes the ownership of the configuration.
*/
Object(const gd::String& name,
const gd::String& type,
gd::ObjectConfiguration* configuration);
/**
* Copy constructor. Calls Init().
@@ -70,6 +82,13 @@ class GD_CORE_API Object {
return gd::make_unique<gd::Object>(*this);
}
/**
* \brief Return the object configuration.
*/
gd::ObjectConfiguration& GetConfiguration();
const gd::ObjectConfiguration& GetConfiguration() const;
/** \name Common properties
* Members functions related to common properties
*/
@@ -93,11 +112,15 @@ class GD_CORE_API Object {
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
void SetType(const gd::String& type_) {
configuration->SetType(type_);
}
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
const gd::String& GetType() const {
return configuration->GetType();
}
/** \brief Change the tags of the object.
*/
@@ -108,92 +131,6 @@ class GD_CORE_API Object {
const gd::String& GetTags() const { return tags; }
///@}
/** \name Resources management
* Members functions related to managing resources used by the object
*/
///@{
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources and sometimes update their filename. Implementation example:
* \code
* worker.ExposeImage(myImage);
* worker.ExposeFile(myResourceFile);
* \endcode
*
* \see ArbitraryResourceWorker
*/
virtual void ExposeResources(gd::ArbitraryResourceWorker& worker) { return; };
/**
* Redefine this function to return true if your object can use shaders.
*/
virtual bool SupportShaders() { return false; }
///@}
/** \name Object properties
* Reading and updating object properties
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of the
object.
*
* Usage example:
\code
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[ToString(_("Text"))].SetValue("Hello world!");
return properties;
\endcode
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the object
*
* \return false if the new value cannot be set
*/
virtual bool UpdateProperty(const gd::String& name, const gd::String& value) {
return false;
};
///@}
/** \name Drawing and editing initial instances
* Members functions related to drawing and editing initial instances of this
* object
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of an
* initial instance of this object.
*
* \return a std::map with properties names as key and values.
* \see gd::InitialInstance
*/
virtual std::map<gd::String, gd::PropertyDescriptor>
GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout);
/**
* \brief Called when the IDE wants to update a custom property of an initial
* instance of this object.
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& layout) {
return false;
};
///@}
/** \name Behaviors management
* Members functions related to behaviors management.
*/
@@ -309,8 +246,7 @@ class GD_CORE_API Object {
protected:
gd::String name; ///< The full name of the object
gd::String assetStoreId; ///< The ID of the asset if the object comes from the store.
gd::String type; ///< Which type is the object. ( To test if we can do
///< something reserved to some objects with it )
std::unique_ptr<gd::ObjectConfiguration> configuration;
std::map<gd::String, std::unique_ptr<gd::Behavior>>
behaviors; ///< Contains all behaviors and their properties for the
///< object. Behavior contents are the ownership of the
@@ -321,20 +257,12 @@ class GD_CORE_API Object {
gd::EffectsContainer
effectsContainer; ///< The effects container for the object.
/**
* \brief Derived objects can redefine this method to load custom attributes.
*/
virtual void DoUnserializeFrom(gd::Project& project,
const SerializerElement& element){};
/**
* \brief Derived objects can redefine this method to save custom attributes.
*/
virtual void DoSerializeTo(SerializerElement& element) const {};
/**
* Initialize object using another object. Used by copy-ctor and assign-op.
* Don't forget to update me if members were changed!
*
* It's needed because there is no default copy for a map of unique_ptr like
* behaviors and it must be a deep copy.
*/
void Init(const gd::Object& object);
};

View File

@@ -0,0 +1,47 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/CustomBehavior.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Project/PropertyDescriptor.h"
#include "GDCore/Tools/Log.h"
namespace gd {
ObjectConfiguration::~ObjectConfiguration() {}
ObjectConfiguration::ObjectConfiguration() {}
std::map<gd::String, gd::PropertyDescriptor> ObjectConfiguration::GetProperties() const {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
std::map<gd::String, gd::PropertyDescriptor>
ObjectConfiguration::GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout) {
std::map<gd::String, gd::PropertyDescriptor> nothing;
return nothing;
}
void ObjectConfiguration::UnserializeFrom(gd::Project& project,
const SerializerElement& element) {
DoUnserializeFrom(project, element);
}
void ObjectConfiguration::SerializeTo(SerializerElement& element) const {
DoSerializeTo(element);
}
} // namespace gd

View File

@@ -0,0 +1,194 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef GDCORE_OBJECTCONFIGURATION_H
#define GDCORE_OBJECTCONFIGURATION_H
#include "GDCore/Vector2.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EffectsContainer.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"
#include "GDCore/Tools/MakeUnique.h"
namespace gd {
class PropertyDescriptor;
class Project;
class Layout;
class ArbitraryResourceWorker;
class InitialInstance;
class SerializerElement;
class EffectsContainer;
} // namespace gd
namespace gd {
/**
* \brief Base class used to represent an object configuration.
* For example, this can be the animations in a sprite, the text, its font,
* its color in a Text object, etc...
*
* \ingroup PlatformDefinition
*/
class GD_CORE_API ObjectConfiguration {
public:
/**
* Create a new object configuration.
*/
ObjectConfiguration();
/**
* Destructor.
*/
virtual ~ObjectConfiguration();
/**
* Must return a pointer to a copy of the configuration. This method is
* needed to do polymorphic copies. Just redefine this method in your derived
* object class like this:
* \code
* return gd::make_unique<MyObjectConfiguration>(*this);
* \endcode
*/
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<gd::ObjectConfiguration>(*this);
}
/** \brief Change the type of the object.
*/
void SetType(const gd::String& type_) { type = type_; }
/** \brief Return the type of the object.
*/
const gd::String& GetType() const { return type; }
/** \name Object properties
* Reading and updating object configuration properties
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of the
object configuration.
*
* Usage example:
\code
std::map<gd::String, gd::PropertyDescriptor> properties;
properties[ToString(_("Text"))].SetValue("Hello world!");
return properties;
\endcode
*
* \return a std::map with properties names as key.
* \see gd::PropertyDescriptor
*/
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties() const;
/**
* \brief Called when the IDE wants to update a custom property of the object
* configuration.
*
* \return false if the new value cannot be set
*/
virtual bool UpdateProperty(const gd::String& name, const gd::String& value) {
return false;
};
///@}
/** \name Drawing and editing initial instances
* Members functions related to drawing and editing initial instances of this
* object configuration
*/
///@{
/**
* \brief Called when the IDE wants to know about the custom properties of an
* initial instance of this object configuration.
*
* \return a std::map with properties names as key and values.
* \see gd::InitialInstance
*/
virtual std::map<gd::String, gd::PropertyDescriptor>
GetInitialInstanceProperties(const gd::InitialInstance& instance,
gd::Project& project,
gd::Layout& layout);
/**
* \brief Called when the IDE wants to update a custom property of an initial
* instance of this object configuration.
*
* \return false if the new value cannot be set
* \see gd::InitialInstance
*/
virtual bool UpdateInitialInstanceProperty(gd::InitialInstance& instance,
const gd::String& name,
const gd::String& value,
gd::Project& project,
gd::Layout& layout) {
return false;
};
///@}
/** \name Resources management
* Members functions related to managing resources used by the object configuration
*/
///@{
/**
* \brief Called ( e.g. during compilation ) so as to inventory internal
* resources and sometimes update their filename. Implementation example:
* \code
* worker.ExposeImage(myImage);
* worker.ExposeFile(myResourceFile);
* \endcode
*
* \see ArbitraryResourceWorker
*/
virtual void ExposeResources(gd::ArbitraryResourceWorker& worker) { return; };
///@}
/** \name Serialization
* Members functions related to serialization of the object configuration
*/
///@{
/**
* \brief Serialize the object configuration.
* \see DoSerializeTo
*/
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the object configuration.
* \see DoUnserializeFrom
*/
void UnserializeFrom(gd::Project& project, const SerializerElement& element);
///@}
protected:
gd::String type; ///< Which type of object is represented by this
///< configuration.
/**
* \brief Derived object configuration can redefine this method to load
* custom attributes.
*/
virtual void DoUnserializeFrom(gd::Project& project,
const SerializerElement& element){};
/**
* \brief Derived object configuration can redefine this method to save
* custom attributes.
*/
virtual void DoSerializeTo(SerializerElement& element) const {};
};
} // namespace gd
/**
* Object configurations are usually managed thanks to (smart) pointers.
*/
using ObjConfSPtr = std::unique_ptr<gd::ObjectConfiguration>;
#endif // GDCORE_OBJECT_H

View File

@@ -23,11 +23,12 @@
#include "GDCore/IDE/PlatformManager.h"
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/CustomObject.h"
#include "GDCore/Project/CustomObjectConfiguration.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Project/ObjectGroupsContainer.h"
#include "GDCore/Project/ResourcesManager.h"
#include "GDCore/Project/SourceFile.h"
@@ -82,12 +83,17 @@ void Project::ResetProjectUuid() { projectUuid = UUID::MakeUuid4(); }
std::unique_ptr<gd::Object> Project::CreateObject(
const gd::String& type,
const gd::String& name) const {
return gd::make_unique<Object>(name, type, CreateObjectConfiguration(type));
}
std::unique_ptr<gd::ObjectConfiguration> Project::CreateObjectConfiguration(
const gd::String& type) const {
if (Project::HasEventsBasedObject(type)) {
return gd::make_unique<CustomObject>(name, *this, type);
return gd::make_unique<CustomObjectConfiguration>(*this, type);
}
else {
// Create a base object if the type can't be found in the platform.
return currentPlatform->CreateObject(type, name);
return currentPlatform->CreateObjectConfiguration(type);
}
}
@@ -940,8 +946,9 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// Add layouts resources
for (std::size_t s = 0; s < GetLayoutsCount(); s++) {
for (std::size_t j = 0; j < GetLayout(s).GetObjectsCount();
++j) // Add objects resources
GetLayout(s).GetObject(j).ExposeResources(worker);
++j) { // Add objects resources
GetLayout(s).GetObject(j).GetConfiguration().ExposeResources(worker);
}
LaunchResourceWorkerOnEvents(*this, GetLayout(s).GetEvents(), worker);
}
@@ -960,7 +967,7 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker& worker) {
// Add global objects resources
for (std::size_t j = 0; j < GetObjectsCount(); ++j) {
GetObject(j).ExposeResources(worker);
GetObject(j).GetConfiguration().ExposeResources(worker);
}
// Add loading screen background image if present

View File

@@ -27,6 +27,7 @@ class EventsFunctionsExtension;
class EventsBasedObject;
class EventsBasedBehavior;
class Object;
class ObjectConfiguration;
class VariablesContainer;
class ArbitraryResourceWorker;
class SourceFile;
@@ -464,6 +465,14 @@ class GD_CORE_API Project : public ObjectsContainer {
std::unique_ptr<gd::Object> CreateObject(const gd::String& type,
const gd::String& name) const;
/**
* Create an object configuration of the given type.
*
* \param type The type of the object
*/
std::unique_ptr<gd::ObjectConfiguration> CreateObjectConfiguration(
const gd::String& type) const;
/**
* Create an event of the given type.
*

View File

@@ -13,12 +13,14 @@
#include "GDCore/IDE/Project/ProjectResourcesAdder.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "DummyPlatform.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "catch.hpp"
class ArbitraryResourceWorkerTest : public gd::ArbitraryResourceWorker {
@@ -63,14 +65,15 @@ TEST_CASE("ArbitraryResourceWorker", "[common][resources]") {
"path/to/file4.png") != worker.files.end());
SECTION("Object using a resource") {
gd::SpriteObject obj("myObject");
gd::SpriteObject spriteConfiguration;
gd::Animation anim;
gd::Sprite sprite;
sprite.SetImageName("res1");
anim.SetDirectionsCount(1);
anim.GetDirection(0).AddSprite(sprite);
obj.AddAnimation(anim);
spriteConfiguration.AddAnimation(anim);
gd::Object obj("myObject", "", spriteConfiguration.Clone());
project.InsertObject(obj, 0);
worker.files.clear();

View File

@@ -120,12 +120,14 @@ void CheckBehaviorProperty(ObjectsContainer &container) {
};
} // namespace
TEST_CASE("ProjectSerialization", "[common]") {
// TODO EBO Add similar test cases for events-based objects.
TEST_CASE("BehaviorSerialization", "[common]") {
SECTION("Save and load a project with a custom behavior property value") {
gd::Platform platform;
gd::Project writtenProject;
SetupProject(writtenProject, platform);
CheckBehaviorProperty(writtenProject.GetLayout("Scene"));
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);

View File

@@ -7,6 +7,8 @@
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/ObjectConfiguration.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Tools/Localization.h"
@@ -98,7 +100,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
// Create the base object. All objects "inherits" from it.
baseObjectExtension->SetExtensionInformation(
"BuiltinObject", "Base Object dummy extension", "", "", "");
auto& baseObject = baseObjectExtension->AddObject<gd::Object>(
auto& baseObject = baseObjectExtension->AddObject<gd::ObjectConfiguration>(
"", "Dummy Base Object", "Dummy Base Object", "");
// Add this expression for all objects. But it requires a "capability".
@@ -130,6 +132,18 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("expression", "Parameter 1 (a number)")
.SetFunctionName("doSomething");
extension
->AddAction("DoSomethingWithObjects",
"Do something",
"This does something",
"Do something please",
"",
"",
"")
.AddParameter("object", _("Object 1 parameter"))
.AddParameter("object", _("Object 2 parameter"))
.SetFunctionName("doSomethingWithObjects");
extension
->AddAction("DoSomethingWithResources",
"Do something with resources",
@@ -214,7 +228,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("objectvar", _("Variable for object 2"))
.SetFunctionName("getStringWith1ObjectParamAnd2ObjectVarParam");
auto& object = extension->AddObject<gd::Object>(
auto& object = extension->AddObject<gd::SpriteObject>(
"Sprite", "Dummy Sprite", "Dummy sprite object", "");
object
.AddExpression("GetObjectVariableAsNumber",
@@ -351,7 +365,7 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
{
auto& object = extension
->AddObject<gd::Object>(
->AddObject<gd::ObjectConfiguration>(
"FakeObjectWithUnsupportedCapability",
"FakeObjectWithUnsupportedCapability",
"This is FakeObjectWithUnsupportedCapability",

View File

@@ -0,0 +1,76 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering events-based extensions
*/
#include "GDCore/IDE/WholeProjectRefactorer.h"
#include <algorithm>
#include <stdexcept>
#include "DummyPlatform.h"
#include "GDCore/Events/Builtin/LinkEvent.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Extensions/PlatformExtension.h"
#include "GDCore/IDE/UnfilledRequiredBehaviorPropertyProblem.h"
#include "GDCore/Project/Behavior.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/ExternalEvents.h"
#include "GDCore/Project/ExternalLayout.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "catch.hpp"
namespace {
gd::EventsFunctionsExtension &
SetupProjectWithEventsFunctionExtension(gd::Project &project) {
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
// Add an events-based behavior
{
auto &eventsBasedBehavior =
eventsExtension.GetEventsBasedBehaviors().InsertNew(
"MyEventsBasedBehavior", 0);
eventsBasedBehavior.SetFullName("My events based behavior");
eventsBasedBehavior.SetDescription("An events based behavior for test");
// Add a property
eventsBasedBehavior.GetPropertyDescriptors()
.InsertNew("MyProperty", 0)
.SetValue("123")
.SetType("Number");
}
return eventsExtension;
}
} // namespace
TEST_CASE("Events-based extension", "[common]") {
SECTION("Behavior property default value") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout1 = project.InsertNewLayout("Layout1", 0);
auto &object = layout1.InsertNewObject(project, "MyExtension::Sprite", "Object1", 0);
// Attach a behavior to an object.
auto *behavior = object.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyEventsBasedBehavior");
behavior->InitializeContent();
// The behavior has the default value.
REQUIRE(behavior->GetProperties().size() == 1);
REQUIRE(behavior->GetProperties().at("MyProperty").GetValue() == "123");
}
}

View File

@@ -0,0 +1,111 @@
/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
/**
* @file Tests covering serialization to JSON.
*/
#include "DummyPlatform.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Builtin/StandardEvent.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Serialization.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/EventsFunctionsExtension.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Tools/SystemStats.h"
#include "GDCore/Tools/VersionWrapper.h"
#include "catch.hpp"
using namespace gd;
namespace {
void SetupProject(gd::Project &project, gd::Platform &platform) {
SetupProjectWithDummyPlatform(project, platform);
gd::Layout &layout = project.InsertNewLayout("Scene", 0);
gd::Object &object =
layout.InsertNewObject(project, "MyExtension::Sprite", "MyObject", 0);
auto &configuration = object.GetConfiguration();
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration != nullptr);
gd::Animation animation;
animation.SetName("Idle");
spriteConfiguration->AddAnimation(animation);
};
void CheckSpriteConfiguration(
SerializerElement &objectContainerElement) {
};
void CheckSpriteConfigurationInElement(SerializerElement &projectElement) {
auto &layoutsElement = projectElement.GetChild("layouts");
layoutsElement.ConsiderAsArrayOf("layout");
REQUIRE(layoutsElement.GetChildrenCount() == 1);
auto &layoutElement = layoutsElement.GetChild(0);
REQUIRE(layoutElement.GetStringAttribute("name") == "Scene");
REQUIRE(layoutElement.HasChild("objects"));
auto &objectsElement = layoutElement.GetChild("objects");
objectsElement.ConsiderAsArrayOf("object");
REQUIRE(objectsElement.GetChildrenCount() == 1);
auto &objectElement = objectsElement.GetChild(0);
REQUIRE(objectElement.GetStringAttribute("name") == "MyObject");
REQUIRE(objectElement.GetStringAttribute("type") == "MyExtension::Sprite");
REQUIRE(objectElement.HasChild("animations"));
auto &animationsElement = objectElement.GetChild("animations");
animationsElement.ConsiderAsArrayOf("animation");
REQUIRE(animationsElement.GetChildrenCount() == 1);
auto &animationElement = animationsElement.GetChild(0);
REQUIRE(animationElement.GetStringAttribute("name") ==
"Idle");
};
void CheckSpriteConfiguration(gd::Project &project) {
auto &layout = project.GetLayout("Scene");
auto &object = layout.GetObject("MyObject");
REQUIRE(object.GetName() == "MyObject");
REQUIRE(object.GetType() == "MyExtension::Sprite");
auto &configuration = object.GetConfiguration();
auto *spriteConfiguration = dynamic_cast<gd::SpriteObject *>(&configuration);
REQUIRE(spriteConfiguration);
REQUIRE(spriteConfiguration->GetAnimationsCount() == 1);
auto &animation = spriteConfiguration->GetAnimation(0);
REQUIRE(animation.GetName() == "Idle");
};
} // namespace
TEST_CASE("ObjectSerialization", "[common]") {
SECTION("Save and load a project with a sprite configuration") {
gd::Platform platform;
gd::Project writtenProject;
SetupProject(writtenProject, platform);
CheckSpriteConfiguration(writtenProject);
SerializerElement projectElement;
writtenProject.SerializeTo(projectElement);
CheckSpriteConfigurationInElement(projectElement);
gd::Project readProject;
readProject.AddPlatform(platform);
readProject.UnserializeFrom(projectElement);
CheckSpriteConfiguration(readProject);
}
}

View File

@@ -67,7 +67,9 @@ const gd::String &GetEventFirstActionType(const gd::BaseEvent &event) {
enum TestEvent {
FreeFunctionAction,
FreeFunctionExpression,
FreeFunctionWithExpression,
FreeFunctionWithObjects,
FreeFunctionWithObjectExpression,
BehaviorAction,
BehaviorPropertyAction,
@@ -113,7 +115,7 @@ const void SetupEvents(gd::EventsList &eventList) {
if (eventList.GetEventsCount() != FreeFunctionAction) {
throw std::logic_error("Invalid events setup");
}
// Create an event in the layout referring to
// Create an event referring to
// MyEventsExtension::MyEventsFunction
{
gd::StandardEvent event;
@@ -128,10 +130,10 @@ const void SetupEvents(gd::EventsList &eventList) {
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionExpression) {
if (eventList.GetEventsCount() != FreeFunctionWithExpression) {
throw std::logic_error("Invalid events setup");
}
// Create an event in the external events referring to
// Create an event referring to
// MyEventsExtension::MyEventsFunctionExpression
{
gd::StandardEvent event;
@@ -145,6 +147,38 @@ const void SetupEvents(gd::EventsList &eventList) {
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjects) {
throw std::logic_error("Invalid events setup");
}
// Create an event referring to objects
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomethingWithObjects");
action.SetParametersCount(2);
action.SetParameter(0, gd::Expression("ObjectWithMyBehavior"));
action.SetParameter(1, gd::Expression("MyCustomObject"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjectExpression) {
throw std::logic_error("Invalid events setup");
}
// Create an event referring to objects in an expression
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
"ObjectWithMyBehavior.GetObjectNumber()"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
}
// Add some events based behavior usages in events
@@ -854,6 +888,30 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject(
"GlobalObject3") == true);
}
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout, "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
}
SECTION("Object renamed (in events function)") {
@@ -894,6 +952,43 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Events are not tested
}
SECTION("Object renamed (in events-based object)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsBasedObject =
project.GetEventsFunctionsExtension("MyEventsExtension")
.GetEventsBasedObjects()
.Get("MyOtherEventsBasedObject");
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsBasedObject(
project, globalObjectsContainer, eventsBasedObject,
"ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
auto &objectFunctionEvents =
eventsBasedObject
.GetEventsFunctions()
.GetEventsFunction("MyObjectEventsFunction")
.GetEvents();
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(
objectFunctionEvents.GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
objectFunctionEvents.GetEvent(FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
SECTION("Object deleted (in events function)") {
gd::Project project;
gd::Platform platform;
@@ -948,7 +1043,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Check that events function calls in expressions have been renamed
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyRenamedExtension::MyEventsFunctionExpression(123, 456)");
// Check that the type of the behavior was changed in the behaviors of
@@ -1137,7 +1232,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been renamed
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyEventsExtension::MyRenamedFunctionExpression(123, 456)");
}
}
@@ -1177,7 +1272,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
for (auto *eventsList : GetEventsLists(project)) {
// Check that events function calls in expressions have been updated
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionExpression)) ==
eventsList->GetEvent(FreeFunctionWithExpression)) ==
"1 + MyEventsExtension::MyEventsFunctionExpression(456, 123)");
}
}

View File

@@ -454,7 +454,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -463,7 +463,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -507,7 +507,8 @@ module.exports = {
* This is called to update the PIXI object on the scene editor
*/
RenderedBBTextInstance.prototype.update = function () {
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
const rawText = properties.get('text').getValue();
if (rawText !== this._pixiObject.text) {

View File

@@ -589,7 +589,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -598,7 +598,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -634,7 +634,8 @@ module.exports = {
// This is called to update the PIXI object on the scene editor
RenderedBitmapTextInstance.prototype.update = function () {
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
// Update the rendered text properties (note: Pixi is only
// applying changes if there were changed).

View File

@@ -506,7 +506,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -515,7 +515,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -549,7 +549,7 @@ module.exports = {
*/
RenderedDummyObjectInstance.prototype.update = function () {
// Read a property from the object
const property1Value = this._associatedObject
const property1Value = this._associatedObjectConfiguration
.getProperties()
.get('My first property')
.getValue();

View File

@@ -269,7 +269,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -278,19 +278,19 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
this._radius = parseFloat(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('radius')
.getValue()
);
if (this._radius <= 0) this._radius = 1;
const colorHex = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('color')
.getValue()

View File

@@ -33,24 +33,16 @@ class PanelSpriteObjectJsExtension : public gd::PlatformExtension {
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::SetOpacity"]
.SetFunctionName("setOpacity")
.SetGetter("getOpacity")
.SetIncludeFile(
"Extensions/PanelSpriteObject/panelspriteruntimeobject.js");
.SetGetter("getOpacity");
GetAllConditionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("getOpacity");
GetAllExpressionsForObject(
"PanelSpriteObject::PanelSprite")["Opacity"]
.SetFunctionName("getOpacity")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("getOpacity");
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::SetColor"]
.SetFunctionName("setColor")
.SetIncludeFile(
"Extensions/TiledSpriteObject/panelspriteruntimeobject.js");
.SetFunctionName("setColor");
GetAllActionsForObject(
"PanelSpriteObject::PanelSprite")["PanelSpriteObject::Width"]

View File

@@ -19,9 +19,8 @@ This project is released under the MIT License.
using namespace std;
PanelSpriteObject::PanelSpriteObject(gd::String name_)
: Object(name_),
textureName(""),
PanelSpriteObject::PanelSpriteObject()
: textureName(""),
width(32),
height(32),
leftMargin(0),

View File

@@ -10,7 +10,7 @@ This project is released under the MIT License.
#include <memory>
#include "GDCore/Project/Object.h"
namespace gd {
class Object;
class ObjectConfiguration;
class InitialInstance;
class Project;
}
@@ -18,12 +18,12 @@ class Project;
/**
* PanelSprite Object
*/
class GD_EXTENSION_API PanelSpriteObject : public gd::Object {
class GD_EXTENSION_API PanelSpriteObject : public gd::ObjectConfiguration {
public:
PanelSpriteObject(gd::String name_);
PanelSpriteObject();
virtual ~PanelSpriteObject();
virtual std::unique_ptr<gd::Object> Clone() const {
return std::unique_ptr<gd::Object>(new PanelSpriteObject(*this));
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return std::unique_ptr<gd::ObjectConfiguration>(new PanelSpriteObject(*this));
}
#if defined(GD_IDE_ONLY)

View File

@@ -55,8 +55,7 @@ ParticleEmitterBase::ParticleEmitterBase()
maxParticleNb(300),
destroyWhenNoParticles(true) {}
ParticleEmitterObject::ParticleEmitterObject(gd::String name_)
: Object(name_) {}
ParticleEmitterObject::ParticleEmitterObject() {}
void ParticleEmitterObject::DoUnserializeFrom(
gd::Project& project, const gd::SerializerElement& element) {

View File

@@ -8,7 +8,7 @@ This project is released under the MIT License.
#ifndef PARTICLEEMITTEROBJECT_H
#define PARTICLEEMITTEROBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class InitialInstance;
class Project;
@@ -211,12 +211,12 @@ class GD_EXTENSION_API ParticleEmitterBase {
/**
* \brief Particle Emitter object used for storage and for the IDE.
*/
class GD_EXTENSION_API ParticleEmitterObject : public gd::Object,
class GD_EXTENSION_API ParticleEmitterObject : public gd::ObjectConfiguration,
public ParticleEmitterBase {
public:
ParticleEmitterObject(gd::String name_);
ParticleEmitterObject();
virtual ~ParticleEmitterObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<ParticleEmitterObject>(*this);
}

View File

@@ -33,7 +33,7 @@ ShapePainterObjectBase::ShapePainterObjectBase()
clearBetweenFrames(true),
absoluteCoordinates(false) {}
ShapePainterObject::ShapePainterObject(gd::String name_) : gd::Object(name_) {}
ShapePainterObject::ShapePainterObject() {}
void ShapePainterObjectBase::DoUnserializeFrom(
gd::Project& project, const gd::SerializerElement& element) {

View File

@@ -9,7 +9,7 @@ This project is released under the MIT License.
#define SHAPEPAINTEROBJECT_H
#include <vector>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class Object;
class InitialInstance;
@@ -88,12 +88,12 @@ class GD_EXTENSION_API ShapePainterObjectBase {
/**
* \brief The Shape Painter object used for storage and by the IDE.
*/
class GD_EXTENSION_API ShapePainterObject : public gd::Object,
class GD_EXTENSION_API ShapePainterObject : public gd::ObjectConfiguration,
public ShapePainterObjectBase {
public:
ShapePainterObject(gd::String name_);
ShapePainterObject();
virtual ~ShapePainterObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<ShapePainterObject>(*this);
}

View File

@@ -10,4 +10,4 @@ This project is released under the MIT License.
using namespace std;
TextEntryObject::TextEntryObject(gd::String name_) : Object(name_) {}
TextEntryObject::TextEntryObject() {}

View File

@@ -7,16 +7,16 @@ This project is released under the MIT License.
#ifndef TEXTENTRYOBJECT_H
#define TEXTENTRYOBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
/**
* \brief Simple object which stores user keyboard input.
*/
class GD_EXTENSION_API TextEntryObject : public gd::Object {
class GD_EXTENSION_API TextEntryObject : public gd::ObjectConfiguration {
public:
TextEntryObject(gd::String name_);
TextEntryObject();
virtual ~TextEntryObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<TextEntryObject>(*this);
}

View File

@@ -582,7 +582,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -590,7 +590,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -618,7 +618,8 @@ module.exports = {
update() {
const instance = this._instance;
const properties = this._associatedObject.getProperties();
const properties = this._associatedObjectConfiguration
.getProperties();
const placeholder =
instance.getRawStringProperty('placeholder') ||

View File

@@ -19,9 +19,8 @@ This project is released under the MIT License.
using namespace std;
TextObject::TextObject(gd::String name_)
: Object(name_),
text("Text"),
TextObject::TextObject()
: text("Text"),
characterSize(20),
fontName(""),
smoothed(true),

View File

@@ -7,7 +7,7 @@ This project is released under the MIT License.
#ifndef TEXTOBJECT_H
#define TEXTOBJECT_H
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class Project;
class Object;
@@ -17,11 +17,11 @@ class InitialInstance;
/**
* Text Object
*/
class GD_EXTENSION_API TextObject : public gd::Object {
class GD_EXTENSION_API TextObject : public gd::ObjectConfiguration {
public:
TextObject(gd::String name_);
TextObject();
virtual ~TextObject();
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<TextObject>(*this);
}

View File

@@ -940,26 +940,23 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
pixiRenderer
pixiResourcesLoader
) {
RenderedInstance.call(
this,
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader,
pixiRenderer
pixiResourcesLoader
);
// This setting allows tile maps with more than 16K tiles.
Tilemap.settings.use32bitIndex = true;
pixiRenderer.plugins.tilemap =
pixiRenderer.plugins.tilemap || new Tilemap.TileRenderer();
this.tileMapPixiObject = new Tilemap.CompositeTilemap();
this._pixiObject = this.tileMapPixiObject;
@@ -1020,26 +1017,26 @@ module.exports = {
*/
RenderedTileMapInstance.prototype.updateTileMap = function () {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObject
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObject
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObject
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const layerIndex = parseInt(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('layerIndex')
.getValue(),
10
);
const displayMode = this._associatedObject
const displayMode = this._associatedObjectConfiguration
.getProperties(this.project)
.get('displayMode')
.getValue();
@@ -1187,7 +1184,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -1196,7 +1193,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -1262,39 +1259,39 @@ module.exports = {
*/
RenderedCollisionMaskInstance.prototype.updateTileMap = function () {
// Get the tileset resource to use
const tilemapAtlasImage = this._associatedObject
const tilemapAtlasImage = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapAtlasImage')
.getValue();
const tilemapJsonFile = this._associatedObject
const tilemapJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilemapJsonFile')
.getValue();
const tilesetJsonFile = this._associatedObject
const tilesetJsonFile = this._associatedObjectConfiguration
.getProperties(this.project)
.get('tilesetJsonFile')
.getValue();
const collisionMaskTag = this._associatedObject
const collisionMaskTag = this._associatedObjectConfiguration
.getProperties(this.project)
.get('collisionMaskTag')
.getValue();
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineColor')
.getValue()
);
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
this._associatedObject
this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillColor')
.getValue()
);
const outlineOpacity = this._associatedObject
const outlineOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('outlineOpacity')
.getValue() / 255;
const fillOpacity = this._associatedObject
const fillOpacity = this._associatedObjectConfiguration
.getProperties(this.project)
.get('fillOpacity')
.getValue() / 255;

View File

@@ -10,7 +10,6 @@ namespace gdjs {
*/
export class TileMapRuntimeObjectPixiRenderer {
private _object: any;
private _runtimeScene: gdjs.RuntimeScene;
private _tileMap: TileMapHelper.EditableTileMap | null = null;
private _pixiObject: PIXI.tilemap.CompositeTilemap;
@@ -24,20 +23,9 @@ namespace gdjs {
runtimeScene: gdjs.RuntimeScene
) {
this._object = runtimeObject;
this._runtimeScene = runtimeScene;
const pixiRenderer = runtimeScene
.getGame()
.getRenderer()
.getPIXIRenderer();
// This setting allows tile maps with more than 16K tiles.
PIXI.tilemap.settings.use32bitIndex = true;
if (pixiRenderer) {
pixiRenderer.plugins.tilemap =
// @ts-ignore - pixi-tilemap types to be added.
pixiRenderer.plugins.tilemap || new PIXI.tilemap.TileRenderer();
}
// Load (or reset)
this._pixiObject = new PIXI.tilemap.CompositeTilemap();

View File

@@ -20,8 +20,8 @@ This project is released under the MIT License.
using namespace std;
TiledSpriteObject::TiledSpriteObject(gd::String name_)
: Object(name_), textureName(""), width(32), height(32) {}
TiledSpriteObject::TiledSpriteObject()
: textureName(""), width(32), height(32) {}
void TiledSpriteObject::DoUnserializeFrom(
gd::Project& project, const gd::SerializerElement& element) {
@@ -40,4 +40,4 @@ void TiledSpriteObject::DoSerializeTo(gd::SerializerElement& element) const {
void TiledSpriteObject::ExposeResources(gd::ArbitraryResourceWorker& worker) {
worker.ExposeImage(textureName);
}
#endif
#endif

View File

@@ -8,7 +8,7 @@ This project is released under the MIT License.
#ifndef TILEDSPRITEOBJECT_H
#define TILEDSPRITEOBJECT_H
#include <memory>
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectConfiguration.h"
namespace gd {
class InitialInstance;
class Project;
@@ -17,11 +17,11 @@ class Project;
/**
* TiledSprite Object
*/
class GD_EXTENSION_API TiledSpriteObject : public gd::Object {
class GD_EXTENSION_API TiledSpriteObject : public gd::ObjectConfiguration {
public:
TiledSpriteObject(gd::String name_);
TiledSpriteObject();
virtual ~TiledSpriteObject(){};
virtual std::unique_ptr<gd::Object> Clone() const {
virtual std::unique_ptr<gd::ObjectConfiguration> Clone() const {
return gd::make_unique<TiledSpriteObject>(*this);
}

View File

@@ -528,7 +528,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
) {
@@ -537,7 +537,7 @@ module.exports = {
project,
layout,
instance,
associatedObject,
associatedObjectConfiguration,
pixiContainer,
pixiResourcesLoader
);
@@ -568,7 +568,7 @@ module.exports = {
RenderedVideoObjectInstance.prototype._getVideoTexture = function () {
// Get the video resource to use
const videoResource = this._associatedObject
const videoResource = this._associatedObjectConfiguration
.getProperties()
.get('videoResource')
.getValue();
@@ -585,7 +585,7 @@ module.exports = {
*/
RenderedVideoObjectInstance.prototype.update = function () {
// Check if the video resource has changed
const videoResource = this._associatedObject
const videoResource = this._associatedObjectConfiguration
.getProperties()
.get('videoResource')
.getValue();
@@ -606,7 +606,7 @@ module.exports = {
}
// Update opacity
const opacity = this._associatedObject
const opacity = this._associatedObjectConfiguration
.getProperties()
.get('Opacity')
.getValue();

View File

@@ -6,6 +6,20 @@
namespace gdjs {
const logger = new gdjs.Logger('Font manager');
const checkIfCredentialsRequired = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
// Note that this is only useful during previews.
if (
url.startsWith('https://project-resources.gdevelop.io/') ||
url.startsWith('https://project-resources-dev.gdevelop.io/')
)
return true;
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
return false;
};
/**
* FontFaceObserverFontManager loads fonts (using `FontFace` or `fontfaceobserver` library)
* from the game resources (see `loadFonts`), and allow to access to
@@ -121,13 +135,40 @@ namespace gdjs {
// @ts-ignore
if (typeof FontFace !== 'undefined') {
// Load the given font using CSS Font Loading API.
// @ts-ignore
const fontFace = new FontFace(fontFamily, srcWithUrl, descriptors);
return fetch(src, {
credentials: checkIfCredentialsRequired(src)
? // Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
'include'
: // For other resources, use "same-origin" as done by default by fetch.
'same-origin',
})
.then((response) => {
if (!response.ok) {
const errorMessage =
'Unable to fetch ' +
src +
' to be loaded as a font. HTTP status is: ' +
response.status +
'.';
logger.error(errorMessage);
throw new Error(errorMessage);
}
// @ts-ignore
document.fonts.add(fontFace);
return fontFace.load();
return response.arrayBuffer();
})
.then((arrayBuffer) => {
// @ts-ignore
const fontFace = new FontFace(fontFamily, arrayBuffer, descriptors);
// @ts-ignore
document.fonts.add(fontFace);
});
} else {
// TODO: this method of loading font should be removed as old and not allowing
// to handle loading with credentials. All moderns and not-so-modern browsers
// that we support also support FontFace API.
// Add @font-face and use FontFaceObserver to be notified when the
// font is ready.
const newStyle = document.createElement('style');

View File

@@ -15,6 +15,20 @@ namespace gdjs {
logger.error('Error while loading an audio file: ' + error),
};
const checkIfCredentialsRequired = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
// Note that this is only useful during previews.
if (
url.startsWith('https://project-resources.gdevelop.io/') ||
url.startsWith('https://project-resources-dev.gdevelop.io/')
)
return true;
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
return false;
};
/**
* Ensure the volume is between 0 and 1.
*/
@@ -517,6 +531,9 @@ namespace gdjs {
{
src: [soundFile],
html5: isMusic,
xhr: {
withCredentials: checkIfCredentialsRequired(soundFile),
},
// Cache the sound with no volume. This avoids a bug where it plays at full volume
// for a split second before setting its correct volume.
volume: 0,
@@ -551,6 +568,9 @@ namespace gdjs {
{
src: [soundFile],
html5: isMusic,
xhr: {
withCredentials: checkIfCredentialsRequired(soundFile),
},
// Cache the sound with no volume. This avoids a bug where it plays at full volume
// for a split second before setting its correct volume.
volume: 0,
@@ -791,6 +811,9 @@ namespace gdjs {
onload: onLoadCallback,
onloaderror: onLoadCallback,
html5: isMusic,
xhr: {
withCredentials: checkIfCredentialsRequired(file),
},
// Cache the sound with no volume. This avoids a bug where it plays at full volume
// for a split second before setting its correct volume.
volume: 0,
@@ -823,6 +846,7 @@ namespace gdjs {
// preloading as sound already does a XHR request, hence "else if"
loadCounter++;
const sound = new XMLHttpRequest();
sound.withCredentials = checkIfCredentialsRequired(file);
sound.addEventListener('load', callback);
sound.addEventListener('error', (_) =>
callback(_, 'XHR error: ' + file)

View File

@@ -17,6 +17,20 @@ namespace gdjs {
content: Object | null
) => void;
const checkIfCredentialsRequired = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
// Note that this is only useful during previews.
if (
url.startsWith('https://project-resources.gdevelop.io/') ||
url.startsWith('https://project-resources-dev.gdevelop.io/')
)
return true;
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
return false;
};
/**
* JsonManager loads json files (using `XMLHttpRequest`), using the "json" resources
* registered in the game resources.
@@ -127,6 +141,7 @@ namespace gdjs {
const that = this;
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.withCredentials = checkIfCredentialsRequired(resource.file);
xhr.open('GET', resource.file);
xhr.onload = function () {
const callbacks = that._callbacks[resourceName];

View File

@@ -16,6 +16,20 @@ namespace gdjs {
// Set this to 0 to unload from memory ("uninstall") as soon as a font is unused.
const uninstallCacheSize = 5;
const checkIfCredentialsRequired = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
// Note that this is only useful during previews.
if (
url.startsWith('https://project-resources.gdevelop.io/') ||
url.startsWith('https://project-resources-dev.gdevelop.io/')
)
return true;
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
return false;
};
/**
* We patch the installed font to use a name that is unique for each font data and texture,
* to avoid conflicts between different font files using the same font name (by default, the
@@ -261,7 +275,14 @@ namespace gdjs {
let loadedCount = 0;
return Promise.all(
bitmapFontResources.map((bitmapFontResource) => {
return fetch(bitmapFontResource.file)
return fetch(bitmapFontResource.file, {
credentials: checkIfCredentialsRequired(bitmapFontResource.file)
? // Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
'include'
: // For other resources, use "same-origin" as done by default by fetch.
'same-origin',
})
.then((response) => response.text())
.then((fontData) => {
this._loadedFontsData[bitmapFontResource.name] = fontData;

View File

@@ -40,7 +40,7 @@ namespace gdjs {
return null;
};
const determineCrossOrigin = (url: string) => {
const checkIfCredentialsRequired = (url: string) => {
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
// i.e: its gdevelop.io cookie, to be passed.
// Note that this is only useful during previews.
@@ -48,12 +48,10 @@ namespace gdjs {
url.startsWith('https://project-resources.gdevelop.io/') ||
url.startsWith('https://project-resources-dev.gdevelop.io/')
)
return 'use-credentials';
return true;
// For other resources, use "anonymous" as done by default by PixiJS. Note that using `false`
// to not having `crossorigin` at all would NOT work because the browser would taint the
// loaded resource so that it can't be read/used in a canvas (it's only working for display `<img>` on screen).
return 'anonymous';
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
return false;
};
/**
@@ -134,7 +132,12 @@ namespace gdjs {
const file = resource.file;
const texture = PIXI.Texture.from(file, {
resourceOptions: {
crossorigin: determineCrossOrigin(file),
// Note that using `false`
// to not having `crossorigin` at all would NOT work because the browser would taint the
// loaded resource so that it can't be read/used in a canvas (it's only working for display `<img>` on screen).
crossorigin: checkIfCredentialsRequired(file)
? 'use-credentials'
: 'anonymous',
},
}).on('error', (error) => {
logFileLoadingError(file, error);
@@ -178,7 +181,12 @@ namespace gdjs {
);
const texture = PIXI.Texture.from(file, {
resourceOptions: {
crossorigin: determineCrossOrigin(file),
// Note that using `false`
// to not having `crossorigin` at all would NOT work because the browser would taint the
// loaded resource so that it can't be read/used in a canvas (it's only working for display `<img>` on screen).
crossorigin: checkIfCredentialsRequired(file)
? 'use-credentials'
: 'anonymous',
},
}).on('error', (error) => {
logFileLoadingError(file, error);
@@ -239,7 +247,9 @@ namespace gdjs {
name: file,
url: file,
loadType: PIXI.LoaderResource.LOAD_TYPE.IMAGE,
crossOrigin: determineCrossOrigin(file),
crossOrigin: checkIfCredentialsRequired(file)
? 'use-credentials'
: 'anonymous',
});
}
}

View File

@@ -121,8 +121,8 @@ interface ProjectHelper {
void STATIC_InitializePlatforms();
[Const, Value] DOMString STATIC_SanityCheckBehaviorProperty(Behavior behavior, [Const] DOMString propertyName, [Const] DOMString newValue);
[Const, Value] DOMString STATIC_SanityCheckBehaviorsSharedDataProperty(BehaviorsSharedData behavior, [Const] DOMString propertyName, [Const] DOMString newValue);
[Const, Value] DOMString STATIC_SanityCheckObjectProperty(gdObject obj, [Const] DOMString propertyName, [Const] DOMString newValue);
[Const, Value] DOMString STATIC_SanityCheckObjectInitialInstanceProperty(gdObject obj, [Const] DOMString propertyName, [Const] DOMString newValue);
[Const, Value] DOMString STATIC_SanityCheckObjectProperty(ObjectConfiguration configuration, [Const] DOMString propertyName, [Const] DOMString newValue);
[Const, Value] DOMString STATIC_SanityCheckObjectInitialInstanceProperty(ObjectConfiguration configuration, [Const] DOMString propertyName, [Const] DOMString newValue);
};
interface EventsVariablesFinder {
@@ -543,10 +543,33 @@ interface BehaviorSharedDataJsImplementation {
void InitializeContent([Ref] SerializerElement behaviorSharedDataContent);
};
interface ObjectConfiguration {
void ObjectConfiguration();
[Value] UniquePtrObjectConfiguration Clone();
[Const, Ref] DOMString GetType();
[Value] MapStringPropertyDescriptor GetProperties();
boolean UpdateProperty([Const] DOMString name, [Const] DOMString value);
[Value] MapStringPropertyDescriptor GetInitialInstanceProperties([Const, Ref] InitialInstance instance, [Ref] Project project, [Ref] Layout scene);
boolean UpdateInitialInstanceProperty([Ref] InitialInstance instance, [Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout scene);
void ExposeResources([Ref] ArbitraryResourceWorker worker);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
};
interface UniquePtrObjectConfiguration {
ObjectConfiguration get();
ObjectConfiguration release();
};
interface gdObject {
// /!\ We need to call it gdObject to avoid messing up with javascript Object
// global in glue.js!
void gdObject([Const] DOMString name);
void gdObject([Const] DOMString name, [Const] DOMString type, ObjectConfiguration configuration);
[Value] UniquePtrObject Clone();
void SetName([Const] DOMString name);
@@ -558,13 +581,7 @@ interface gdObject {
void SetTags([Const] DOMString tags);
[Const, Ref] DOMString GetTags();
[Value] MapStringPropertyDescriptor GetProperties();
boolean UpdateProperty([Const] DOMString name, [Const] DOMString value);
[Value] MapStringPropertyDescriptor GetInitialInstanceProperties([Const, Ref] InitialInstance instance, [Ref] Project project, [Ref] Layout scene);
boolean UpdateInitialInstanceProperty([Ref] InitialInstance instance, [Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout scene);
void ExposeResources([Ref] ArbitraryResourceWorker worker);
[Ref] ObjectConfiguration GetConfiguration();
[Ref] VariablesContainer GetVariables();
[Ref] EffectsContainer GetEffects();
@@ -584,10 +601,10 @@ interface UniquePtrObject {
gdObject release();
};
[JSImplementation=gdObject]
[JSImplementation=ObjectConfiguration]
interface ObjectJsImplementation {
void ObjectJsImplementation();
[Value] UniquePtrObject Clone();
[Value] UniquePtrObjectConfiguration Clone();
[Value] MapStringPropertyDescriptor GetProperties();
boolean UpdateProperty([Const] DOMString name, [Const] DOMString value);
@@ -606,6 +623,19 @@ interface ObjectJsImplementation {
// void DoUnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
};
interface CustomObjectConfiguration {
[Value] UniquePtrObjectConfiguration Clone();
[Ref] ObjectConfiguration GetChildObjectConfiguration([Const] DOMString objectName);
[Value] MapStringPropertyDescriptor GetProperties();
boolean UpdateProperty([Const] DOMString name, [Const] DOMString value);
[Value] MapStringPropertyDescriptor GetInitialInstanceProperties([Const, Ref] InitialInstance instance, [Ref] Project project, [Ref] Layout scene);
boolean UpdateInitialInstanceProperty([Ref] InitialInstance instance, [Const] DOMString name, [Const] DOMString value, [Ref] Project project, [Ref] Layout scene);
};
CustomObjectConfiguration implements ObjectConfiguration;
interface Layout {
void Layout();
@@ -1600,7 +1630,7 @@ interface PlatformExtension {
[Const] DOMString fullname,
[Const] DOMString description,
[Const] DOMString icon24x24,
gdObject instance);
ObjectConfiguration instance);
[Ref] ObjectMetadata AddEventsBasedObject([Const] DOMString name,
[Const] DOMString fullname,
@@ -2047,7 +2077,7 @@ interface WholeProjectRefactorer {
void STATIC_ObjectOrGroupRemovedInLayout([Ref] Project project, [Ref] Layout layout, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
void STATIC_ObjectOrGroupRenamedInEventsFunction([Ref] Project project, [Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
void STATIC_ObjectOrGroupRemovedInEventsFunction([Ref] Project project, [Ref] EventsFunction eventsFunction, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
void STATIC_ObjectOrGroupRenamedInEventsBasedObject([Ref] Project project, [Ref] EventsBasedObject eventsBasedObject, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
void STATIC_ObjectOrGroupRenamedInEventsBasedObject([Ref] Project project, [Ref] ObjectsContainer globalObjectsContainer, [Ref] EventsBasedObject eventsBasedObject, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
void STATIC_ObjectOrGroupRemovedInEventsBasedObject([Ref] Project project, [Ref] EventsBasedObject eventsBasedObject, [Ref] ObjectsContainer globalObjectsContainer, [Ref] ObjectsContainer objectsContainer, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
void STATIC_GlobalObjectOrGroupRenamed([Ref] Project project, [Const] DOMString oldName, [Const] DOMString newName, boolean isObjectGroup);
void STATIC_GlobalObjectOrGroupRemoved([Ref] Project project, [Const] DOMString objectName, boolean isObjectGroup, boolean removeEventsAndGroups);
@@ -2298,24 +2328,11 @@ interface EventsBasedObject {
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
//Inherited from gd::ObjectsContainer
[Ref] gdObject InsertNewObject([Ref] Project project, [Const] DOMString type, [Const] DOMString name, unsigned long pos);
[Ref] gdObject InsertObject([Const, Ref] gdObject obj, unsigned long pos);
boolean HasObjectNamed([Const] DOMString name);
[Ref] gdObject GetObject([Const] DOMString name);
[Ref] gdObject GetObjectAt(unsigned long pos);
unsigned long GetObjectPosition([Const] DOMString name);
void RemoveObject([Const] DOMString name);
void SwapObjects(unsigned long first, unsigned long second);
void MoveObject(unsigned long oldIndex, unsigned long newIndex);
void MoveObjectToAnotherContainer([Const] DOMString name, [Ref] ObjectsContainer newObjectsContainer, unsigned long newPosition);
unsigned long GetObjectsCount();
[Ref] ObjectGroupsContainer GetObjectGroups();
[Const, Value] DOMString STATIC_GetPropertyActionName([Const] DOMString propertyName);
[Const, Value] DOMString STATIC_GetPropertyConditionName([Const] DOMString propertyName);
[Const, Value] DOMString STATIC_GetPropertyExpressionName([Const] DOMString propertyName);
};
EventsBasedObject implements ObjectsContainer;
interface EventsBasedObjectsList {
[Ref] EventsBasedObject InsertNew([Const] DOMString name, unsigned long pos);
@@ -2659,7 +2676,7 @@ interface Animation {
};
interface SpriteObject {
void SpriteObject([Const] DOMString name);
void SpriteObject();
void AddAnimation([Const, Ref] Animation animation);
[Ref] Animation GetAnimation(unsigned long index);
@@ -2673,7 +2690,7 @@ interface SpriteObject {
void SetUpdateIfNotVisible(boolean updateIfNotVisible);
boolean GetUpdateIfNotVisible();
};
SpriteObject implements gdObject;
SpriteObject implements ObjectConfiguration;
interface Vector2f {
void Vector2f();
@@ -2697,7 +2714,7 @@ interface VectorVector2f {
//Extensions bindings:
interface TextObject {
void TextObject([Const] DOMString name);
void TextObject();
void SetString([Const] DOMString string);
[Const, Ref] DOMString GetString();
@@ -2716,10 +2733,10 @@ interface TextObject {
unsigned long GetColorG();
unsigned long GetColorB();
};
TextObject implements gdObject;
TextObject implements ObjectConfiguration;
interface TiledSpriteObject {
void TiledSpriteObject([Const] DOMString name);
void TiledSpriteObject();
void SetTexture([Const] DOMString texture);
[Const, Ref] DOMString GetTexture();
@@ -2728,10 +2745,10 @@ interface TiledSpriteObject {
void SetHeight(float height);
float GetHeight();
};
TiledSpriteObject implements gdObject;
TiledSpriteObject implements ObjectConfiguration;
interface PanelSpriteObject {
void PanelSpriteObject([Const] DOMString name);
void PanelSpriteObject();
float GetLeftMargin();
void SetLeftMargin(float newMargin);
@@ -2756,10 +2773,10 @@ interface PanelSpriteObject {
void SetHeight(float height);
float GetHeight();
};
PanelSpriteObject implements gdObject;
PanelSpriteObject implements ObjectConfiguration;
interface ShapePainterObject {
void ShapePainterObject([Const] DOMString name);
void ShapePainterObject();
void SetCoordinatesAbsolute();
void SetCoordinatesRelative();
@@ -2787,12 +2804,12 @@ interface ShapePainterObject {
unsigned long GetFillColorG();
unsigned long GetFillColorB();
};
ShapePainterObject implements gdObject;
ShapePainterObject implements ObjectConfiguration;
interface TextEntryObject {
void TextEntryObject([Const] DOMString name);
void TextEntryObject();
};
TextEntryObject implements gdObject;
TextEntryObject implements ObjectConfiguration;
enum ParticleEmitterObject_RendererType {
"ParticleEmitterObject::Point",
@@ -2801,7 +2818,7 @@ enum ParticleEmitterObject_RendererType {
};
interface ParticleEmitterObject {
void ParticleEmitterObject([Const] DOMString name);
void ParticleEmitterObject();
void SetRendererType(ParticleEmitterObject_RendererType type);
ParticleEmitterObject_RendererType GetRendererType();
@@ -2889,7 +2906,7 @@ interface ParticleEmitterObject {
float GetParticleAngleRandomness2();
};
ParticleEmitterObject implements gdObject;
ParticleEmitterObject implements ObjectConfiguration;
//GDJS bindings:
[Prefix="gdjs::"]

View File

@@ -12,7 +12,7 @@
using namespace gd;
std::unique_ptr<gd::Object> ObjectJsImplementation::Clone() const {
std::unique_ptr<gd::ObjectConfiguration> ObjectJsImplementation::Clone() const {
ObjectJsImplementation* clone = new ObjectJsImplementation(*this);
// Copy the references to the JS implementations of the functions (because we
@@ -31,7 +31,7 @@ std::unique_ptr<gd::Object> ObjectJsImplementation::Clone() const {
(int)clone,
(int)this);
return std::unique_ptr<gd::Object>(clone);
return std::unique_ptr<gd::ObjectConfiguration>(clone);
}
std::map<gd::String, gd::PropertyDescriptor>
@@ -144,12 +144,12 @@ bool ObjectJsImplementation::UpdateInitialInstanceProperty(
(int)&scene);
}
void ObjectJsImplementation::DoSerializeTo(SerializerElement& arg0) const {
arg0.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
void ObjectJsImplementation::DoSerializeTo(SerializerElement& element) const {
element.AddChild("content") = gd::Serializer::FromJSON(jsonContent);
}
void ObjectJsImplementation::DoUnserializeFrom(Project& arg0,
const SerializerElement& arg1) {
jsonContent = gd::Serializer::ToJSON(arg1.GetChild("content"));
void ObjectJsImplementation::DoUnserializeFrom(Project& project,
const SerializerElement& element) {
jsonContent = gd::Serializer::ToJSON(element.GetChild("content"));
}
void ObjectJsImplementation::__destroy__() { // Useless?

View File

@@ -1,4 +1,5 @@
#include <GDCore/Project/Object.h>
#include <GDCore/Project/ObjectConfiguration.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Project/PropertyDescriptor.h>
#include <GDCore/Serialization/Serializer.h>
@@ -14,14 +15,10 @@ using namespace gd;
* It also implements "ExposeResources" to expose the properties of type
* "resource".
*/
class ObjectJsImplementation : public gd::Object {
class ObjectJsImplementation : public gd::ObjectConfiguration {
public:
ObjectJsImplementation()
: // Name is not important as this object is just a "blueprint"
// that is copied (see calls to AddObject).
Object("ObjectJsImplementation"),
jsonContent("{}") {}
std::unique_ptr<gd::Object> Clone() const override;
ObjectJsImplementation() : jsonContent("{}") {}
std::unique_ptr<gd::ObjectConfiguration> Clone() const override;
std::map<gd::String, gd::PropertyDescriptor> GetProperties() const override;
bool UpdateProperty(const gd::String& name, const gd::String& value) override;

View File

@@ -103,25 +103,28 @@ class ProjectHelper {
* \brief This check that the given gd::Object can be properly cloned
* and have the given property updated.
*/
static gd::String SanityCheckObjectProperty(gd::Object* object,
const gd::String& propertyName,
const gd::String& newValue) {
if (!object) return "FAIL: object passed is null";
static gd::String SanityCheckObjectProperty(
gd::ObjectConfiguration* objectConfiguration,
const gd::String& propertyName,
const gd::String& newValue) {
if (!objectConfiguration) return "FAIL: object passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::String originalValue =
object->GetProperties()[propertyName].GetValue();
gd::Object object("MyObject", "", objectConfiguration->Clone());
std::unique_ptr<gd::Object> copiedObject = object->Clone();
if (copiedObject->GetProperties()[propertyName].GetValue() !=
gd::String originalValue =
object.GetConfiguration().GetProperties()[propertyName].GetValue();
std::unique_ptr<gd::Object> copiedObject = object.Clone();
if (copiedObject->GetConfiguration().GetProperties()[propertyName].GetValue() !=
originalValue) {
return "FAIL: Cloning the object does not copy properly the property";
}
object->UpdateProperty(propertyName, newValue);
object.GetConfiguration().UpdateProperty(propertyName, newValue);
gd::String updatedValue =
object->GetProperties()[propertyName].GetValue();
object.GetConfiguration().GetProperties()[propertyName].GetValue();
if (updatedValue != newValue) {
return "FAIL: expected the newValue to be set for the property, but "
"received:" +
@@ -129,7 +132,7 @@ class ProjectHelper {
}
gd::String copiedObjectValue =
copiedObject->GetProperties()[propertyName].GetValue();
copiedObject->GetConfiguration().GetProperties()[propertyName].GetValue();
if (copiedObjectValue != originalValue) {
return "FAIL: Updating the property of the object will change the "
"property of the cloned object. Clone object property is "
@@ -145,34 +148,39 @@ class ProjectHelper {
* and return/set the properties of a gd::InitialInstance.
*/
static gd::String SanityCheckObjectInitialInstanceProperty(
gd::Object* object,
gd::ObjectConfiguration* objectConfiguration,
const gd::String& propertyName,
const gd::String& newValue) {
if (!object) return "FAIL: object passed is null";
if (!objectConfiguration) return "FAIL: object passed is null";
gd::Project project;
project.AddPlatform(JsPlatform::Get());
gd::Layout layout;
gd::InitialInstance instance;
gd::Object object("MyObject", "", objectConfiguration->Clone());
gd::String originalValue = object
->GetInitialInstanceProperties(
.GetConfiguration()
.GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue();
std::unique_ptr<gd::Object> copiedObject = object->Clone();
std::unique_ptr<gd::Object> copiedObject = object.Clone();
if (copiedObject
->GetInitialInstanceProperties(
->GetConfiguration()
.GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue() != originalValue) {
return "FAIL: Cloned object does not return the same initial value for "
"the instance property";
}
copiedObject->UpdateInitialInstanceProperty(
copiedObject->GetConfiguration().UpdateInitialInstanceProperty(
instance, propertyName, newValue, project, layout);
gd::String updatedValue = copiedObject
->GetInitialInstanceProperties(
->GetConfiguration()
.GetInitialInstanceProperties(
instance, project, layout)[propertyName]
.GetValue();
if (updatedValue != newValue) {

View File

@@ -62,8 +62,8 @@
#include <GDCore/Project/Layout.h>
#include <GDCore/Project/NamedPropertyDescriptor.h>
#include <GDCore/Project/Object.h>
#include <GDCore/Project/CustomObject.h>
#include <GDCore/Project/CustomBehavior.h>
#include <GDCore/Project/ObjectConfiguration.h>
#include <GDCore/Project/CustomObjectConfiguration.h>
#include <GDCore/Project/Project.h>
#include <GDCore/Project/PropertyDescriptor.h>
#include <GDCore/Project/Variable.h>
@@ -413,6 +413,7 @@ typedef gd::Object gdObject; // To avoid clashing javascript Object in glue.js
typedef ParticleEmitterObject::RendererType ParticleEmitterObject_RendererType;
typedef EventsFunction::FunctionType EventsFunction_FunctionType;
typedef std::unique_ptr<gd::Object> UniquePtrObject;
typedef std::unique_ptr<gd::ObjectConfiguration> UniquePtrObjectConfiguration;
typedef std::unique_ptr<ExpressionNode> UniquePtrExpressionNode;
typedef std::vector<gd::ExpressionParserDiagnostic *>
VectorExpressionParserDiagnostic;
@@ -491,7 +492,7 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
fullname, \
description, \
icon24x24, \
std::shared_ptr<gd::Object>(instance))
std::shared_ptr<gd::ObjectConfiguration>(instance))
#define WRAPPED_at(a) at(a).get()

View File

@@ -117,33 +117,36 @@ var adaptNamingConventions = function (gd) {
return gd.castObject(evt, gd.Platform);
};
gd.asSpriteObject = function (evt) {
gd.asSpriteConfiguration = function (evt) {
return gd.castObject(evt, gd.SpriteObject);
};
gd.asTiledSpriteObject = function (evt) {
gd.asTiledSpriteConfiguration = function (evt) {
return gd.castObject(evt, gd.TiledSpriteObject);
};
gd.asPanelSpriteObject = function (evt) {
gd.asPanelSpriteConfiguration = function (evt) {
return gd.castObject(evt, gd.PanelSpriteObject);
};
gd.asTextObject = function (evt) {
gd.asTextObjectConfiguration = function (evt) {
return gd.castObject(evt, gd.TextObject);
};
gd.asShapePainterObject = function (evt) {
gd.asShapePainterConfiguration = function (evt) {
return gd.castObject(evt, gd.ShapePainterObject);
};
gd.asAdMobObject = function (evt) {
gd.asAdMobConfiguration = function (evt) {
return gd.castObject(evt, gd.AdMobObject);
};
gd.asTextEntryObject = function (evt) {
return gd.castObject(evt, gd.TextEntryObject);
};
gd.asParticleEmitterObject = function (evt) {
gd.asParticleEmitterConfiguration = function (evt) {
return gd.castObject(evt, gd.ParticleEmitterObject);
};
gd.asObjectJsImplementation = function (evt) {
return gd.castObject(evt, gd.ObjectJsImplementation);
};
gd.asCustomObjectConfiguration = function (evt) {
return gd.castObject(evt, gd.CustomObjectConfiguration);
};
gd.asImageResource = function (evt) {
return gd.castObject(evt, gd.ImageResource);

View File

@@ -374,20 +374,14 @@ describe('libGD.js', function () {
describe('gd.ObjectsContainer', function () {
it('can move objects between containers, without moving them in memory', function () {
const project = new gd.ProjectHelper.createNewGDJSProject();
// Prepare two containers, one with 3 objects and one empty
const objectsContainer1 = new gd.ObjectsContainer();
const objectsContainer2 = new gd.ObjectsContainer();
const mySpriteObject = new gd.SpriteObject('MySprite');
const mySprite2Object = new gd.SpriteObject('MySprite2');
const mySprite3Object = new gd.SpriteObject('MySprite3');
objectsContainer1.insertObject(mySpriteObject, 0);
objectsContainer1.insertObject(mySprite2Object, 1);
objectsContainer1.insertObject(mySprite3Object, 2);
// Objects are copied when inserted in the container, so we delete them:
mySpriteObject.delete();
mySprite2Object.delete();
mySprite3Object.delete();
const mySpriteObject = objectsContainer1.insertNewObject(project, 'Sprite', 'MySprite', 0);
const mySprite2Object = objectsContainer1.insertNewObject(project, 'Sprite', 'MySprite2', 1);
const mySprite3Object = objectsContainer1.insertNewObject(project, 'Sprite', 'MySprite3', 2);
// Find the pointer to the objects in memory
expect(objectsContainer1.getObjectsCount()).toBe(3);
@@ -455,6 +449,7 @@ describe('libGD.js', function () {
expect(gd.getPointer(objectsContainer2.getObjectAt(0))).toBe(
mySprite3ObjectPtr
);
project.delete();
});
});
@@ -1200,7 +1195,7 @@ describe('libGD.js', function () {
anim1.setDirectionsCount(1);
anim1.getDirection(0).addSprite(sprite1);
gd.castObject(obj, gd.SpriteObject).addAnimation(anim1);
gd.castObject(obj.getConfiguration(), gd.SpriteObject).addAnimation(anim1);
{
let allResources = project.getResourcesManager().getAllResourceNames();
@@ -1257,13 +1252,13 @@ describe('libGD.js', function () {
it('should be called with resources of the project', function (done) {
let project = gd.ProjectHelper.createNewGDJSProject();
let obj = project.insertNewObject(project, 'Sprite', 'MyObject', 0);
const spriteObject = gd.asSpriteObject(obj);
const spriteConfiguration = gd.asSpriteConfiguration(obj.getConfiguration());
let sprite1 = new gd.Sprite();
sprite1.setImageName('Used');
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
spriteObject.addAnimation(animation);
spriteConfiguration.addAnimation(animation);
let worker = extend(new gd.ArbitraryResourceWorkerJS(), {
exposeImage: function (image) {
@@ -1288,7 +1283,7 @@ describe('libGD.js', function () {
let sprite3 = new gd.Sprite();
sprite3.setImageName('Image3');
const spriteObject = new gd.SpriteObject('My sprite object');
const spriteObject = new gd.SpriteObject();
const animation = new gd.Animation();
animation.setDirectionsCount(1);
animation.getDirection(0).addSprite(sprite1);
@@ -1296,7 +1291,7 @@ describe('libGD.js', function () {
animation.getDirection(0).addSprite(sprite1);
spriteObject.addAnimation(animation);
const spriteObject2 = new gd.SpriteObject('My sprite object');
const spriteObject2 = new gd.SpriteObject();
const animation2 = new gd.Animation();
animation2.setDirectionsCount(1);
animation2.getDirection(0).addSprite(sprite1);
@@ -1779,7 +1774,7 @@ describe('libGD.js', function () {
// see ObjectJsImplementation C++ implementation). If called directly here from JS,
// the arguments will be mismatched. To workaround this, always case the object to
// a base gdObject to ensure C++ methods are called.
return gd.castObject(myObject, gd.Object);
return gd.castObject(myObject, gd.ObjectConfiguration);
};
it('can create a gd.ObjectJsImplementation and pass sanity checks', function () {
@@ -1824,7 +1819,6 @@ describe('libGD.js', function () {
});
it('can clone a gd.ObjectJsImplementation', function () {
const project = gd.ProjectHelper.createNewGDJSProject();
const object1 = createSampleObjectJsImplementation();
expect(
object1.getProperties().get('My first property').getValue() ==
@@ -1883,8 +1877,6 @@ describe('libGD.js', function () {
propertiesObject3.get('My first property').getValue() == 'test1'
);
}
project.delete();
});
});
@@ -2640,17 +2632,19 @@ describe('libGD.js', function () {
describe('gd.SpriteObject', function () {
it('is a gd.Object and can have tags', function () {
let object = new gd.SpriteObject('MySpriteObject');
const project = new gd.ProjectHelper.createNewGDJSProject();
let object = project.insertNewObject(project, 'Sprite', 'MySpriteObject');
expect(object instanceof gd.Object).toBe(true);
object.setTags('tag1, tag2, tag3');
expect(object.getTags()).toBe('tag1, tag2, tag3');
expect(object.getVariables()).toBeTruthy();
object.delete();
project.delete();
});
it('can have animations', function () {
let obj = new gd.SpriteObject('MySpriteObject');
let obj = new gd.SpriteObject();
obj.addAnimation(new gd.Animation());
obj.addAnimation(new gd.Animation());
expect(obj.getAnimationsCount()).toBe(2);
@@ -2659,7 +2653,7 @@ describe('libGD.js', function () {
});
it('can swap animations', function () {
let obj = new gd.SpriteObject('MySpriteObject');
let obj = new gd.SpriteObject();
obj.removeAllAnimations();
let anim1 = new gd.Animation();
let anim2 = new gd.Animation();

View File

@@ -618,17 +618,9 @@ describe('libGD.js - GDJS related tests', function () {
});
});
const testObjectFeatures = (object) => {
expect(object instanceof gd.Object).toBe(true);
object.setTags('tag1, tag2, tag3');
expect(object.getTags()).toBe('tag1, tag2, tag3');
expect(object.getVariables()).toBeTruthy();
};
describe('TextObject', function () {
it('should expose TextObject specific methods', function () {
var object = new gd.TextObject('MyTextObject');
testObjectFeatures(object);
object.setString('Hello');
object.setFontName('Hello.ttf');
object.setCharacterSize(10);
@@ -647,7 +639,6 @@ describe('libGD.js - GDJS related tests', function () {
describe('TiledSpriteObject', function () {
it('should expose TiledSpriteObject specific methods', function () {
var object = new gd.TiledSpriteObject('MyTiledSpriteObject');
testObjectFeatures(object);
object.setTexture('MyImageName');
expect(object.getTexture()).toBe('MyImageName');
});
@@ -655,7 +646,6 @@ describe('libGD.js - GDJS related tests', function () {
describe('PanelSpriteObject', function () {
it('should expose PanelSpriteObject specific methods', function () {
var object = new gd.PanelSpriteObject('MyPanelSpriteObject');
testObjectFeatures(object);
object.setTexture('MyImageName');
expect(object.getTexture()).toBe('MyImageName');
});
@@ -663,7 +653,6 @@ describe('libGD.js - GDJS related tests', function () {
describe('ShapePainterObject', function () {
it('should expose ShapePainterObject specific methods', function () {
var object = new gd.ShapePainterObject('MyShapePainterObject');
testObjectFeatures(object);
object.setCoordinatesAbsolute();
expect(object.areCoordinatesAbsolute()).toBe(true);
object.setCoordinatesRelative();
@@ -673,7 +662,6 @@ describe('libGD.js - GDJS related tests', function () {
describe('ShapePainterObject', function () {
it('should expose ShapePainterObject specific methods', function () {
var object = new gd.ShapePainterObject('MyShapePainterObject');
testObjectFeatures(object);
object.setClearBetweenFrames(true);
expect(object.isClearedBetweenFrames()).toBe(true);
object.setClearBetweenFrames(false);
@@ -683,13 +671,11 @@ describe('libGD.js - GDJS related tests', function () {
describe('TextEntryObject', function () {
it('should expose TextEntryObject', function () {
var object = new gd.TextEntryObject('MyTextEntryObject');
testObjectFeatures(object);
});
});
describe('ParticleEmitterObject', function () {
it('should expose ParticleEmitterObject', function () {
var object = new gd.ParticleEmitterObject('MyParticleEmitterObject');
testObjectFeatures(object);
});
});
describe('JsCodeEvent', function () {

View File

@@ -0,0 +1,194 @@
const path = require('path');
const {
makeFakeAbstractFileSystem,
} = require('../TestUtils/FakeAbstractFileSystem');
const initializeGDevelopJs = require('../../Binaries/embuild/GDevelop.js/libGD.js');
const { makeTestExtensions } = require('../TestUtils/TestExtensions');
describe('libGD.js - GDJS project serialization tests', function () {
let gd = null;
beforeAll((done) => {
initializeGDevelopJs().then((module) => {
gd = module;
makeTestExtensions(gd);
done();
});
});
it('should keep TextObject configuration after after a save and reload', function () {
const checkConfiguration = (project) => {
const layout = project.getLayout('Scene');
const object = layout.getObject('MyObject');
const configuration = gd.asTextObjectConfiguration(
object.getConfiguration()
);
expect(configuration.getString()).toBe('Hello');
};
const serializerElement = new gd.SerializerElement();
{
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(
project,
'TextObject::Text',
'MyObject',
0
);
const configuration = gd.asTextObjectConfiguration(
object.getConfiguration()
);
configuration.setString('Hello');
checkConfiguration(project);
project.serializeTo(serializerElement);
}
{
const project = gd.ProjectHelper.createNewGDJSProject();
project.unserializeFrom(serializerElement);
checkConfiguration(project);
}
});
it('should keep TiledSpriteObject configuration after a save and reload', function () {
const checkConfiguration = (project) => {
const layout = project.getLayout('Scene');
const object = layout.getObject('MyObject');
const configuration = gd.asTiledSpriteConfiguration(
object.getConfiguration()
);
expect(configuration.getTexture()).toBe('MyImageName');
};
const serializerElement = new gd.SerializerElement();
{
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(
project,
'TiledSpriteObject::TiledSprite',
'MyObject',
0
);
const configuration = gd.asTiledSpriteConfiguration(
object.getConfiguration()
);
configuration.setTexture('MyImageName');
checkConfiguration(project);
project.serializeTo(serializerElement);
}
{
const project = gd.ProjectHelper.createNewGDJSProject();
project.unserializeFrom(serializerElement);
checkConfiguration(project);
}
});
it('should keep PanelSpriteObject configuration after a save and reload', function () {
const checkConfiguration = (project) => {
const layout = project.getLayout('Scene');
const object = layout.getObject('MyObject');
const configuration = gd.asPanelSpriteConfiguration(
object.getConfiguration()
);
expect(configuration.getTexture()).toBe('MyImageName');
};
const serializerElement = new gd.SerializerElement();
{
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(
project,
'PanelSpriteObject::PanelSprite',
'MyObject',
0
);
const configuration = gd.asPanelSpriteConfiguration(
object.getConfiguration()
);
configuration.setTexture('MyImageName');
checkConfiguration(project);
project.serializeTo(serializerElement);
}
{
const project = gd.ProjectHelper.createNewGDJSProject();
project.unserializeFrom(serializerElement);
checkConfiguration(project);
}
});
it('should keep ShapePainterObject configuration after a save and reload', function () {
const checkConfiguration = (project) => {
const layout = project.getLayout('Scene');
const object = layout.getObject('MyObject');
const configuration = gd.asShapePainterConfiguration(
object.getConfiguration()
);
expect(configuration.areCoordinatesAbsolute()).toBe(true);
};
const serializerElement = new gd.SerializerElement();
{
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(
project,
'PrimitiveDrawing::Drawer',
'MyObject',
0
);
const configuration = gd.asShapePainterConfiguration(
object.getConfiguration()
);
// It's relative by default.
configuration.setCoordinatesAbsolute();
checkConfiguration(project);
project.serializeTo(serializerElement);
}
{
const project = gd.ProjectHelper.createNewGDJSProject();
project.unserializeFrom(serializerElement);
checkConfiguration(project);
}
});
it('should keep ParticleEmitterObject configuration after a save and reload', function () {
const checkConfiguration = (project) => {
const layout = project.getLayout('Scene');
const object = layout.getObject('MyObject');
const configuration = gd.asParticleEmitterConfiguration(
object.getConfiguration()
);
expect(configuration.getParticleAngle1()).toBe(45);
};
const serializerElement = new gd.SerializerElement();
{
const project = gd.ProjectHelper.createNewGDJSProject();
const layout = project.insertNewLayout('Scene', 0);
const object = layout.insertNewObject(
project,
'ParticleSystem::ParticleEmitter',
'MyObject',
0
);
const configuration = gd.asParticleEmitterConfiguration(
object.getConfiguration()
);
configuration.setParticleAngle1(45);
checkConfiguration(project);
project.serializeTo(serializerElement);
}
{
const project = gd.ProjectHelper.createNewGDJSProject();
project.unserializeFrom(serializerElement);
checkConfiguration(project);
}
});
});

View File

@@ -56,11 +56,7 @@ describe('libGD.js object serialization', function() {
describe('gd.Serializer', function() {
it('should serialize a Text Object', function() {
var obj = new gd.TextObject('testObject');
obj.setType('TextObject::Text');
obj.setName('testObject');
obj.setString('Text of the object, with 官话 characters');
obj.setTags('inventory, player');
obj.setAssetStoreId('1234');
var serializedObject = new gd.SerializerElement();
obj.serializeTo(serializedObject);
@@ -69,7 +65,7 @@ describe('libGD.js object serialization', function() {
obj.delete();
expect(jsonObject).toBe(
'{"assetStoreId":"1234","bold":false,"italic":false,"name":"testObject","smoothed":true,"tags":"inventory, player","type":"TextObject::Text","underlined":false,"variables":[],"effects":[],"behaviors":[],"string":"Text of the object, with 官话 characters","font":"","characterSize":20.0,"color":{"b":0,"g":0,"r":0}}'
'{"bold":false,"italic":false,"smoothed":true,"underlined":false,"string":"Text of the object, with 官话 characters","font":"","characterSize":20.0,"color":{"b":0,"g":0,"r":0}}'
);
});
});

View File

@@ -177,15 +177,16 @@ type ParticleEmitterObject_RendererType = 0 | 1 | 2`
` asJsCodeEvent(gdBaseEvent): gdJsCodeEvent;`,
` asPlatform(gdPlatform): gdPlatform;`,
'',
` asSpriteObject(gdObject): gdSpriteObject;`,
` asTiledSpriteObject(gdObject): gdTiledSpriteObject;`,
` asPanelSpriteObject(gdObject): gdPanelSpriteObject;`,
` asTextObject(gdObject): gdTextObject;`,
` asShapePainterObject(gdObject): gdShapePainterObject;`,
` asAdMobObject(gdObject): gdAdMobObject;`,
` asTextEntryObject(gdObject): gdTextEntryObject;`,
` asParticleEmitterObject(gdObject): gdParticleEmitterObject;`,
` asObjectJsImplementation(gdObject): gdObjectJsImplementation;`,
` asSpriteConfiguration(gdObjectConfiguration): gdSpriteObject;`,
` asTiledSpriteConfiguration(gdObjectConfiguration): gdTiledSpriteObject;`,
` asPanelSpriteConfiguration(gdObjectConfiguration): gdPanelSpriteObject;`,
` asTextObjectConfiguration(gdObjectConfiguration): gdTextObject;`,
` asShapePainterConfiguration(gdObjectConfiguration): gdShapePainterObject;`,
` asAdMobConfiguration(gdObjectConfiguration): gdAdMobObject;`,
` asTextEntryConfiguration(gdObjectConfiguration): gdTextEntryObject;`,
` asParticleEmitterConfiguration(gdObjectConfiguration): gdParticleEmitterObject;`,
` asObjectJsImplementation(gdObjectConfiguration): gdObjectJsImplementation;`,
` asCustomObjectConfiguration(gdObjectConfiguration): gdCustomObjectConfiguration;`,
'',
` asImageResource(gdResource): gdImageResource;`,
'',
@@ -249,14 +250,14 @@ type ParticleEmitterObject_RendererType = 0 | 1 | 2`
shell.sed(
'-i',
'declare class gdObjectJsImplementation {',
'declare class gdObjectJsImplementation extends gdObject {',
'declare class gdObjectJsImplementation extends gdObjectConfiguration {',
'types/gdobjectjsimplementation.js'
);
shell.sed(
'-i',
'declare class gdCustomObject {',
'declare class gdCustomObject extends gdObject {',
'types/gdcustomobject.js'
'declare class gdCustomObjectConfiguration {',
'declare class gdCustomObjectConfiguration extends gdObjectConfiguration {',
'types/gdcustomobjectconfiguration.js'
);
shell.sed(
'-i',
@@ -264,12 +265,6 @@ type ParticleEmitterObject_RendererType = 0 | 1 | 2`
'declare class gdBehaviorJsImplementation extends gdBehavior {',
'types/gdbehaviorjsimplementation.js'
);
shell.sed(
'-i',
'declare class gdCustomBehavior {',
'declare class gdCustomBehavior extends gdBehavior {',
'types/gdcustombehavior.js'
);
shell.sed(
'-i',
'declare class gdBehaviorSharedDataJsImplementation {',

View File

@@ -0,0 +1,11 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdCustomObjectConfiguration extends gdObjectConfiguration {
clone(): gdUniquePtrObjectConfiguration;
getChildObjectConfiguration(objectName: string): gdObjectConfiguration;
getProperties(): gdMapStringPropertyDescriptor;
updateProperty(name: string, value: string): boolean;
getInitialInstanceProperties(instance: gdInitialInstance, project: gdProject, scene: gdLayout): gdMapStringPropertyDescriptor;
updateInitialInstanceProperty(instance: gdInitialInstance, name: string, value: string, project: gdProject, scene: gdLayout): boolean;
delete(): void;
ptr: number;
};

View File

@@ -11,18 +11,6 @@ declare class gdEventsBasedObject extends gdObjectsContainer {
getPropertyDescriptors(): gdNamedPropertyDescriptorsList;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
insertNewObject(project: gdProject, type: string, name: string, pos: number): gdObject;
insertObject(obj: gdObject, pos: number): gdObject;
hasObjectNamed(name: string): boolean;
getObject(name: string): gdObject;
getObjectAt(pos: number): gdObject;
getObjectPosition(name: string): number;
removeObject(name: string): void;
swapObjects(first: number, second: number): void;
moveObject(oldIndex: number, newIndex: number): void;
moveObjectToAnotherContainer(name: string, newObjectsContainer: gdObjectsContainer, newPosition: number): void;
getObjectsCount(): number;
getObjectGroups(): gdObjectGroupsContainer;
static getPropertyActionName(propertyName: string): string;
static getPropertyConditionName(propertyName: string): string;
static getPropertyExpressionName(propertyName: string): string;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdObject {
constructor(name: string): void;
constructor(name: string, type: string, configuration: gdObjectConfiguration): void;
clone(): gdUniquePtrObject;
setName(name: string): void;
getName(): string;
@@ -10,11 +10,7 @@ declare class gdObject {
getType(): string;
setTags(tags: string): void;
getTags(): string;
getProperties(): gdMapStringPropertyDescriptor;
updateProperty(name: string, value: string): boolean;
getInitialInstanceProperties(instance: gdInitialInstance, project: gdProject, scene: gdLayout): gdMapStringPropertyDescriptor;
updateInitialInstanceProperty(instance: gdInitialInstance, name: string, value: string, project: gdProject, scene: gdLayout): boolean;
exposeResources(worker: gdArbitraryResourceWorker): void;
getConfiguration(): gdObjectConfiguration;
getVariables(): gdVariablesContainer;
getEffects(): gdEffectsContainer;
getAllBehaviorNames(): gdVectorString;

View File

@@ -0,0 +1,15 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdObjectConfiguration {
constructor(): void;
clone(): gdUniquePtrObjectConfiguration;
getType(): string;
getProperties(): gdMapStringPropertyDescriptor;
updateProperty(name: string, value: string): boolean;
getInitialInstanceProperties(instance: gdInitialInstance, project: gdProject, scene: gdLayout): gdMapStringPropertyDescriptor;
updateInitialInstanceProperty(instance: gdInitialInstance, name: string, value: string, project: gdProject, scene: gdLayout): boolean;
exposeResources(worker: gdArbitraryResourceWorker): void;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
delete(): void;
ptr: number;
};

View File

@@ -1,7 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdObjectJsImplementation extends gdObject {
declare class gdObjectJsImplementation extends gdObjectConfiguration {
constructor(): void;
clone(): gdUniquePtrObject;
clone(): gdUniquePtrObjectConfiguration;
getProperties(): gdMapStringPropertyDescriptor;
updateProperty(name: string, value: string): boolean;
getInitialInstanceProperties(instance: gdInitialInstance, project: gdProject, scene: gdLayout): gdMapStringPropertyDescriptor;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdPanelSpriteObject extends gdObject {
constructor(name: string): void;
declare class gdPanelSpriteObject extends gdObjectConfiguration {
constructor(): void;
getLeftMargin(): number;
setLeftMargin(newMargin: number): void;
getTopMargin(): number;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdParticleEmitterObject extends gdObject {
constructor(name: string): void;
declare class gdParticleEmitterObject extends gdObjectConfiguration {
constructor(): void;
setRendererType(type: ParticleEmitterObject_RendererType): void;
getRendererType(): ParticleEmitterObject_RendererType;
setParticleTexture(resourceName: string): void;

View File

@@ -16,7 +16,7 @@ declare class gdPlatformExtension {
addDependency(): gdDependencyMetadata;
addBehavior(name: string, fullname: string, defaultName: string, description: string, group: string, icon24x24: string, className: string, instance: gdBehavior, sharedDatasInstance: gdBehaviorsSharedData): gdBehaviorMetadata;
addEventsBasedBehavior(name: string, fullname: string, description: string, group: string, icon24x24: string): gdBehaviorMetadata;
addObject(name: string, fullname: string, description: string, icon24x24: string, instance: gdObject): gdObjectMetadata;
addObject(name: string, fullname: string, description: string, icon24x24: string, instance: gdObjectConfiguration): gdObjectMetadata;
addEventsBasedObject(name: string, fullname: string, description: string, icon24x24: string): gdObjectMetadata;
addEffect(name: string): gdEffectMetadata;
registerProperty(name: string): gdPropertyDescriptor;

View File

@@ -4,8 +4,8 @@ declare class gdProjectHelper {
static initializePlatforms(): void;
static sanityCheckBehaviorProperty(behavior: gdBehavior, propertyName: string, newValue: string): string;
static sanityCheckBehaviorsSharedDataProperty(behavior: gdBehaviorsSharedData, propertyName: string, newValue: string): string;
static sanityCheckObjectProperty(obj: gdObject, propertyName: string, newValue: string): string;
static sanityCheckObjectInitialInstanceProperty(obj: gdObject, propertyName: string, newValue: string): string;
static sanityCheckObjectProperty(configuration: gdObjectConfiguration, propertyName: string, newValue: string): string;
static sanityCheckObjectInitialInstanceProperty(configuration: gdObjectConfiguration, propertyName: string, newValue: string): string;
delete(): void;
ptr: number;
};

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdShapePainterObject extends gdObject {
constructor(name: string): void;
declare class gdShapePainterObject extends gdObjectConfiguration {
constructor(): void;
setCoordinatesAbsolute(): void;
setCoordinatesRelative(): void;
areCoordinatesAbsolute(): boolean;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdSpriteObject extends gdObject {
constructor(name: string): void;
declare class gdSpriteObject extends gdObjectConfiguration {
constructor(): void;
addAnimation(animation: gdAnimation): void;
getAnimation(index: number): gdAnimation;
getAnimationsCount(): number;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdTextEntryObject extends gdObject {
constructor(name: string): void;
declare class gdTextEntryObject extends gdObjectConfiguration {
constructor(): void;
delete(): void;
ptr: number;
};

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdTextObject extends gdObject {
constructor(name: string): void;
declare class gdTextObject extends gdObjectConfiguration {
constructor(): void;
setString(string: string): void;
getString(): string;
setCharacterSize(size: number): void;

View File

@@ -1,6 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdTiledSpriteObject extends gdObject {
constructor(name: string): void;
declare class gdTiledSpriteObject extends gdObjectConfiguration {
constructor(): void;
setTexture(texture: string): void;
getTexture(): string;
setWidth(width: number): void;

View File

@@ -0,0 +1,7 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdUniquePtrObjectConfiguration {
get(): gdObjectConfiguration;
release(): gdObjectConfiguration;
delete(): void;
ptr: number;
};

View File

@@ -16,7 +16,7 @@ declare class gdWholeProjectRefactorer {
static objectOrGroupRemovedInLayout(project: gdProject, layout: gdLayout, objectName: string, isObjectGroup: boolean, removeEventsAndGroups: boolean): void;
static objectOrGroupRenamedInEventsFunction(project: gdProject, eventsFunction: gdEventsFunction, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, oldName: string, newName: string, isObjectGroup: boolean): void;
static objectOrGroupRemovedInEventsFunction(project: gdProject, eventsFunction: gdEventsFunction, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, isObjectGroup: boolean, removeEventsAndGroups: boolean): void;
static objectOrGroupRenamedInEventsBasedObject(project: gdProject, eventsBasedObject: gdEventsBasedObject, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, oldName: string, newName: string, isObjectGroup: boolean): void;
static objectOrGroupRenamedInEventsBasedObject(project: gdProject, globalObjectsContainer: gdObjectsContainer, eventsBasedObject: gdEventsBasedObject, oldName: string, newName: string, isObjectGroup: boolean): void;
static objectOrGroupRemovedInEventsBasedObject(project: gdProject, eventsBasedObject: gdEventsBasedObject, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer, objectName: string, isObjectGroup: boolean, removeEventsAndGroups: boolean): void;
static globalObjectOrGroupRenamed(project: gdProject, oldName: string, newName: string, isObjectGroup: boolean): void;
static globalObjectOrGroupRemoved(project: gdProject, objectName: string, isObjectGroup: boolean, removeEventsAndGroups: boolean): void;

2728
GDevelop.js/types/libgdevelop.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -22,15 +22,16 @@ declare class libGDevelop {
asJsCodeEvent(gdBaseEvent): gdJsCodeEvent;
asPlatform(gdPlatform): gdPlatform;
asSpriteObject(gdObject): gdSpriteObject;
asTiledSpriteObject(gdObject): gdTiledSpriteObject;
asPanelSpriteObject(gdObject): gdPanelSpriteObject;
asTextObject(gdObject): gdTextObject;
asShapePainterObject(gdObject): gdShapePainterObject;
asAdMobObject(gdObject): gdAdMobObject;
asTextEntryObject(gdObject): gdTextEntryObject;
asParticleEmitterObject(gdObject): gdParticleEmitterObject;
asObjectJsImplementation(gdObject): gdObjectJsImplementation;
asSpriteConfiguration(gdObjectConfiguration): gdSpriteObject;
asTiledSpriteConfiguration(gdObjectConfiguration): gdTiledSpriteObject;
asPanelSpriteConfiguration(gdObjectConfiguration): gdPanelSpriteObject;
asTextObjectConfiguration(gdObjectConfiguration): gdTextObject;
asShapePainterConfiguration(gdObjectConfiguration): gdShapePainterObject;
asAdMobConfiguration(gdObjectConfiguration): gdAdMobObject;
asTextEntryConfiguration(gdObjectConfiguration): gdTextEntryObject;
asParticleEmitterConfiguration(gdObjectConfiguration): gdParticleEmitterObject;
asObjectJsImplementation(gdObjectConfiguration): gdObjectJsImplementation;
asCustomObjectConfiguration(gdObjectConfiguration): gdCustomObjectConfiguration;
asImageResource(gdResource): gdImageResource;
@@ -69,9 +70,12 @@ declare class libGDevelop {
BehaviorJsImplementation: Class<gdBehaviorJsImplementation>;
BehaviorsSharedData: Class<gdBehaviorsSharedData>;
BehaviorSharedDataJsImplementation: Class<gdBehaviorSharedDataJsImplementation>;
ObjectConfiguration: Class<gdObjectConfiguration>;
UniquePtrObjectConfiguration: Class<gdUniquePtrObjectConfiguration>;
gdObject: Class<gdObject>;
UniquePtrObject: Class<gdUniquePtrObject>;
ObjectJsImplementation: Class<gdObjectJsImplementation>;
CustomObjectConfiguration: Class<gdCustomObjectConfiguration>;
Layout: Class<gdLayout>;
ExternalEvents: Class<gdExternalEvents>;
ExternalLayout: Class<gdExternalLayout>;

View File

@@ -0,0 +1,91 @@
// @flow
import * as React from 'react';
import {
type Announcement,
listAllAnnouncements,
} from '../Utils/GDevelopServices/Announcement';
type AnnouncementsFeedState = {|
announcements: ?(Announcement[]),
error: ?Error,
fetchAnnouncements: () => void,
|};
export const AnnouncementsFeedContext = React.createContext<AnnouncementsFeedState>(
{
announcements: null,
error: null,
fetchAnnouncements: () => {},
}
);
type AnnouncementsFeedStateProviderProps = {|
children: React.Node,
|};
export const AnnouncementsFeedStateProvider = ({
children,
}: AnnouncementsFeedStateProviderProps) => {
const [announcements, setAnnouncements] = React.useState<?(Announcement[])>(
null
);
const [error, setError] = React.useState<?Error>(null);
const isLoading = React.useRef<boolean>(false);
const fetchAnnouncements = React.useCallback(
() => {
if (isLoading.current) return;
(async () => {
setError(null);
isLoading.current = true;
try {
const announcements = await listAllAnnouncements();
setAnnouncements(announcements);
} catch (error) {
console.error(
`Unable to load the announcements from the api:`,
error
);
setError(error);
}
isLoading.current = false;
})();
},
[isLoading]
);
// Preload the announcements when the app loads.
React.useEffect(
() => {
// Don't attempt to load again announcements if they
// were loaded already.
if (announcements || isLoading.current) return;
const timeoutId = setTimeout(() => {
console.info('Pre-fetching announcements from the api...');
fetchAnnouncements();
}, 1000);
return () => clearTimeout(timeoutId);
},
[fetchAnnouncements, announcements, isLoading]
);
const announcementsFeedState = React.useMemo(
() => ({
announcements,
error,
fetchAnnouncements,
}),
[announcements, error, fetchAnnouncements]
);
return (
<AnnouncementsFeedContext.Provider value={announcementsFeedState}>
{children}
</AnnouncementsFeedContext.Provider>
);
};

View File

@@ -0,0 +1,111 @@
// @flow
import { Trans } from '@lingui/macro';
import { I18n } from '@lingui/react';
import * as React from 'react';
import AlertMessage from '../UI/AlertMessage';
import { ColumnStackLayout } from '../UI/Layout';
import PlaceholderError from '../UI/PlaceholderError';
import PlaceholderLoader from '../UI/PlaceholderLoader';
import RaisedButton from '../UI/RaisedButton';
import { selectMessageByLocale } from '../Utils/i18n/MessageByLocale';
import Window from '../Utils/Window';
import { AnnouncementsFeedContext } from './AnnouncementsFeedContext';
import Text from '../UI/Text';
import { Line } from '../UI/Grid';
import PreferencesContext from '../MainFrame/Preferences/PreferencesContext';
import { MarkdownText } from '../UI/MarkdownText';
type AnnouncementsFeedProps = {|
level?: 'urgent' | 'normal',
canClose?: boolean,
addMargins?: boolean,
|};
export const AnnouncementsFeed = ({
level,
canClose,
addMargins,
}: AnnouncementsFeedProps) => {
const { announcements, error, fetchAnnouncements } = React.useContext(
AnnouncementsFeedContext
);
const { values, showAnnouncement } = React.useContext(PreferencesContext);
if (error) {
return (
<PlaceholderError onRetry={fetchAnnouncements}>
<Trans>
Can't load the announcements. Verify your internet connection or try
again later.
</Trans>
</PlaceholderError>
);
} else if (!announcements) {
return <PlaceholderLoader />;
}
const properLevelAnnouncements = level
? announcements.filter(announcement => announcement.level === level)
: announcements;
const displayedAnnouncements = canClose
? properLevelAnnouncements.filter(announcement => {
return !values.hiddenAnnouncements[announcement.id];
})
: properLevelAnnouncements;
if (!displayedAnnouncements.length) return null;
return (
<I18n>
{({ i18n }) => (
<Line noMargin={!addMargins}>
<ColumnStackLayout noMargin={!addMargins} expand>
{displayedAnnouncements.map(announcement => {
const { buttonLabelByLocale, buttonUrl } = announcement;
return (
<AlertMessage
kind={announcement.type === 'warning' ? 'warning' : 'info'}
renderRightButton={
buttonLabelByLocale && buttonUrl
? () => (
<RaisedButton
label={selectMessageByLocale(
i18n,
buttonLabelByLocale
)}
onClick={() => Window.openExternalURL(buttonUrl)}
/>
)
: null
}
onHide={
canClose
? () => {
showAnnouncement(announcement.id, false);
}
: null
}
>
<Text size="block-title">
<Trans>
{selectMessageByLocale(i18n, announcement.titleByLocale)}
</Trans>
</Text>
<MarkdownText
source={selectMessageByLocale(
i18n,
announcement.markdownMessageByLocale
)}
allowParagraphs={false}
/>
</AlertMessage>
);
})}
</ColumnStackLayout>
</Line>
)}
</I18n>
);
};

View File

@@ -183,7 +183,7 @@ export const addAssetToProject = async ({
const renamedResourcesMap = toNewGdMapStringString(resourceNewNames);
const resourcesRenamer = new gd.ResourcesRenamer(renamedResourcesMap);
renamedResourcesMap.delete();
object.exposeResources(resourcesRenamer);
object.getConfiguration().exposeResources(resourcesRenamer);
resourcesRenamer.delete();
objectAsset.customization.forEach(customization => {

View File

@@ -227,7 +227,7 @@ describe('InstallAsset', () => {
const object = layout.getObject('PlayerSpaceship');
const resourcesInUse = new gd.ResourcesInUseHelper();
object.exposeResources(resourcesInUse);
object.getConfiguration().exposeResources(resourcesInUse);
const objectResourceNames = resourcesInUse
.getAllImages()
.toNewVectorString()

View File

@@ -59,6 +59,7 @@ type ImageCardProps = {|
size: number,
resource: Resource,
onChoose: () => void,
removeThemeFilterAppliedOnBackground?: boolean,
imageStyle?: {|
width: number,
height: number,
@@ -70,12 +71,21 @@ const ImageCard = ({
onChoose,
size,
imageStyle,
removeThemeFilterAppliedOnBackground,
}: ImageCardProps) => {
return (
<ButtonBase onClick={onChoose} focusRipple>
<div style={{ ...styles.cardContainer, width: size, height: size }}>
<div style={{ ...styles.previewContainer, width: size, height: size }}>
<CheckeredBackground />
<CheckeredBackground
style={
removeThemeFilterAppliedOnBackground
? {
filter: 'unset',
}
: undefined
}
/>
<CorsAwareImage
key={resource.url}
style={{
@@ -148,6 +158,7 @@ export const ResourceCard = ({ resource, onChoose, size }: Props) => {
onChoose={onChoose}
size={size}
imageStyle={styles.previewIcon}
removeThemeFilterAppliedOnBackground // All svg are black and hard to see on background darken by the theme filter so we prefer using the raw checkered background that is quite light #nofilter
/>
);
case 'audio':

View File

@@ -69,6 +69,7 @@ export const create = (authentication: Authentication) => {
renderPreviewLauncher={(props, ref) => (
<BrowserS3PreviewLauncher {...props} ref={ref} />
)}
initialDialog={appArguments['initial-dialog']}
renderExportDialog={props => (
<ExportDialog
project={props.project}

View File

@@ -27,6 +27,7 @@ type Props = {|
type State = {|
editedObjectWithContext: ?ObjectWithContext,
editedObjectInitialTab: ?ObjectEditorTab,
selectedObjectNames: string[],
|};
export default class EventBasedObjectChildrenEditor extends React.Component<
@@ -38,6 +39,7 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
state = {
editedObjectWithContext: null,
editedObjectInitialTab: 'properties',
selectedObjectNames: [],
};
_onDeleteObject = (i18n: I18nType) => (
@@ -131,7 +133,6 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
if (object.getName() !== newName) {
gd.WholeProjectRefactorer.objectOrGroupRenamedInEventsBasedObject(
project,
eventsBasedObject,
globalObjectsContainer,
eventsBasedObject,
object.getName(),
@@ -161,6 +162,18 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
}
};
_onObjectSelected = (selectedObjectName: string) => {
if (!selectedObjectName) {
this.setState({
selectedObjectNames: [],
});
} else {
this.setState({
selectedObjectNames: [selectedObjectName],
});
}
};
updateBehaviorsSharedData = () => {
// TODO EBO Decide how BehaviorsSharedData of child-objects should work.
// - Use a shared data per object instance
@@ -181,6 +194,11 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
render() {
const { eventsBasedObject, project } = this.props;
// TODO EBO When adding an object, filter the object types to excludes
// object that depend (transitively) on this object to avoid cycles.
// TODO EBO Add a button icon to mark some objects solid or not.
return (
<I18n>
{({ i18n }) => (
@@ -197,7 +215,7 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
resourceSources={[]}
resourceExternalEditors={[]}
onChooseResource={() => Promise.resolve([])}
selectedObjectNames={[]}
selectedObjectNames={this.state.selectedObjectNames}
onEditObject={this.editObject}
onDeleteObject={this._onDeleteObject(i18n)}
canRenameObject={newName =>
@@ -205,8 +223,7 @@ export default class EventBasedObjectChildrenEditor extends React.Component<
}
// Nothing special to do.
onObjectCreated={() => {}}
// Object selection has no impact.
onObjectSelected={() => {}}
onObjectSelected={this._onObjectSelected}
onRenameObject={(objectWithContext, newName, done) =>
this._onRenameObject(objectWithContext, newName, done, i18n)
}

View File

@@ -15,6 +15,7 @@ import Text from '../../UI/Text';
import { ExtensionOptionsEditor } from './ExtensionOptionsEditor';
import { Tab, Tabs } from '../../UI/Tabs';
import { ExtensionDependenciesEditor } from './ExtensionDependenciesEditor';
import { LineStackLayout } from '../../UI/Layout';
const exportExtension = (
eventsFunctionsExtensionsState: EventsFunctionsExtensionsState,
@@ -36,31 +37,8 @@ const exportExtension = (
};
const openGitHubIssue = () => {
const body = `
**⚠️ Please edit and complete this before submitting your extension:**
## Describe the extension
A clear and concise description of what the extension is, how useful it is.
## Checklist
- [ ] Extension has a proper name and description.
- [ ] Extension has tags (for example: "platform, brick, breakable").
- [ ] All behaviors have a description.
- [ ] All functions (actions, conditions, expressions) have descriptions.
- [ ] I confirm that this extension can be intergrated to this GitHub repository, distributed and MIT licensed.
## Extension file
Finally, attach the .json file of your extension here.
You also may have to create an account on GitHub before posting.
If your extension is high quality and useful, it will be added to the list of GDevelop community extensions.
When you're ready, remove this last paragraph and click on "Submit new issue". Thanks 🙌`;
Window.openExternalURL(
`https://github.com/4ian/GDevelop-extensions/issues/new?body=${encodeURIComponent(
body
)}&title=New%20extension`
'https://github.com/GDevelopApp/GDevelop-extensions/issues/new?assignees=&labels=%E2%9C%A8+New+extension&template=new-extension.yml&title=New+extension%3A+%3Ctitle%3E'
);
};
@@ -166,7 +144,7 @@ export default function OptionsEditorDialog({
</Trans>
</Text>
</Line>
<Line>
<LineStackLayout>
<RaisedButton
icon={<CloudUpload />}
primary
@@ -182,7 +160,7 @@ export default function OptionsEditorDialog({
label={<Trans>Submit extension to the community</Trans>}
onClick={openGitHubIssue}
/>
</Line>
</LineStackLayout>
</Column>
</Dialog>
)}

View File

@@ -1410,15 +1410,21 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
)
}
initialNodes={
isDev !==
// "objects-list" must only appear in dev mode.
isDev ===
mosaicContainsNode(
initialMosaicEditorNodes,
getDefaultEditorMosaicNode(
'events-functions-extension-editor'
) || initialMosaicEditorNodes,
'objects-list'
)
? getDefaultEditorMosaicNode(
'events-functions-extension-editor'
) || initialMosaicEditorNodes
: initialMosaicEditorNodes
: // Force the mosaic to reset to default.
// It contains "objects-list" only
// in dev mode.
initialMosaicEditorNodes
}
/>
)}

View File

@@ -47,10 +47,12 @@ export default class ObjectAnimationNameField extends Component<
}
if (object.getType() === 'Sprite') {
const spriteObject = gd.asSpriteObject(object);
const spriteConfiguration = gd.asSpriteConfiguration(
object.getConfiguration()
);
return mapFor(0, spriteObject.getAnimationsCount(), index => {
const animationName = spriteObject.getAnimation(index).getName();
return mapFor(0, spriteConfiguration.getAnimationsCount(), index => {
const animationName = spriteConfiguration.getAnimation(index).getName();
return animationName.length > 0 ? animationName : null;
})
.filter(Boolean)

View File

@@ -47,9 +47,11 @@ export default class ObjectPointNameField extends Component<
}
if (object.getType() === 'Sprite') {
const spriteObject = gd.asSpriteObject(object);
const spriteConfiguration = gd.asSpriteConfiguration(
object.getConfiguration()
);
return getAllPointNames(spriteObject)
return getAllPointNames(spriteConfiguration)
.map(spriteObjectName =>
spriteObjectName.length > 0 ? spriteObjectName : null
)

View File

@@ -163,7 +163,11 @@ export const browserOnlineCordovaExportPipeline: ExportPipeline<
exportState: ExportState,
authenticatedUser: AuthenticatedUser,
uploadBucketKey: string,
gameId: string
gameId: string,
options: {|
gameName: string,
gameVersion: string,
|}
): Promise<Build> => {
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
if (!firebaseUser)
@@ -175,7 +179,8 @@ export const browserOnlineCordovaExportPipeline: ExportPipeline<
uploadBucketKey,
exportState.targets,
exportState.keystore,
gameId
gameId,
options
);
},
};

View File

@@ -161,7 +161,11 @@ export const browserOnlineElectronExportPipeline: ExportPipeline<
exportState: ExportState,
authenticatedUser: AuthenticatedUser,
uploadBucketKey: string,
gameId: string
gameId: string,
options: {|
gameName: string,
gameVersion: string,
|}
): Promise<Build> => {
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
if (!firebaseUser)
@@ -172,7 +176,8 @@ export const browserOnlineElectronExportPipeline: ExportPipeline<
firebaseUser.uid,
uploadBucketKey,
exportState.targets,
gameId
gameId,
options
);
},
};

View File

@@ -172,7 +172,11 @@ export const browserOnlineWebExportPipeline: ExportPipeline<
exportState: ExportState,
authenticatedUser: AuthenticatedUser,
uploadBucketKey: string,
gameId: string
gameId: string,
options: {|
gameName: string,
gameVersion: string,
|}
): Promise<Build> => {
const { getAuthorizationHeader, firebaseUser } = authenticatedUser;
if (!firebaseUser)
@@ -182,7 +186,8 @@ export const browserOnlineWebExportPipeline: ExportPipeline<
getAuthorizationHeader,
firebaseUser.uid,
uploadBucketKey,
gameId
gameId,
options
);
},
};

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