mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
35 Commits
v5.0.0-bet
...
v5.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
07f26027f6 | ||
![]() |
09cf13d6e2 | ||
![]() |
42f91565fa | ||
![]() |
ac6c146808 | ||
![]() |
59ad23f8ac | ||
![]() |
769c6fe3d5 | ||
![]() |
07b92911ab | ||
![]() |
eb57bcfc87 | ||
![]() |
7addeba73a | ||
![]() |
263902b45a | ||
![]() |
d283f759fe | ||
![]() |
fabd028a63 | ||
![]() |
2b18272c41 | ||
![]() |
a1fb39da3d | ||
![]() |
09602fdf9e | ||
![]() |
d574ef17ba | ||
![]() |
0e3f70627b | ||
![]() |
a814a07105 | ||
![]() |
c49af90a9c | ||
![]() |
24afa155c8 | ||
![]() |
9e5a431516 | ||
![]() |
182a94285c | ||
![]() |
442c2c8dd9 | ||
![]() |
f8fd0dd353 | ||
![]() |
faad9e23ac | ||
![]() |
d2af0da1b1 | ||
![]() |
c65e5c3e49 | ||
![]() |
24a8dfc5f0 | ||
![]() |
9c6790ac37 | ||
![]() |
18ef7460ba | ||
![]() |
63cd0e76c3 | ||
![]() |
bdbf7fd9fc | ||
![]() |
6bbedbd8f9 | ||
![]() |
648bd1ff2e | ||
![]() |
d4288caedb |
64
.vscode/c_cpp_properties.json
vendored
64
.vscode/c_cpp_properties.json
vendored
@@ -10,29 +10,29 @@
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"/usr/local/lib/wx/include/osx_cocoa-unicode-3.0",
|
||||
"/usr/local/include/wx-3.0",
|
||||
"/usr/include/machine",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
|
||||
"/usr/include"
|
||||
"/usr/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\" \"",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1",
|
||||
"/usr/local/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/8.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include",
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",
|
||||
"/usr/include",
|
||||
"${workspaceRoot}"
|
||||
@@ -40,7 +40,6 @@
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
@@ -50,10 +49,27 @@
|
||||
"name": "Linux",
|
||||
"includePath": [
|
||||
"${workspaceRoot}",
|
||||
"${workspaceRoot}/IDE",
|
||||
"${workspaceRoot}/GDCpp",
|
||||
"${workspaceRoot}/GDJS",
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"/usr/include",
|
||||
"/usr/local/include"
|
||||
"/usr/local/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [],
|
||||
"defines": [
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "clang-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"/usr/include",
|
||||
@@ -62,19 +78,34 @@
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "clang-x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceRoot}",
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include"
|
||||
"${workspaceRoot}/IDE",
|
||||
"${workspaceRoot}/GDCpp",
|
||||
"${workspaceRoot}/GDJS",
|
||||
"${workspaceRoot}/Extensions",
|
||||
"${workspaceRoot}/Core",
|
||||
"${workspaceRoot}/ExtLibs/SFML/include",
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include",
|
||||
"${workspaceRoot}"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE"
|
||||
"UNICODE",
|
||||
"GD_IDE_ONLY",
|
||||
"__WXMAC__",
|
||||
"__WXOSX__",
|
||||
"__WXOSX_COCOA__",
|
||||
"GD_CORE_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"GD_EXTENSION_API=\/* Macro used to export classes on Windows, please ignore *\/",
|
||||
"WXUSINGDLL"
|
||||
],
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"browse": {
|
||||
"path": [
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*",
|
||||
@@ -82,8 +113,7 @@
|
||||
],
|
||||
"limitSymbolsToIncludedHeaders": true,
|
||||
"databaseFilename": ""
|
||||
},
|
||||
"intelliSenseMode": "msvc-x64"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -70,7 +70,8 @@
|
||||
"ratio": "cpp",
|
||||
"atomic": "cpp",
|
||||
"locale": "cpp",
|
||||
"string_view": "cpp"
|
||||
"string_view": "cpp",
|
||||
"__string": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -2,7 +2,9 @@
|
||||
Version=1.0
|
||||
Name=GDevelop
|
||||
GenericName=Game creator IDE
|
||||
GenericName[de]=Entwicklungsumgebung für Spiele
|
||||
Comment=HTML5 and native game development software
|
||||
Comment[de]=Entwicklungsumgebung für native und HTML5-Spiele
|
||||
Exec=sh -c "gdevelop %F"
|
||||
MimeType=application/x-gdevelop-project;
|
||||
Icon=GDevelop
|
||||
|
@@ -945,7 +945,7 @@ void ResourcesEditor::Refresh()
|
||||
gd::ResourceFolder & folder = project.GetResourcesManager().GetFolder(folders[i]);
|
||||
wxTreeItemId folderItem = resourcesTree->AppendItem( resourcesTree->GetRootItem(), folders[i], -1, -1, new gd::TreeItemStringData("Folder", folders[i] ));
|
||||
|
||||
std::vector<gd::String> resources = folder.GetAllResourcesList();
|
||||
std::vector<gd::String> resources = folder.GetAllResourceNames();
|
||||
for (std::size_t j=0;j<resources.size();++j)
|
||||
{
|
||||
gd::Resource & resource = folder.GetResource(resources[j]);
|
||||
@@ -959,7 +959,7 @@ void ResourcesEditor::Refresh()
|
||||
|
||||
//All images
|
||||
allImagesItem = resourcesTree->AppendItem( resourcesTree->GetRootItem(), _("All images"), -1,-1, new gd::TreeItemStringData("BaseFolder", "" ));
|
||||
std::vector<gd::String> resources = project.GetResourcesManager().GetAllResourcesList();
|
||||
std::vector<gd::String> resources = project.GetResourcesManager().GetAllResourceNames();
|
||||
for ( std::size_t i = 0;i <resources.size();i++ )
|
||||
{
|
||||
gd::Resource & resource = project.GetResourcesManager().GetResource(resources[i]);
|
||||
|
@@ -51,7 +51,7 @@ void ArbitraryResourceWorker::ExposeResources(gd::ResourcesManager * resourcesMa
|
||||
|
||||
resourcesManagers.push_back(resourcesManager);
|
||||
|
||||
std::vector<gd::String> resources = resourcesManager->GetAllResourcesList();
|
||||
std::vector<gd::String> resources = resourcesManager->GetAllResourceNames();
|
||||
for ( std::size_t i = 0;i < resources.size() ;i++ )
|
||||
{
|
||||
if ( resourcesManager->GetResource(resources[i]).UseFile() )
|
||||
|
@@ -45,7 +45,7 @@ std::vector<gd::String> ProjectResourcesAdder::GetAllUselessImages(gd::Project &
|
||||
std::set<gd::String> & usedImages = inventorizer.GetAllUsedImages();
|
||||
|
||||
//Search all images resources not used
|
||||
std::vector<gd::String> resources = project.GetResourcesManager().GetAllResourcesList();
|
||||
std::vector<gd::String> resources = project.GetResourcesManager().GetAllResourceNames();
|
||||
for (std::size_t i = 0;i < resources.size();i++)
|
||||
{
|
||||
if (project.GetResourcesManager().GetResource(resources[i]).GetKind() != "image")
|
||||
|
@@ -45,7 +45,7 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(gd::Project & originalProject, A
|
||||
#endif
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
|
||||
std::cout << "Copying all ressources from " << projectDirectory << " to " << destinationDirectory;
|
||||
std::cout << "Copying all ressources from " << projectDirectory << " to " << destinationDirectory << "..." << std::endl;
|
||||
|
||||
//Get the resources to be copied
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(fs);
|
||||
|
@@ -68,7 +68,7 @@ public:
|
||||
* Usage example:
|
||||
\code
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[ToString(_("Initial speed"))].SetValue("5");
|
||||
properties[_("Initial speed")].SetValue(gd::String::From(initialSpeed));
|
||||
|
||||
return properties;
|
||||
\endcode
|
||||
|
@@ -5,6 +5,10 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/BehaviorsSharedData.h"
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include <map>
|
||||
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
|
||||
#endif
|
||||
|
||||
namespace gd
|
||||
{
|
||||
@@ -13,4 +17,12 @@ BehaviorsSharedData::~BehaviorsSharedData()
|
||||
{
|
||||
};
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> BehaviorsSharedData::GetProperties(gd::Project & project) const
|
||||
{
|
||||
std::map<gd::String, gd::PropertyDescriptor> nothing;
|
||||
return nothing;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@@ -8,9 +8,13 @@
|
||||
#define BEHAVIORSSHAREDDATA_H
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include "GDCore/String.h"
|
||||
class BehaviorsRuntimeSharedData;
|
||||
namespace gd { class SerializerElement; }
|
||||
namespace gd { class PropertyDescriptor; }
|
||||
namespace gd { class Project; }
|
||||
namespace gd { class Layout; }
|
||||
|
||||
namespace gd
|
||||
{
|
||||
@@ -52,6 +56,31 @@ public:
|
||||
virtual void SetTypeName(const gd::String & type_) { type = type_; };
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to know about the properties of the shared data.
|
||||
*
|
||||
* Usage example:
|
||||
\code
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Initial speed")].SetValue(gd::String::From(initialSpeed));
|
||||
|
||||
return properties;
|
||||
\endcode
|
||||
*
|
||||
* \return a std::map with properties names as key.
|
||||
* \see gd::PropertyDescriptor
|
||||
*/
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(gd::Project & project) const;
|
||||
|
||||
/**
|
||||
* \brief Called when the IDE wants to update a property of the shared data
|
||||
*
|
||||
* \return false if the new value cannot be set
|
||||
* \see gd::InitialInstance
|
||||
*/
|
||||
virtual bool UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project) {return false;};
|
||||
|
||||
/**
|
||||
* \brief Serialize behaviors shared data.
|
||||
*/
|
||||
|
@@ -141,7 +141,7 @@ void ImageManager::LoadPermanentImages()
|
||||
//so as not to unload images that could be still present.
|
||||
std::map < gd::String, std::shared_ptr<SFMLTextureWrapper> > newPermanentlyLoadedImages;
|
||||
|
||||
std::vector<gd::String> resources = resourcesManager->GetAllResourcesList();
|
||||
std::vector<gd::String> resources = resourcesManager->GetAllResourceNames();
|
||||
for ( std::size_t i = 0;i <resources.size();i++ )
|
||||
{
|
||||
try
|
||||
|
@@ -29,6 +29,7 @@ namespace gd
|
||||
{
|
||||
|
||||
gd::Layer Layout::badLayer;
|
||||
gd::BehaviorsSharedData Layout::badBehaviorSharedData;
|
||||
|
||||
Layout::Layout(const Layout & other)
|
||||
{
|
||||
@@ -74,6 +75,53 @@ void Layout::SetName(const gd::String & name_)
|
||||
mangledName = gd::SceneNameMangler::GetMangledSceneName(name);
|
||||
};
|
||||
|
||||
bool Layout::HasBehaviorSharedData(const gd::String & behaviorName)
|
||||
{
|
||||
return behaviorsInitialSharedDatas.find(behaviorName) != behaviorsInitialSharedDatas.end();
|
||||
}
|
||||
|
||||
std::vector <gd::String> Layout::GetAllBehaviorSharedDataNames() const
|
||||
{
|
||||
std::vector < gd::String > allNames;
|
||||
|
||||
for (auto & it : behaviorsInitialSharedDatas)
|
||||
allNames.push_back(it.first);
|
||||
|
||||
return allNames;
|
||||
}
|
||||
|
||||
const gd::BehaviorsSharedData & Layout::GetBehaviorSharedData(const gd::String & behaviorName) const
|
||||
{
|
||||
auto it = behaviorsInitialSharedDatas.find(behaviorName);
|
||||
if (it != behaviorsInitialSharedDatas.end())
|
||||
return *it->second;
|
||||
|
||||
return badBehaviorSharedData;
|
||||
}
|
||||
|
||||
gd::BehaviorsSharedData & Layout::GetBehaviorSharedData(const gd::String & behaviorName)
|
||||
{
|
||||
auto it = behaviorsInitialSharedDatas.find(behaviorName);
|
||||
if (it != behaviorsInitialSharedDatas.end())
|
||||
return *it->second;
|
||||
|
||||
return badBehaviorSharedData;
|
||||
}
|
||||
|
||||
std::shared_ptr<gd::BehaviorsSharedData> Layout::GetBehaviorSharedDataSmartPtr(const gd::String & behaviorName)
|
||||
{
|
||||
auto it = behaviorsInitialSharedDatas.find(behaviorName);
|
||||
if (it != behaviorsInitialSharedDatas.end())
|
||||
return it->second;
|
||||
|
||||
return std::shared_ptr<gd::BehaviorsSharedData>();
|
||||
}
|
||||
|
||||
const std::map < gd::String, std::shared_ptr<gd::BehaviorsSharedData> > & Layout::GetAllBehaviorSharedData() const
|
||||
{
|
||||
return behaviorsInitialSharedDatas;
|
||||
}
|
||||
|
||||
gd::Layer & Layout::GetLayer(const gd::String & name)
|
||||
{
|
||||
std::vector<gd::Layer>::iterator layer = find_if(initialLayers.begin(), initialLayers.end(), bind2nd(gd::LayerHasName(), name));
|
||||
@@ -83,6 +131,7 @@ gd::Layer & Layout::GetLayer(const gd::String & name)
|
||||
|
||||
return badLayer;
|
||||
}
|
||||
|
||||
const gd::Layer & Layout::GetLayer(const gd::String & name) const
|
||||
{
|
||||
std::vector<gd::Layer>::const_iterator layer = find_if(initialLayers.begin(), initialLayers.end(), bind2nd(gd::LayerHasName(), name));
|
||||
@@ -92,14 +141,17 @@ const gd::Layer & Layout::GetLayer(const gd::String & name) const
|
||||
|
||||
return badLayer;
|
||||
}
|
||||
|
||||
gd::Layer & Layout::GetLayer(std::size_t index)
|
||||
{
|
||||
return initialLayers[index];
|
||||
}
|
||||
|
||||
const gd::Layer & Layout::GetLayer (std::size_t index) const
|
||||
{
|
||||
return initialLayers[index];
|
||||
}
|
||||
|
||||
std::size_t Layout::GetLayersCount() const
|
||||
{
|
||||
return initialLayers.size();
|
||||
|
@@ -172,37 +172,37 @@ public:
|
||||
|
||||
/** \name Layout layers management
|
||||
* Members functions related to layout layers management.
|
||||
* TODO: This should be moved to a separate class
|
||||
* TODO: This could be moved to a separate class
|
||||
*/
|
||||
///@{
|
||||
|
||||
/**
|
||||
* Must return true if the layer called "name" exists.
|
||||
* \brief Return true if the layer called "name" exists.
|
||||
*/
|
||||
bool HasLayerNamed(const gd::String & name) const;
|
||||
|
||||
/**
|
||||
* Must return a reference to the layer called "name".
|
||||
* \brief Return a reference to the layer called "name".
|
||||
*/
|
||||
Layer & GetLayer(const gd::String & name);
|
||||
|
||||
/**
|
||||
* Must return a reference to the layer called "name".
|
||||
* \brief Return a reference to the layer called "name".
|
||||
*/
|
||||
const Layer & GetLayer(const gd::String & name) const;
|
||||
|
||||
/**
|
||||
* Must return a reference to the layer at position "index" in the layers list
|
||||
* \brief Return a reference to the layer at position "index" in the layers list
|
||||
*/
|
||||
Layer & GetLayer(std::size_t index);
|
||||
|
||||
/**
|
||||
* Must return a reference to the layer at position "index" in the layers list
|
||||
* \brief Return a reference to the layer at position "index" in the layers list
|
||||
*/
|
||||
const Layer & GetLayer (std::size_t index) const;
|
||||
const Layer & GetLayer(std::size_t index) const;
|
||||
|
||||
/**
|
||||
* Must return the position of the layer called "name" in the layers list
|
||||
* \brief Return the position of the layer called "name" in the layers list
|
||||
*/
|
||||
std::size_t GetLayerPosition(const gd::String & name) const;
|
||||
|
||||
@@ -253,15 +253,45 @@ public:
|
||||
///@}
|
||||
|
||||
/**
|
||||
* Make sure that the scene had an instance of shared data for
|
||||
* This ensures that the scene had an instance of shared data for
|
||||
* every behavior of every object that can be used on the scene
|
||||
* ( i.e. the objects of the scene and the global objects )
|
||||
* (i.e. the objects of the scene and the global objects)
|
||||
*
|
||||
* Must be called when a behavior have been added/deleted
|
||||
* or when a scene have been added to a project.
|
||||
*/
|
||||
void UpdateBehaviorsSharedData(gd::Project & project);
|
||||
|
||||
/**
|
||||
* \brief Get the names of all shared data stored for behaviors
|
||||
*/
|
||||
std::vector <gd::String> GetAllBehaviorSharedDataNames() const;
|
||||
|
||||
/**
|
||||
* \brief Check if shared data are stored for a behavior
|
||||
*/
|
||||
bool HasBehaviorSharedData(const gd::String & behaviorName);
|
||||
|
||||
/**
|
||||
* \brief Get the shared data stored for a behavior
|
||||
*/
|
||||
const gd::BehaviorsSharedData & GetBehaviorSharedData(const gd::String & behaviorName) const;
|
||||
|
||||
/**
|
||||
* \brief Get the shared data stored for a behavior
|
||||
*/
|
||||
gd::BehaviorsSharedData & GetBehaviorSharedData(const gd::String & behaviorName);
|
||||
|
||||
/**
|
||||
* \brief Get a map of all shared data stored for behaviors
|
||||
*/
|
||||
const std::map < gd::String, std::shared_ptr<gd::BehaviorsSharedData> > & GetAllBehaviorSharedData() const;
|
||||
|
||||
/**
|
||||
* \brief Get the (smart pointer to the) shared data stored for a behavior.
|
||||
*/
|
||||
std::shared_ptr<gd::BehaviorsSharedData> GetBehaviorSharedDataSmartPtr(const gd::String & behaviorName);
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* Return the settings associated to the layout.
|
||||
@@ -357,9 +387,6 @@ public:
|
||||
void UnserializeFrom(gd::Project & project, const SerializerElement & element);
|
||||
///@}
|
||||
|
||||
//TODO: Send this to private part.
|
||||
std::map < gd::String, std::shared_ptr<gd::BehaviorsSharedData> > behaviorsInitialSharedDatas; ///< Initial shared datas of behaviors
|
||||
|
||||
//TODO: GD C++ Platform specific code below
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/** \name Events compilation and bitcode management
|
||||
@@ -442,6 +469,7 @@ private:
|
||||
gd::InitialInstancesContainer initialInstances; ///< Initial instances
|
||||
std::vector < gd::Layer > initialLayers; ///< Initial layers
|
||||
ObjectGroupsContainer objectGroups; ///< Objects groups
|
||||
std::map < gd::String, std::shared_ptr<gd::BehaviorsSharedData> > behaviorsInitialSharedDatas; ///< Initial shared datas of behaviors
|
||||
bool stopSoundsOnStartup; ///< True to make the scene stop all sounds at startup.
|
||||
bool standardSortMethod; ///< True to sort objects using standard sort.
|
||||
float oglFOV; ///< OpenGL Field Of View value
|
||||
@@ -449,6 +477,7 @@ private:
|
||||
float oglZFar; ///< OpenGL Far Z position
|
||||
bool disableInputWhenNotFocused; /// If set to true, the input must be disabled when the window do not have the focus.
|
||||
static gd::Layer badLayer; ///< Null object, returned when GetLayer can not find an appropriate layer.
|
||||
static gd::BehaviorsSharedData badBehaviorSharedData; ///< Null object, returned when GetBehaviorSharedData can not find the specified behavior shared data.
|
||||
#if defined(GD_IDE_ONLY)
|
||||
EventsList events; ///< Scene events
|
||||
gd::LayoutEditorCanvasOptions associatedSettings;
|
||||
|
58
Core/GDCore/Project/PlatformSpecificAssets.cpp
Normal file
58
Core/GDCore/Project/PlatformSpecificAssets.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "PlatformSpecificAssets.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
gd::String PlatformSpecificAssets::badStr;
|
||||
|
||||
bool PlatformSpecificAssets::Has(const gd::String& platform, const gd::String& name) const
|
||||
{
|
||||
return assets.find(platform + "-" + name) != assets.end();
|
||||
}
|
||||
|
||||
const gd::String& PlatformSpecificAssets::Get(const gd::String& platform, const gd::String& name) const
|
||||
{
|
||||
const auto & it = assets.find(platform + "-" + name);
|
||||
return it != assets.end() ? it->second : badStr;
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::Remove(const gd::String& platform, const gd::String& name)
|
||||
{
|
||||
assets.erase(platform + "-" + name);
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::Set(const gd::String& platform, const gd::String& name, const gd::String& resourceName)
|
||||
{
|
||||
assets[platform + "-" + name] = resourceName;
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::SerializeTo(SerializerElement& element) const
|
||||
{
|
||||
for (auto& it : assets) {
|
||||
element.AddChild(it.first).SetValue(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::UnserializeFrom(const SerializerElement& element)
|
||||
{
|
||||
assets.clear();
|
||||
|
||||
for (auto& child : element.GetAllChildren()) {
|
||||
assets[child.first] = child.second->GetValue().GetString();
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformSpecificAssets::ExposeResources(gd::ArbitraryResourceWorker & worker)
|
||||
{
|
||||
for (auto& it : assets) {
|
||||
worker.ExposeImage(it.second);
|
||||
}
|
||||
}
|
||||
}
|
76
Core/GDCore/Project/PlatformSpecificAssets.h
Normal file
76
Core/GDCore/Project/PlatformSpecificAssets.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2018 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#ifndef GDCORE_PLATFORMASSETS_H
|
||||
#define GDCORE_PLATFORMASSETS_H
|
||||
#include "GDCore/String.h"
|
||||
#include <map>
|
||||
namespace gd {
|
||||
class SerializerElement;
|
||||
}
|
||||
namespace gd {
|
||||
class ArbitraryResourceWorker;
|
||||
}
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Store the icons, splashscreens or reference to any other asset
|
||||
* that can be needed when exporting the game.
|
||||
*
|
||||
* \see gd::Project
|
||||
*
|
||||
* \ingroup PlatformDefinition
|
||||
*/
|
||||
class GD_CORE_API PlatformSpecificAssets {
|
||||
public:
|
||||
PlatformSpecificAssets(){};
|
||||
virtual ~PlatformSpecificAssets(){};
|
||||
|
||||
/**
|
||||
* \brief Return true if the specified asset exists.
|
||||
*/
|
||||
bool Has(const gd::String& platform, const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Get the specified asset resource name.
|
||||
*/
|
||||
const gd::String& Get(const gd::String& platform, const gd::String& name) const;
|
||||
|
||||
/**
|
||||
* \brief Remove the specified asset.
|
||||
*/
|
||||
void Remove(const gd::String& platform, const gd::String& name);
|
||||
|
||||
/**
|
||||
* \brief Remove the specified asset.
|
||||
*/
|
||||
void Set(const gd::String& platform, const gd::String& name, const gd::String& resourceName);
|
||||
|
||||
void ExposeResources(gd::ArbitraryResourceWorker & worker);
|
||||
|
||||
/** \name Saving and loading
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* \brief Serialize objects groups container.
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Unserialize the objects groups container.
|
||||
*/
|
||||
void UnserializeFrom(const SerializerElement& element);
|
||||
///@}
|
||||
|
||||
private:
|
||||
std::map<gd::String, gd::String> assets;
|
||||
|
||||
static gd::String badStr;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GDCORE_PLATFORMASSETS_H
|
@@ -61,6 +61,7 @@ Project::Project() :
|
||||
#if defined(GD_IDE_ONLY)
|
||||
name(_("Project")),
|
||||
packageName("com.example.gamename"),
|
||||
orientation("landscape"),
|
||||
folderProject(false),
|
||||
#endif
|
||||
windowWidth(800),
|
||||
@@ -531,9 +532,11 @@ void Project::UnserializeFrom(const SerializerElement & element)
|
||||
#if defined(GD_IDE_ONLY)
|
||||
SetAuthor(propElement.GetChild("author", 0, "Auteur").GetValue().GetString());
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetProjectFile(propElement.GetStringAttribute("projectFile"));
|
||||
SetLastCompilationDirectory(propElement.GetChild("latestCompilationDirectory", 0, "LatestCompilationDirectory").GetValue().GetString());
|
||||
platformSpecificAssets.UnserializeFrom(propElement.GetChild("platformSpecificAssets"));
|
||||
winExecutableFilename = propElement.GetStringAttribute("winExecutableFilename");
|
||||
winExecutableIconFile = propElement.GetStringAttribute("winExecutableIconFile");
|
||||
linuxExecutableFilename = propElement.GetStringAttribute("linuxExecutableFilename");
|
||||
@@ -743,6 +746,8 @@ void Project::SerializeTo(SerializerElement & element) const
|
||||
propElement.SetAttribute("projectFile", gameFile);
|
||||
propElement.SetAttribute("folderProject", folderProject);
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
platformSpecificAssets.SerializeTo(propElement.AddChild("platformSpecificAssets"));
|
||||
propElement.SetAttribute("winExecutableFilename", winExecutableFilename);
|
||||
propElement.SetAttribute("winExecutableIconFile", winExecutableIconFile);
|
||||
propElement.SetAttribute("linuxExecutableFilename", linuxExecutableFilename);
|
||||
@@ -815,6 +820,7 @@ void Project::ExposeResources(gd::ArbitraryResourceWorker & worker)
|
||||
{
|
||||
//Add project resources
|
||||
worker.ExposeResources(&GetResourcesManager());
|
||||
platformSpecificAssets.ExposeResources(worker);
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
gd::SafeYield::Do();
|
||||
#endif
|
||||
@@ -1019,8 +1025,10 @@ void Project::Init(const gd::Project & game)
|
||||
#if defined(GD_IDE_ONLY)
|
||||
author = game.author;
|
||||
packageName = game.packageName;
|
||||
orientation = game.orientation;
|
||||
folderProject = game.folderProject;
|
||||
latestCompilationDirectory = game.latestCompilationDirectory;
|
||||
platformSpecificAssets = game.platformSpecificAssets;
|
||||
objectGroups = game.objectGroups;
|
||||
|
||||
GDMajorVersion = game.GDMajorVersion;
|
||||
|
@@ -16,6 +16,7 @@ class TiXmlElement;
|
||||
#include "GDCore/Project/ChangesNotifier.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/Project/ResourcesManager.h"
|
||||
#include "GDCore/Project/PlatformSpecificAssets.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
namespace gd { class Platform; }
|
||||
namespace gd { class Layout; }
|
||||
@@ -85,6 +86,18 @@ public:
|
||||
*/
|
||||
const gd::String & GetPackageName() const { return packageName; }
|
||||
|
||||
/**
|
||||
* \brief Change the project orientation (in particular when exported with Cordova).
|
||||
* This has no effect on desktop and web browsers.
|
||||
* \param orientation The orientation to use ("default", "landscape", "portrait").
|
||||
*/
|
||||
void SetOrientation(const gd::String & orientation_) { orientation = orientation_; };
|
||||
|
||||
/**
|
||||
* \brief Get project orientation ("default", "landscape", "portrait").
|
||||
*/
|
||||
const gd::String & GetOrientation() const { return orientation; }
|
||||
|
||||
/**
|
||||
* Called when project file has changed.
|
||||
*/
|
||||
@@ -118,6 +131,16 @@ public:
|
||||
* \see gd::Project::SetLastCompilationDirectory
|
||||
*/
|
||||
const gd::String & GetLastCompilationDirectory() const {return latestCompilationDirectory;}
|
||||
|
||||
/**
|
||||
* \brief Return a reference to platform assets of the project (icons, splashscreen...).
|
||||
*/
|
||||
gd::PlatformSpecificAssets & GetPlatformSpecificAssets() { return platformSpecificAssets; }
|
||||
|
||||
/**
|
||||
* \brief Return a reference to platform assets of the project (icons, splashscreen...).
|
||||
*/
|
||||
const gd::PlatformSpecificAssets & GetPlatformSpecificAssets() const { return platformSpecificAssets; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -737,10 +760,12 @@ private:
|
||||
gd::ObjectGroupsContainer objectGroups; ///< Global objects groups
|
||||
gd::String author; ///< Game author name
|
||||
gd::String packageName; ///< Game package name
|
||||
gd::String orientation; ///< Lock game orientation (on mobile devices). "default", "landscape" or "portrait".
|
||||
bool folderProject; ///< True if folder project, false if single file project.
|
||||
gd::String gameFile; ///< File of the game
|
||||
gd::String latestCompilationDirectory; ///< File of the game
|
||||
gd::Platform* currentPlatform; ///< The platform being used to edit the project.
|
||||
gd::PlatformSpecificAssets platformSpecificAssets;
|
||||
std::vector < std::unique_ptr<gd::ExternalEvents> > externalEvents; ///< List of all externals events
|
||||
mutable unsigned int GDMajorVersion; ///< The GD major version used the last time the project was saved.
|
||||
mutable unsigned int GDMinorVersion; ///< The GD minor version used the last time the project was saved.
|
||||
|
@@ -105,7 +105,7 @@ bool ResourcesManager::HasResource(const gd::String & name) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<gd::String> ResourcesManager::GetAllResourcesList()
|
||||
std::vector<gd::String> ResourcesManager::GetAllResourceNames()
|
||||
{
|
||||
std::vector<gd::String> allResources;
|
||||
for (std::size_t i = 0;i<resources.size();++i)
|
||||
@@ -146,7 +146,7 @@ bool ResourcesManager::AddResource(const gd::String & name, const gd::String & f
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<gd::String> ResourceFolder::GetAllResourcesList()
|
||||
std::vector<gd::String> ResourceFolder::GetAllResourceNames()
|
||||
{
|
||||
std::vector<gd::String> allResources;
|
||||
for (std::size_t i = 0;i<resources.size();++i)
|
||||
@@ -351,6 +351,25 @@ bool ResourcesManager::MoveResourceDownInList(const gd::String & name)
|
||||
return gd::MoveResourceDownInList(resources, name);
|
||||
}
|
||||
|
||||
std::size_t ResourcesManager::GetResourcePosition(const gd::String & name) const
|
||||
{
|
||||
for (std::size_t i = 0;i<resources.size();++i)
|
||||
{
|
||||
if (resources[i]->GetName() == name) return i;
|
||||
}
|
||||
return gd::String::npos;
|
||||
}
|
||||
|
||||
void ResourcesManager::MoveResource(std::size_t oldIndex, std::size_t newIndex)
|
||||
{
|
||||
if ( oldIndex >= resources.size() || newIndex >= resources.size() )
|
||||
return;
|
||||
|
||||
auto resource = resources[oldIndex];
|
||||
resources.erase(resources.begin() + oldIndex);
|
||||
resources.insert(resources.begin() + newIndex, resource);
|
||||
}
|
||||
|
||||
bool ResourcesManager::MoveFolderUpInList(const gd::String & name)
|
||||
{
|
||||
for (std::size_t i =1;i<folders.size();++i)
|
||||
|
@@ -293,9 +293,9 @@ public:
|
||||
std::shared_ptr<Resource> CreateResource(const gd::String & kind);
|
||||
|
||||
/**
|
||||
* \brief Get a list containing the name of all of the resources.
|
||||
* \brief Get a list containing the names of all resources.
|
||||
*/
|
||||
std::vector<gd::String> GetAllResourcesList();
|
||||
std::vector<gd::String> GetAllResourceNames();
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
@@ -324,6 +324,11 @@ public:
|
||||
*/
|
||||
void RenameResource(const gd::String & oldName, const gd::String & newName);
|
||||
|
||||
/**
|
||||
* \brief Return the position of the layer called "name" in the layers list
|
||||
*/
|
||||
std::size_t GetResourcePosition(const gd::String & name) const;
|
||||
|
||||
/**
|
||||
* \brief Move a resource up in the list
|
||||
*/
|
||||
@@ -334,6 +339,11 @@ public:
|
||||
*/
|
||||
bool MoveResourceDownInList(const gd::String & name);
|
||||
|
||||
/**
|
||||
* Change the position of the specified resource.
|
||||
*/
|
||||
void MoveResource(std::size_t oldIndex, std::size_t newIndex);
|
||||
|
||||
/**
|
||||
* \brief Return true if the folder exists.
|
||||
*/
|
||||
@@ -445,7 +455,7 @@ public:
|
||||
/**
|
||||
* Get a list containing the name of all of the resources.
|
||||
*/
|
||||
virtual std::vector<gd::String> GetAllResourcesList();
|
||||
virtual std::vector<gd::String> GetAllResourceNames();
|
||||
|
||||
/**
|
||||
* Move a resource up in the list
|
||||
|
@@ -84,7 +84,7 @@ TEST_CASE( "Resources", "[common][resources]" ) {
|
||||
|
||||
gd::ProjectResourcesAdder::RemoveAllUselessImages(project);
|
||||
std::vector<gd::String> remainingResources =
|
||||
project.GetResourcesManager().GetAllResourcesList();
|
||||
project.GetResourcesManager().GetAllResourceNames();
|
||||
REQUIRE(remainingResources.size() == 2);
|
||||
REQUIRE(remainingResources[0] == "res1");
|
||||
REQUIRE(remainingResources[1] == "res4");
|
||||
|
@@ -21,7 +21,7 @@ class GD_EXTENSION_API AdMobObject : public gd::Object
|
||||
public:
|
||||
AdMobObject(gd::String name_);
|
||||
virtual ~AdMobObject() {};
|
||||
virtual std::unique_ptr<gd::Object> Clone() const { return gd::make_unique<AdMobObject>(*this); }
|
||||
virtual std::unique_ptr<gd::Object> Clone() const override { return gd::make_unique<AdMobObject>(*this); }
|
||||
|
||||
#if !defined(GD_NO_WX_GUI)
|
||||
void DrawInitialInstance(gd::InitialInstance & instance, sf::RenderTarget & renderTarget, gd::Project & project, gd::Layout & layout) override;
|
||||
|
@@ -322,13 +322,13 @@ PathBehaviorEditor::PathBehaviorEditor(wxWindow* parent, gd::Project & game_, gd
|
||||
followAngleCheck->SetValue(behavior.FollowAngle());
|
||||
|
||||
//Setup shared datas
|
||||
if ( !scene || scene->behaviorsInitialSharedDatas.find(behavior.GetName()) == scene->behaviorsInitialSharedDatas.end())
|
||||
if (!scene || !scene->HasBehaviorSharedData(behavior.GetName()))
|
||||
{
|
||||
gd::LogError(_("Unable to access to shared datas."));
|
||||
return;
|
||||
}
|
||||
|
||||
sharedDatas = std::dynamic_pointer_cast<ScenePathDatas>(scene->behaviorsInitialSharedDatas[behavior.GetName()]);
|
||||
sharedDatas = std::dynamic_pointer_cast<ScenePathDatas>(scene->GetBehaviorSharedDataSmartPtr(behavior.GetName()));
|
||||
|
||||
if ( sharedDatas == std::shared_ptr<ScenePathDatas>() )
|
||||
{
|
||||
|
@@ -241,13 +241,13 @@ scene(scene_)
|
||||
}
|
||||
|
||||
//Setup shared datas
|
||||
if ( !scene || scene->behaviorsInitialSharedDatas.find(behavior.GetName()) == scene->behaviorsInitialSharedDatas.end())
|
||||
if (!scene || !scene->HasBehaviorSharedData(behavior.GetName()))
|
||||
{
|
||||
gd::LogError(_("Unable to access to shared datas."));
|
||||
return;
|
||||
}
|
||||
|
||||
sharedDatas = std::dynamic_pointer_cast<ScenePhysicsDatas>(scene->behaviorsInitialSharedDatas[behavior.GetName()]);
|
||||
sharedDatas = std::dynamic_pointer_cast<ScenePhysicsDatas>(scene->GetBehaviorSharedDataSmartPtr(behavior.GetName()));
|
||||
|
||||
if ( sharedDatas == std::shared_ptr<ScenePhysicsDatas>() )
|
||||
{
|
||||
|
@@ -6,9 +6,46 @@ This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "ScenePhysicsDatas.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include <map>
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/IDE/Dialogs/PropertyDescriptor.h"
|
||||
#endif
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
std::map<gd::String, gd::PropertyDescriptor> ScenePhysicsDatas::GetProperties(gd::Project & project) const
|
||||
{
|
||||
std::map<gd::String, gd::PropertyDescriptor> properties;
|
||||
properties[_("Gravity on X axis (in m/s²)")].SetValue(gd::String::From(gravityX));
|
||||
properties[_("Gravity on Y axis (in m/s²)")].SetValue(gd::String::From(gravityY));
|
||||
properties[_("X Scale: number of pixels for 1 meter")].SetValue(gd::String::From(scaleX));
|
||||
properties[_("Y Scale: number of pixels for 1 meter")].SetValue(gd::String::From(scaleY));
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool ScenePhysicsDatas::UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project)
|
||||
{
|
||||
if (name == _("Gravity on X axis (in m/s²)")) {
|
||||
gravityX = value.To<float>();
|
||||
}
|
||||
if (name == _("Gravity on Y axis (in m/s²)")) {
|
||||
gravityY = value.To<float>();
|
||||
}
|
||||
if (name == _("X scale: number of pixels for 1 meter")) {
|
||||
scaleX = value.To<float>();
|
||||
}
|
||||
if (name == _("Y scale: number of pixels for 1 meter")) {
|
||||
scaleY = value.To<float>();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScenePhysicsDatas::SerializeTo(gd::SerializerElement & element) const
|
||||
{
|
||||
element.SetAttribute("gravityX", gravityX);
|
||||
|
@@ -17,7 +17,7 @@ This project is released under the MIT License.
|
||||
class ScenePhysicsDatas : public gd::BehaviorsSharedData
|
||||
{
|
||||
public:
|
||||
ScenePhysicsDatas() : BehaviorsSharedData(), gravityX(0), gravityY(0), scaleX(100), scaleY(100)
|
||||
ScenePhysicsDatas() : BehaviorsSharedData(), gravityX(0), gravityY(9), scaleX(100), scaleY(100)
|
||||
{
|
||||
};
|
||||
virtual ~ScenePhysicsDatas() {};
|
||||
@@ -34,6 +34,8 @@ public:
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
virtual std::map<gd::String, gd::PropertyDescriptor> GetProperties(gd::Project & project) const;
|
||||
virtual bool UpdateProperty(const gd::String & name, const gd::String & value, gd::Project & project);
|
||||
virtual void SerializeTo(gd::SerializerElement & element) const;
|
||||
#endif
|
||||
|
||||
|
@@ -450,7 +450,7 @@ bool RuntimeScene::LoadFromSceneAndCustomInstances( const gd::Layout & scene, co
|
||||
|
||||
//Behaviors shared data
|
||||
std::cout << ".";
|
||||
behaviorsSharedDatas.LoadFrom(scene.behaviorsInitialSharedDatas);
|
||||
behaviorsSharedDatas.LoadFrom(scene.GetAllBehaviorSharedData());
|
||||
|
||||
std::cout << ".";
|
||||
//Extensions specific initialization
|
||||
|
@@ -97,8 +97,9 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
bool minify, bool exportForCordova)
|
||||
{
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
gd::Project exportedProject = project;
|
||||
|
||||
auto exportProject = [this, &project, &minify,
|
||||
auto exportProject = [this, &exportedProject, &minify,
|
||||
&exportForCordova, &helper](gd::String exportDir)
|
||||
{
|
||||
wxProgressDialog * progressDialogPtr = NULL;
|
||||
@@ -111,8 +112,6 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
fs.MkDir(exportDir);
|
||||
std::vector<gd::String> includesFiles;
|
||||
|
||||
gd::Project exportedProject = project;
|
||||
|
||||
//Export the resources (before generating events as some resources filenames may be updated)
|
||||
helper.ExportResources(fs, exportedProject, exportDir, progressDialogPtr);
|
||||
|
||||
@@ -168,11 +167,12 @@ bool Exporter::ExportWholePixiProject(gd::Project & project, gd::String exportDi
|
||||
{
|
||||
//Prepare the export directory
|
||||
fs.MkDir(exportDir);
|
||||
if (!helper.ExportCordovaConfigFile(project, exportDir))
|
||||
return false;
|
||||
|
||||
if (!exportProject(exportDir + "/www"))
|
||||
return false;
|
||||
|
||||
if (!helper.ExportCordovaConfigFile(exportedProject, exportDir))
|
||||
return false;
|
||||
} else {
|
||||
if (!exportProject(exportDir))
|
||||
return false;
|
||||
|
@@ -156,10 +156,42 @@ bool ExporterHelper::ExportPixiIndexFile(gd::String source, gd::String exportDir
|
||||
|
||||
bool ExporterHelper::ExportCordovaConfigFile(const gd::Project & project, gd::String exportDir)
|
||||
{
|
||||
auto & platformSpecificAssets = project.GetPlatformSpecificAssets();
|
||||
auto & resourceManager = project.GetResourcesManager();
|
||||
auto getIconFilename = [&resourceManager, &platformSpecificAssets](const gd::String & platform, const gd::String & name) {
|
||||
const gd::String & file = resourceManager.GetResource(platformSpecificAssets.Get(platform, name)).GetFile();
|
||||
return file.empty() ? "" : "www/" + file;
|
||||
};
|
||||
|
||||
gd::String str = fs.ReadFile(gdjsRoot + "/Runtime/Cordova/config.xml")
|
||||
.FindAndReplace("GDJS_PROJECTNAME", project.GetName())
|
||||
.FindAndReplace("GDJS_PACKAGENAME", project.GetPackageName())
|
||||
.FindAndReplace("GDJS_ORIENTATION", "default");
|
||||
.FindAndReplace("GDJS_ORIENTATION", project.GetOrientation())
|
||||
// Android icons
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_36", getIconFilename("android", "icon-36"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_48", getIconFilename("android", "icon-48"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_72", getIconFilename("android", "icon-72"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_96", getIconFilename("android", "icon-96"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_144", getIconFilename("android", "icon-144"))
|
||||
.FindAndReplace("GDJS_ICON_ANDROID_192", getIconFilename("android", "icon-192"))
|
||||
// iOS icons
|
||||
.FindAndReplace("GDJS_ICON_IOS_180", getIconFilename("ios", "icon-180"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_60", getIconFilename("ios", "icon-60"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_120", getIconFilename("ios", "icon-120"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_76", getIconFilename("ios", "icon-76"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_152", getIconFilename("ios", "icon-152"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_40", getIconFilename("ios", "icon-40"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_80", getIconFilename("ios", "icon-80"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_57", getIconFilename("ios", "icon-57"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_114", getIconFilename("ios", "icon-114"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_72", getIconFilename("ios", "icon-72"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_144", getIconFilename("ios", "icon-144"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_167", getIconFilename("ios", "icon-167"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_29", getIconFilename("ios", "icon-29"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_58", getIconFilename("ios", "icon-58"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_50", getIconFilename("ios", "icon-50"))
|
||||
.FindAndReplace("GDJS_ICON_IOS_100", getIconFilename("ios", "icon-100"))
|
||||
;
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/config.xml", str))
|
||||
{
|
||||
@@ -196,14 +228,14 @@ bool ExporterHelper::ExportCocos2dFiles(const gd::Project & project, gd::String
|
||||
std::vector<gd::String> noIncludesInThisFile;
|
||||
if (!CompleteIndexFile(str, customCss, customHtml, exportDir, noIncludesInThisFile, ""))
|
||||
{
|
||||
lastError = "Unable to complete Cocos2d index.html file.";
|
||||
lastError = "Unable to complete Cocos2d-JS index.html file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Write the index.html file
|
||||
if (!fs.WriteToFile(exportDir + "/index.html", str))
|
||||
{
|
||||
lastError = "Unable to write Cocos2d index.html file.";
|
||||
lastError = "Unable to write Cocos2d-JS index.html file.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -229,7 +261,7 @@ bool ExporterHelper::ExportCocos2dFiles(const gd::Project & project, gd::String
|
||||
|
||||
if (!fs.WriteToFile(exportDir + "/project.json", str))
|
||||
{
|
||||
lastError = "Unable to write Cocos2d project.json file.";
|
||||
lastError = "Unable to write Cocos2d-JS project.json file.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -12,10 +12,46 @@
|
||||
<allow-intent href="geo:*" />
|
||||
<platform name="android">
|
||||
<allow-intent href="market:*" />
|
||||
<icon src="GDJS_ICON_ANDROID_36" density="ldpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_48" density="mdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_72" density="hdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_96" density="xhdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_144" density="xxhdpi" />
|
||||
<icon src="GDJS_ICON_ANDROID_192" density="xxxhdpi" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<allow-intent href="itms:*" />
|
||||
<allow-intent href="itms-apps:*" />
|
||||
<!-- iOS 8.0+ -->
|
||||
<!-- iPhone 6 Plus -->
|
||||
<icon src="GDJS_ICON_IOS_180" width="180" height="180" />
|
||||
<!-- iOS 7.0+ -->
|
||||
<!-- iPhone / iPod Touch -->
|
||||
<icon src="GDJS_ICON_IOS_60" width="60" height="60" />
|
||||
<icon src="GDJS_ICON_IOS_120" width="120" height="120" />
|
||||
<!-- iPad -->
|
||||
<icon src="GDJS_ICON_IOS_76" width="76" height="76" />
|
||||
<icon src="GDJS_ICON_IOS_152" width="152" height="152" />
|
||||
<!-- Spotlight Icon -->
|
||||
<icon src="GDJS_ICON_IOS_40" width="40" height="40" />
|
||||
<icon src="GDJS_ICON_IOS_80" width="80" height="80" />
|
||||
<!-- iOS 6.1 -->
|
||||
<!-- iPhone / iPod Touch -->
|
||||
<icon src="GDJS_ICON_IOS_57" width="57" height="57" />
|
||||
<icon src="GDJS_ICON_IOS_114" width="114" height="114" />
|
||||
<!-- iPad -->
|
||||
<icon src="GDJS_ICON_IOS_72" width="72" height="72" />
|
||||
<icon src="GDJS_ICON_IOS_144" width="144" height="144" />
|
||||
<!-- iPad Pro -->
|
||||
<icon src="GDJS_ICON_IOS_167" width="167" height="167" />
|
||||
<!-- iPhone Spotlight and Settings Icon -->
|
||||
<icon src="GDJS_ICON_IOS_29" width="29" height="29" />
|
||||
<icon src="GDJS_ICON_IOS_58" width="58" height="58" />
|
||||
<!-- iPad Spotlight and Settings Icon -->
|
||||
<icon src="GDJS_ICON_IOS_50" width="50" height="50" />
|
||||
<icon src="GDJS_ICON_IOS_100" width="100" height="100" />
|
||||
<!-- iPad Pro -->
|
||||
<icon src="GDJS_ICON_IOS_167" width="167" height="167" />
|
||||
</platform>
|
||||
<preference name="orientation" value="GDJS_ORIENTATION" />
|
||||
</widget>
|
||||
|
@@ -161,7 +161,6 @@ gdjs.RuntimeScene.prototype.unloadScene = function() {
|
||||
this._allInstancesList = [];
|
||||
this._instancesRemoved = [];
|
||||
|
||||
this._renderer = new gdjs.RuntimeSceneRenderer(this, this._runtimeGame ? this._runtimeGame.getRenderer() : null);
|
||||
this._lastId = 0;
|
||||
this._eventsContext = null;
|
||||
|
||||
|
1
newIDE/app/flow-typed/libGD.js
vendored
1
newIDE/app/flow-typed/libGD.js
vendored
@@ -13,6 +13,7 @@ declare type gdExternalEvents = EmscriptenObject;
|
||||
declare type gdSerializerElement = EmscriptenObject;
|
||||
declare type gdInitialInstance = EmscriptenObject;
|
||||
declare type gdBaseEvent = EmscriptenObject;
|
||||
declare type gdResource = EmscriptenObject;
|
||||
|
||||
//Represents all objects that have serializeTo and unserializeFrom methods.
|
||||
declare type gdSerializable = EmscriptenObject;
|
||||
|
@@ -25,27 +25,8 @@
|
||||
<meta name="theme-color" content="#4ab0e4">
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
|
||||
<!-- Fullstory user analytics -->
|
||||
<script>
|
||||
window['_fs_debug'] = false;
|
||||
window['_fs_host'] = 'fullstory.com';
|
||||
window['_fs_org'] = '8DWZ1';
|
||||
window['_fs_namespace'] = 'FS';
|
||||
(function(m,n,e,t,l,o,g,y){
|
||||
if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
|
||||
g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[];
|
||||
o=n.createElement(t);o.async=1;o.src='https://'+_fs_host+'/s/fs.js';
|
||||
y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
|
||||
g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){g(l,v)};
|
||||
g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
|
||||
g.clearUserCookie=function(c,d,i){if(!c || document.cookie.match('fs_uid=[`;`]*`[`;`]*`[`;`]*`')){
|
||||
d=n.domain;while(1){n.cookie='fs_uid=;domain='+d+
|
||||
';path=/;expires='+new Date(0).toUTCString();i=d.indexOf('.');if(i<0)break;d=d.slice(i+1)}}};
|
||||
})(window,document,window['_fs_namespace'],'script','user');
|
||||
</script>
|
||||
|
||||
<!-- Stripe.com Checkout -->
|
||||
<script src="https://checkout.stripe.com/checkout.js"></script>
|
||||
<!-- Stripe.com Checkout -->
|
||||
<script src="https://checkout.stripe.com/checkout.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -9,6 +9,9 @@ import { findGDJS } from './LocalGDJSFinder';
|
||||
import localFileSystem from './LocalFileSystem';
|
||||
import LocalFolderPicker from '../../UI/LocalFolderPicker';
|
||||
import HelpButton from '../../UI/HelpButton';
|
||||
import { displaySanityCheck } from '../SanityChecker';
|
||||
import { getSanityMessages } from '../SanityChecker/CordovaSanityChecker';
|
||||
import { translate } from 'react-i18next';
|
||||
import assignIn from 'lodash/assignIn';
|
||||
import optionalRequire from '../../Utils/OptionalRequire';
|
||||
import Window from '../../Utils/Window';
|
||||
@@ -17,7 +20,7 @@ const shell = electron ? electron.shell : null;
|
||||
|
||||
const gd = global.gd;
|
||||
|
||||
export default class LocalCordovaExport extends Component {
|
||||
class LocalCordovaExport extends Component {
|
||||
state = {
|
||||
exportFinishedDialogOpen: false,
|
||||
outputDir: '',
|
||||
@@ -53,11 +56,13 @@ export default class LocalCordovaExport extends Component {
|
||||
};
|
||||
|
||||
launchExport = () => {
|
||||
const { project } = this.props;
|
||||
const { t, project } = this.props;
|
||||
if (!project) return;
|
||||
|
||||
sendExportLaunched('local-cordova');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project))) return;
|
||||
|
||||
const outputDir = this.state.outputDir;
|
||||
project.setLastCompilationDirectory(outputDir);
|
||||
|
||||
@@ -89,7 +94,7 @@ export default class LocalCordovaExport extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { project } = this.props;
|
||||
const { t, project } = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
return (
|
||||
@@ -125,7 +130,7 @@ export default class LocalCordovaExport extends Component {
|
||||
/>
|
||||
</Line>
|
||||
<Dialog
|
||||
title="Export finished"
|
||||
title={t('Export finished')}
|
||||
actions={[
|
||||
<FlatButton
|
||||
key="open"
|
||||
@@ -162,10 +167,12 @@ export default class LocalCordovaExport extends Component {
|
||||
fullWidth
|
||||
primary
|
||||
onClick={() => this.openPhoneGapBuild()}
|
||||
label="Open PhoneGap Build"
|
||||
label={t('Open PhoneGap Build')}
|
||||
/>
|
||||
</Dialog>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(LocalCordovaExport);
|
||||
|
@@ -25,6 +25,9 @@ import Window from '../../../Utils/Window';
|
||||
import { delay } from '../../../Utils/Delay';
|
||||
import CreateProfile from '../../../Profile/CreateProfile';
|
||||
import LimitDisplayer from '../../../Profile/LimitDisplayer';
|
||||
import { displaySanityCheck } from '../../SanityChecker';
|
||||
import { getSanityMessages } from '../../SanityChecker/CordovaSanityChecker';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
const path = optionalRequire('path');
|
||||
const os = optionalRequire('os');
|
||||
const electron = optionalRequire('electron');
|
||||
@@ -51,7 +54,7 @@ type State = {
|
||||
errored: boolean,
|
||||
};
|
||||
|
||||
type Props = WithUserProfileProps & {
|
||||
type Props = WithUserProfileProps & TranslatorProps & {
|
||||
project: gdProject,
|
||||
onChangeSubscription: Function,
|
||||
};
|
||||
@@ -97,7 +100,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
};
|
||||
|
||||
launchExport = (): Promise<string> => {
|
||||
const { project } = this.props;
|
||||
const { project, t } = this.props;
|
||||
if (!project) return Promise.reject();
|
||||
|
||||
return LocalOnlineCordovaExport.prepareExporter()
|
||||
@@ -114,7 +117,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
return outputDir;
|
||||
})
|
||||
.catch(err => {
|
||||
showErrorBox('Unable to export the game', err);
|
||||
showErrorBox(t('Unable to export the game'), err);
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
@@ -198,8 +201,12 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
};
|
||||
|
||||
launchWholeExport = () => {
|
||||
const { t, project } = this.props;
|
||||
sendExportLaunched('local-online-cordova');
|
||||
|
||||
if (!displaySanityCheck(t, getSanityMessages(t, project)))
|
||||
return;
|
||||
|
||||
const handleError = (message: string) => err => {
|
||||
if (!this.state.errored) {
|
||||
this.setState({
|
||||
@@ -226,33 +233,33 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
exportStep: 'compress',
|
||||
});
|
||||
return this.launchCompression(outputDir);
|
||||
}, handleError('Error while exporting the game.'))
|
||||
}, handleError(t('Error while exporting the game.')))
|
||||
.then(outputFile => {
|
||||
this.setState({
|
||||
exportStep: 'upload',
|
||||
});
|
||||
return this.launchUpload(outputFile);
|
||||
}, handleError('Error while compressing the game.'))
|
||||
}, handleError(t('Error while compressing the game.')))
|
||||
.then((uploadBucketKey: string) => {
|
||||
this.setState({
|
||||
exportStep: 'waiting-for-build',
|
||||
});
|
||||
return this.launchBuild(uploadBucketKey);
|
||||
}, handleError('Error while uploading the game. Check your internet connection or try again later.'))
|
||||
}, handleError(t('Error while uploading the game. Check your internet connection or try again later.')))
|
||||
.then(buildId => {
|
||||
this.setState({
|
||||
exportStep: 'build',
|
||||
});
|
||||
|
||||
return this.pollBuild(buildId);
|
||||
}, handleError('Error while lauching the build of the game.'))
|
||||
}, handleError(t('Error while lauching the build of the game.')))
|
||||
.then(build => {
|
||||
this.setState({
|
||||
exportStep: 'done',
|
||||
build,
|
||||
});
|
||||
this.props.onRefreshUserProfile();
|
||||
}, handleError('Error while building the game.'));
|
||||
}, handleError(t('Error while building the game.')));
|
||||
};
|
||||
|
||||
_download = () => {
|
||||
@@ -291,6 +298,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
onLogin,
|
||||
subscription,
|
||||
limits,
|
||||
t,
|
||||
} = this.props;
|
||||
if (!project) return null;
|
||||
|
||||
@@ -302,13 +310,12 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
return (
|
||||
<Column noMargin>
|
||||
<Line>
|
||||
Packaging your game for Android will create an APK file that can be
|
||||
installed on Android phones, based on Cordova framework.
|
||||
{t("Packaging your game for Android will create an APK file that can be installed on Android phones, based on Cordova framework.")}
|
||||
</Line>
|
||||
{authenticated && (
|
||||
<Line justifyContent="center">
|
||||
<RaisedButton
|
||||
label="Package for Android"
|
||||
label={t("Package for Android")}
|
||||
primary
|
||||
onClick={this.launchWholeExport}
|
||||
disabled={disableBuild}
|
||||
@@ -324,7 +331,7 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
)}
|
||||
{!authenticated && (
|
||||
<CreateProfile
|
||||
message="Create an account to build your game for Android in one-click:"
|
||||
message={t("Create an account to build your game for Android in one-click:")}
|
||||
onLogin={onLogin}
|
||||
/>
|
||||
)}
|
||||
@@ -347,6 +354,6 @@ class LocalOnlineCordovaExport extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
export default withUserProfile({ fetchLimits: true, fetchSubscription: true })(
|
||||
export default translate()(withUserProfile({ fetchLimits: true, fetchSubscription: true })(
|
||||
LocalOnlineCordovaExport
|
||||
);
|
||||
));
|
||||
|
30
newIDE/app/src/Export/SanityChecker/CordovaSanityChecker.js
Normal file
30
newIDE/app/src/Export/SanityChecker/CordovaSanityChecker.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// @flow
|
||||
import { type TFunction } from 'react-i18next';
|
||||
import { type SanityMessages } from './index';
|
||||
|
||||
export const getSanityMessages = (t: TFunction, project: gdProject): SanityMessages => {
|
||||
let errors = [];
|
||||
let warnings = [];
|
||||
if (!project.getPackageName()) {
|
||||
errors.push(
|
||||
t(
|
||||
'The package name is empty. Choose and enter a package name in the game properties.'
|
||||
)
|
||||
);
|
||||
} else if (project.getPackageName().length >= 255) {
|
||||
errors.push(t('The package name is too long.'));
|
||||
}
|
||||
|
||||
if (!project.getName()) {
|
||||
errors.push(
|
||||
t(
|
||||
'The game name is empty. Choose and enter a name in the game properties.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
errors,
|
||||
warnings,
|
||||
};
|
||||
};
|
25
newIDE/app/src/Export/SanityChecker/index.js
Normal file
25
newIDE/app/src/Export/SanityChecker/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// @flow
|
||||
import { type TFunction } from 'react-i18next';
|
||||
import { showErrorBox } from '../../UI/Messages/MessageBox';
|
||||
|
||||
export type SanityMessages = {
|
||||
errors: Array<string>,
|
||||
warnings: Array<string>,
|
||||
};
|
||||
|
||||
export const displaySanityCheck = (
|
||||
t: TFunction,
|
||||
messages: SanityMessages
|
||||
): boolean => {
|
||||
if (messages.errors.length) {
|
||||
showErrorBox(
|
||||
t(
|
||||
'Your game has some invalid elements, please fix these before continuing:'
|
||||
) +
|
||||
'\n\n' +
|
||||
messages.errors.map(message => `- ${message}`).join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
return !messages.errors.length;
|
||||
};
|
21
newIDE/app/src/MainFrame/Editors/ResourcesEditor.js
Normal file
21
newIDE/app/src/MainFrame/Editors/ResourcesEditor.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import BaseEditor from './BaseEditor';
|
||||
import ResourcesFullEditor from '../../ResourcesEditor';
|
||||
|
||||
export default class ResourcesEditor extends BaseEditor {
|
||||
updateToolbar() {
|
||||
if (this.editor) this.editor.updateToolbar();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { project } = this.props;
|
||||
|
||||
return (
|
||||
<ResourcesFullEditor
|
||||
{...this.props}
|
||||
ref={editor => (this.editor = editor)}
|
||||
project={project}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@@ -16,8 +16,10 @@ import PreferencesDialog from './Preferences/PreferencesDialog';
|
||||
import ConfirmCloseDialog from './ConfirmCloseDialog';
|
||||
import AboutDialog, { type UpdateStatus } from './AboutDialog';
|
||||
import ProjectManager from '../ProjectManager';
|
||||
import PlatformSpecificAssetsDialog from '../PlatformSpecificAssetsEditor/PlatformSpecificAssetsDialog';
|
||||
import LoaderModal from '../UI/LoaderModal';
|
||||
import EditorBar from '../UI/EditorBar';
|
||||
import CloseConfirmDialog from '../UI/CloseConfirmDialog';
|
||||
import ProfileDialog from '../Profile/ProfileDialog';
|
||||
import Window from '../Utils/Window';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
@@ -47,6 +49,7 @@ import ExternalEventsEditor from './Editors/ExternalEventsEditor';
|
||||
import SceneEditor from './Editors/SceneEditor';
|
||||
import ExternalLayoutEditor from './Editors/ExternalLayoutEditor';
|
||||
import StartPage from './Editors/StartPage';
|
||||
import ResourcesEditor from './Editors/ResourcesEditor';
|
||||
import {
|
||||
type PreferencesState,
|
||||
getThemeName,
|
||||
@@ -55,6 +58,7 @@ import {
|
||||
} from './Preferences/PreferencesHandler';
|
||||
import ErrorBoundary from '../UI/ErrorBoundary';
|
||||
import SubscriptionDialog from '../Profile/SubscriptionDialog';
|
||||
import ResourcesLoader from '../ResourcesLoader/index';
|
||||
|
||||
const gd = global.gd;
|
||||
|
||||
@@ -87,6 +91,7 @@ type State = {|
|
||||
updateStatus: UpdateStatus,
|
||||
aboutDialogOpen: boolean,
|
||||
onSubscriptionDialogClosed: ?Function,
|
||||
platformSpecificAssetsDialogOpen: boolean,
|
||||
|};
|
||||
|
||||
export default class MainFrame extends Component<*, State> {
|
||||
@@ -111,6 +116,7 @@ export default class MainFrame extends Component<*, State> {
|
||||
updateStatus: { message: '', status: 'unknown' },
|
||||
aboutDialogOpen: false,
|
||||
onSubscriptionDialogClosed: null,
|
||||
platformSpecificAssetsDialogOpen: false,
|
||||
};
|
||||
toolbar = null;
|
||||
confirmCloseDialog: any = null;
|
||||
@@ -139,6 +145,11 @@ export default class MainFrame extends Component<*, State> {
|
||||
|
||||
loadFromProject = (project: gdProject, cb: Function) => {
|
||||
this.closeProject(() => {
|
||||
// Make sure that the ResourcesLoader cache is emptied, so that
|
||||
// the URL to a resource with a name in the old project is not re-used
|
||||
// for another resource with the same name in the new project.
|
||||
ResourcesLoader.burstUrlsCache();
|
||||
|
||||
this.setState(
|
||||
{
|
||||
currentProject: project,
|
||||
@@ -241,7 +252,11 @@ export default class MainFrame extends Component<*, State> {
|
||||
const name = newNameGenerator('NewScene', name =>
|
||||
currentProject.hasLayoutNamed(name)
|
||||
);
|
||||
currentProject.insertNewLayout(name, currentProject.getLayoutsCount());
|
||||
const newLayout = currentProject.insertNewLayout(
|
||||
name,
|
||||
currentProject.getLayoutsCount()
|
||||
);
|
||||
newLayout.updateBehaviorsSharedData(currentProject);
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
@@ -541,6 +556,36 @@ export default class MainFrame extends Component<*, State> {
|
||||
);
|
||||
};
|
||||
|
||||
openResources = () => {
|
||||
this.setState(
|
||||
{
|
||||
editorTabs: openEditorTab(this.state.editorTabs, {
|
||||
name: 'Resources',
|
||||
editorCreator: () => (
|
||||
<ResourcesEditor
|
||||
project={this.state.currentProject}
|
||||
setToolbar={this.setEditorToolbar}
|
||||
onDeleteResource={(resource: gdResource, cb: boolean => void) => {
|
||||
// TODO: Project wide refactoring of objects/events using the resource
|
||||
cb(true);
|
||||
}}
|
||||
onRenameResource={(
|
||||
resource: gdResource,
|
||||
newName: string,
|
||||
cb: boolean => void
|
||||
) => {
|
||||
// TODO: Project wide refactoring of objects/events using the resource
|
||||
cb(true);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
key: 'resources',
|
||||
}),
|
||||
},
|
||||
() => this.updateToolbar()
|
||||
);
|
||||
};
|
||||
|
||||
openStartPage = () => {
|
||||
this.setState(
|
||||
{
|
||||
@@ -724,6 +769,12 @@ export default class MainFrame extends Component<*, State> {
|
||||
});
|
||||
};
|
||||
|
||||
openPlatformSpecificAssets = (open: boolean = true) => {
|
||||
this.setState({
|
||||
platformSpecificAssetsDialogOpen: open,
|
||||
});
|
||||
};
|
||||
|
||||
setUpdateStatus = (status: UpdateStatus) => {
|
||||
this.setState({
|
||||
updateStatus: status,
|
||||
@@ -810,6 +861,9 @@ export default class MainFrame extends Component<*, State> {
|
||||
onCloseProject={this.askToCloseProject}
|
||||
onExportProject={this.openExportDialog}
|
||||
onOpenPreferences={() => this.openPreferences(true)}
|
||||
onOpenResources={() => this.openResources()}
|
||||
onOpenPlatformSpecificAssets={() =>
|
||||
this.openPlatformSpecificAssets()}
|
||||
/>
|
||||
)}
|
||||
</Drawer>
|
||||
@@ -892,6 +946,16 @@ export default class MainFrame extends Component<*, State> {
|
||||
open: this.state.saveDialogOpen,
|
||||
onClose: () => this._openSaveDialog(false),
|
||||
})}
|
||||
{!!this.state.currentProject && (
|
||||
<PlatformSpecificAssetsDialog
|
||||
project={this.state.currentProject}
|
||||
open={this.state.platformSpecificAssetsDialogOpen}
|
||||
onApply={() => this.openPlatformSpecificAssets(false)}
|
||||
onClose={() => this.openPlatformSpecificAssets(false)}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={this._onChooseResource}
|
||||
/>
|
||||
)}
|
||||
{!!genericDialog &&
|
||||
React.cloneElement(genericDialog, {
|
||||
open: this.state.genericDialogOpen,
|
||||
@@ -908,7 +972,7 @@ export default class MainFrame extends Component<*, State> {
|
||||
open={profileDialogOpen}
|
||||
authentification={authentification}
|
||||
onClose={() => this.openProfile(false)}
|
||||
onChangeSubscription={(onDone) => this.openSubscription(true, onDone)}
|
||||
onChangeSubscription={onDone => this.openSubscription(true, onDone)}
|
||||
/>
|
||||
<SubscriptionDialog
|
||||
onClose={() => {
|
||||
@@ -933,6 +997,7 @@ export default class MainFrame extends Component<*, State> {
|
||||
onClose={() => this.openAboutDialog(false)}
|
||||
updateStatus={updateStatus}
|
||||
/>
|
||||
<CloseConfirmDialog shouldPrompt={!!this.state.currentProject} />
|
||||
</div>
|
||||
</Providers>
|
||||
);
|
||||
|
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import { Line, Column } from '../../UI/Grid';
|
||||
import ResourcesLoader from '../../ObjectsRendering/ResourcesLoader';
|
||||
import ResourcesLoader from '../../ResourcesLoader';
|
||||
import ResourceSelectorWithThumbnail from '../ResourceSelectorWithThumbnail';
|
||||
const gd = global.gd;
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import MiniToolbar from '../../../UI/MiniToolbar';
|
||||
import DragHandle from '../../../UI/DragHandle';
|
||||
import ContextMenu from '../../../UI/Menu/ContextMenu';
|
||||
import { showWarningBox } from '../../../UI/Messages/MessageBox';
|
||||
import ResourcesLoader from '../../../ObjectsRendering/ResourcesLoader';
|
||||
import ResourcesLoader from '../../../ResourcesLoader';
|
||||
import PointsEditor from './PointsEditor';
|
||||
import { deleteSpritesFromAnimation } from './Utils/SpriteObjectHelper';
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import { Line, Column } from '../../UI/Grid';
|
||||
import ResourcesLoader from '../../ObjectsRendering/ResourcesLoader';
|
||||
import ResourcesLoader from '../../ResourcesLoader';
|
||||
import ResourceSelectorWithThumbnail from '../ResourceSelectorWithThumbnail';
|
||||
const gd = global.gd;
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
|
||||
const MARGIN = 50;
|
||||
|
||||
@@ -29,12 +31,50 @@ const styles = {
|
||||
},
|
||||
};
|
||||
|
||||
export default class ImagePreview extends React.Component {
|
||||
state = {
|
||||
errored: false,
|
||||
imageWidth: null,
|
||||
imageHeight: null,
|
||||
};
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourceName: string,
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
children?: any,
|
||||
style?: Object,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
errored: boolean,
|
||||
imageWidth: ?number,
|
||||
imageHeight: ?number,
|
||||
imageSource: ?string,
|
||||
|};
|
||||
|
||||
export default class ImagePreview extends React.Component<Props, State> {
|
||||
_container: ?HTMLDivElement = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = this._loadFrom(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
if (
|
||||
newProps.resourceName !== this.props.resourceName ||
|
||||
newProps.project !== this.props.project ||
|
||||
newProps.resourcesLoader !== this.props.resourcesLoader
|
||||
) {
|
||||
this.setState(this._loadFrom(newProps));
|
||||
}
|
||||
}
|
||||
|
||||
_loadFrom(props: Props): State {
|
||||
const { project, resourceName, resourcesLoader } = props;
|
||||
return {
|
||||
errored: false,
|
||||
imageWidth: null,
|
||||
imageHeight: null,
|
||||
imageSource:
|
||||
resourcesLoader.getResourceFullUrl(project, resourceName),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this._container) {
|
||||
@@ -49,7 +89,7 @@ export default class ImagePreview extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
_handleImageLoaded = (e, t) => {
|
||||
_handleImageLoaded = (e: any) => {
|
||||
const imgElement = e.target;
|
||||
|
||||
this.setState({
|
||||
@@ -59,15 +99,9 @@ export default class ImagePreview extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
project,
|
||||
resourceName,
|
||||
resourcesLoader,
|
||||
style,
|
||||
children,
|
||||
} = this.props;
|
||||
const { resourceName, style, children } = this.props;
|
||||
|
||||
const { imageHeight, imageWidth } = this.state;
|
||||
const { imageHeight, imageWidth, imageSource } = this.state;
|
||||
|
||||
const overlayStyle = {
|
||||
...styles.overlayContainer,
|
||||
@@ -86,9 +120,10 @@ export default class ImagePreview extends React.Component {
|
||||
<img
|
||||
style={styles.spriteThumbnailImage}
|
||||
alt={resourceName}
|
||||
src={resourcesLoader.getResourceFullFilename(project, resourceName)}
|
||||
src={imageSource}
|
||||
onError={this._handleError}
|
||||
onLoad={this._handleImageLoaded}
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
)}
|
||||
{canDisplayOverlays &&
|
||||
|
@@ -58,13 +58,14 @@ const ThemableImageThumbnail = ({
|
||||
}}
|
||||
onContextMenu={e => {
|
||||
e.stopPropagation();
|
||||
onContextMenu(e.clientX, e.clientY);
|
||||
if (onContextMenu) onContextMenu(e.clientX, e.clientY);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={styles.spriteThumbnailImage}
|
||||
alt={resourceName}
|
||||
src={resourcesLoader.getResourceFullFilename(project, resourceName)}
|
||||
src={resourcesLoader.getResourceFullUrl(project, resourceName)}
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
{selectable && (
|
||||
<div style={styles.checkboxContainer}>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import ResourcesLoader from '../ObjectsRendering/ResourcesLoader';
|
||||
import ResourceSelector from '../ResourcesEditor/ResourceSelector';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
import ResourceSelector from '../ResourcesList/ResourceSelector';
|
||||
import ImageThumbnail from './ImageThumbnail';
|
||||
|
||||
export default ({
|
||||
@@ -10,6 +10,7 @@ export default ({
|
||||
resourceKind,
|
||||
resourceName,
|
||||
onChange,
|
||||
floatingLabelText,
|
||||
}) => {
|
||||
return (
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'flex-end' }}>
|
||||
@@ -22,6 +23,7 @@ export default ({
|
||||
fullWidth
|
||||
initialResourceName={resourceName}
|
||||
onChange={onChange}
|
||||
floatingLabelText={floatingLabelText}
|
||||
/>
|
||||
</div>
|
||||
<ImageThumbnail
|
||||
|
@@ -257,7 +257,7 @@ export default class ObjectsListContainer extends React.Component<
|
||||
|
||||
const { object: pasteObject, global } = objectWithContext;
|
||||
const { object: copiedObject, type, name } = Clipboard.get(CLIPBOARD_KIND);
|
||||
const { project, objectsContainer } = this.props;
|
||||
const { project, objectsContainer, onObjectPasted } = this.props;
|
||||
|
||||
const newName = newNameGenerator(
|
||||
'CopyOf' + name,
|
||||
@@ -287,6 +287,7 @@ export default class ObjectsListContainer extends React.Component<
|
||||
);
|
||||
|
||||
this.forceUpdate();
|
||||
if (onObjectPasted) onObjectPasted(newObject);
|
||||
};
|
||||
|
||||
_editName = (objectWithContext: ?ObjectWithContext) => {
|
||||
|
@@ -7,7 +7,7 @@ import RenderedTextInstance from './Renderers/RenderedTextInstance';
|
||||
import RenderedShapePainterInstance from './Renderers/RenderedShapePainterInstance';
|
||||
import RenderedTextEntryInstance from './Renderers/RenderedTextEntryInstance';
|
||||
import PixiResourcesLoader from './PixiResourcesLoader';
|
||||
import ResourcesLoader from './ResourcesLoader';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
|
||||
/**
|
||||
* A service containing functions that are called to render instances
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import slugs from 'slugs';
|
||||
import PIXI from 'pixi.js';
|
||||
import ResourcesLoader from './ResourcesLoader';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
import { loadFontFace } from '../Utils/FontFaceLoader';
|
||||
const gd = global.gd;
|
||||
|
||||
@@ -9,7 +10,7 @@ const loadedTextures = {};
|
||||
const invalidTexture = PIXI.Texture.fromImage('res/error48.png');
|
||||
|
||||
export default class PixiResourcesLoader {
|
||||
static _initializeTexture(resource, texture) {
|
||||
static _initializeTexture(resource: gdResource, texture: any) {
|
||||
if (resource.getKind() !== 'image') return;
|
||||
|
||||
const imageResource = gd.asImageResource(resource);
|
||||
@@ -18,15 +19,19 @@ export default class PixiResourcesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
static loadTextures(project, onProgress, onComplete) {
|
||||
static loadTextures(
|
||||
project: gdProject,
|
||||
onProgress: (number, number) => void,
|
||||
onComplete: () => void
|
||||
) {
|
||||
const resourcesManager = project.getResourcesManager();
|
||||
const loader = PIXI.loader;
|
||||
|
||||
const resourcesList = resourcesManager.getAllResourcesList().toJSArray();
|
||||
const resourcesList = resourcesManager.getAllResourceNames().toJSArray();
|
||||
const allResources = {};
|
||||
resourcesList.forEach(resourceName => {
|
||||
const resource = resourcesManager.getResource(resourceName);
|
||||
const filename = ResourcesLoader.getResourceFullFilename(
|
||||
const filename = ResourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
resourceName
|
||||
);
|
||||
@@ -72,7 +77,7 @@ export default class PixiResourcesLoader {
|
||||
* should listen to PIXI.Texture `update` event, and refresh your object
|
||||
* if this event is triggered.
|
||||
*/
|
||||
static getPIXITexture(project, resourceName) {
|
||||
static getPIXITexture(project: gdProject, resourceName: string) {
|
||||
if (loadedTextures[resourceName]) {
|
||||
return loadedTextures[resourceName];
|
||||
}
|
||||
@@ -84,7 +89,7 @@ export default class PixiResourcesLoader {
|
||||
if (resource.getKind() !== 'image') return invalidTexture;
|
||||
|
||||
loadedTextures[resourceName] = PIXI.Texture.fromImage(
|
||||
ResourcesLoader.getResourceFullFilename(project, resourceName),
|
||||
ResourcesLoader.getResourceFullUrl(project, resourceName),
|
||||
true /* Treats request as cross-origin */
|
||||
);
|
||||
|
||||
@@ -100,14 +105,17 @@ export default class PixiResourcesLoader {
|
||||
* @returns a Promise that resolves with the font-family to be used
|
||||
* to render a text with the font.
|
||||
*/
|
||||
static loadFontFamily(project, fontFilename) {
|
||||
static loadFontFamily(
|
||||
project: gdProject,
|
||||
fontFilename: string
|
||||
): Promise<string> {
|
||||
// Avoid reloading a font if it's already cached
|
||||
if (loadedFontFamilies[fontFilename]) {
|
||||
return Promise.resolve(loadedFontFamilies[fontFilename]);
|
||||
}
|
||||
|
||||
const fontFamily = slugs(fontFilename);
|
||||
const fullFilename = ResourcesLoader.getFullFilename(project, fontFilename);
|
||||
const fullFilename = ResourcesLoader.getFullUrl(project, fontFilename);
|
||||
return loadFontFace(
|
||||
fontFamily,
|
||||
`url("${fullFilename}")`,
|
||||
@@ -124,7 +132,7 @@ export default class PixiResourcesLoader {
|
||||
* The font won't be loaded.
|
||||
* @returns The font-family to be used to render a text with the font.
|
||||
*/
|
||||
static getFontFamily(project, fontFilename) {
|
||||
static getFontFamily(project: gdProject, fontFilename: string) {
|
||||
if (loadedFontFamilies[fontFilename]) {
|
||||
return loadedFontFamilies[fontFilename];
|
||||
}
|
||||
|
@@ -376,7 +376,7 @@ RenderedPanelSpriteInstance.getThumbnail = function(
|
||||
) {
|
||||
const panelSprite = gd.asPanelSpriteObject(object);
|
||||
|
||||
return resourcesLoader.getResourceFullFilename(
|
||||
return resourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
panelSprite.getTexture()
|
||||
);
|
||||
|
@@ -69,7 +69,7 @@ RenderedSpriteInstance.getThumbnail = function(
|
||||
.getDirection(0)
|
||||
.getSprite(0)
|
||||
.getImageName();
|
||||
return resourcesLoader.getResourceFullFilename(project, imageName);
|
||||
return resourcesLoader.getResourceFullUrl(project, imageName);
|
||||
}
|
||||
|
||||
return 'res/unknown32.png';
|
||||
|
@@ -55,7 +55,7 @@ RenderedTiledSpriteInstance.getThumbnail = function(
|
||||
) {
|
||||
var tiledSprite = gd.asTiledSpriteObject(object);
|
||||
|
||||
return resourcesLoader.getResourceFullFilename(
|
||||
return resourcesLoader.getResourceFullUrl(
|
||||
project,
|
||||
tiledSprite.getTexture()
|
||||
);
|
||||
|
@@ -1,84 +0,0 @@
|
||||
import optionalRequire from '../Utils/OptionalRequire.js';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
|
||||
class FilenamesCache {
|
||||
constructor() {
|
||||
this.projectCache = {};
|
||||
}
|
||||
|
||||
_getProjectCache(project) {
|
||||
const cache = this.projectCache[project.ptr];
|
||||
if (!cache) {
|
||||
return (this.projectCache[project.ptr] = {});
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
getSystemFilename(project, filename) {
|
||||
const cache = this._getProjectCache(project);
|
||||
return cache[filename];
|
||||
}
|
||||
|
||||
cacheSystemFilename(project, filename, systemFilename) {
|
||||
const cache = this._getProjectCache(project);
|
||||
return (cache[filename] = systemFilename);
|
||||
}
|
||||
}
|
||||
|
||||
export default class ResourceLoader {
|
||||
static _cache = new FilenamesCache();
|
||||
|
||||
static isURL(filename) {
|
||||
return (
|
||||
filename.indexOf('http://') === 0 ||
|
||||
filename.indexOf('https://') === 0 ||
|
||||
filename.indexOf('ftp://') === 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified URL/filename for a filename relative to the project.
|
||||
*/
|
||||
static getFullFilename(project, filename) {
|
||||
const cachedSystemFilename = ResourceLoader._cache.getSystemFilename(
|
||||
project,
|
||||
filename
|
||||
);
|
||||
if (cachedSystemFilename) return cachedSystemFilename;
|
||||
|
||||
if (electron && !ResourceLoader.isURL(filename)) {
|
||||
// Support local filesystem with Electron
|
||||
const file = project.getProjectFile();
|
||||
const projectPath = path.dirname(file);
|
||||
const resourceAbsolutePath = path
|
||||
.resolve(projectPath, filename)
|
||||
.replace(/\\/g, '/');
|
||||
|
||||
console.info('Loading', resourceAbsolutePath);
|
||||
return this._cache.cacheSystemFilename(
|
||||
project,
|
||||
filename,
|
||||
'file://' + resourceAbsolutePath
|
||||
);
|
||||
}
|
||||
|
||||
return this._cache.cacheSystemFilename(project, filename, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified URL/filename associated with the given resource.
|
||||
*/
|
||||
static getResourceFullFilename(project, resourceName) {
|
||||
if (project.getResourcesManager().hasResource(resourceName)) {
|
||||
const resourceRelativePath = project
|
||||
.getResourcesManager()
|
||||
.getResource(resourceName)
|
||||
.getFile();
|
||||
return ResourceLoader.getFullFilename(project, resourceRelativePath);
|
||||
}
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
}
|
25
newIDE/app/src/PlatformSpecificAssetsEditor/ImageResizer.js
Normal file
25
newIDE/app/src/PlatformSpecificAssetsEditor/ImageResizer.js
Normal file
@@ -0,0 +1,25 @@
|
||||
//@flow
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
const Jimp = optionalRequire('jimp');
|
||||
|
||||
export const isResizeSupported = () => !!Jimp;
|
||||
|
||||
export const resizeImage = (
|
||||
inputFile: string,
|
||||
outputFile: string,
|
||||
{ width, height }: { width: number, height: number }
|
||||
): Promise<any> => {
|
||||
if (!Jimp) return Promise.resolve(false);
|
||||
|
||||
return Jimp.read(inputFile)
|
||||
.then(function(jimpImage) {
|
||||
return jimpImage.contain(width, height).write(outputFile);
|
||||
})
|
||||
.then(() => {
|
||||
return true;
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error(err);
|
||||
return false;
|
||||
});
|
||||
};
|
@@ -0,0 +1,255 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import path from 'path';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import Dialog from '../UI/Dialog';
|
||||
import { Line } from '../UI/Grid';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
import ResourceSelectorWithThumbnail from '../ObjectEditor/ResourceSelectorWithThumbnail';
|
||||
import {
|
||||
type ResourceSource,
|
||||
type ChooseResourceFunction,
|
||||
} from '../ResourcesList/ResourceSource.flow';
|
||||
import { resizeImage, isResizeSupported } from './ImageResizer';
|
||||
import { showErrorBox } from '../UI/Messages/MessageBox';
|
||||
const gd = global.gd;
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
open: boolean,
|
||||
onClose: Function,
|
||||
onApply: Function,
|
||||
resourceSources: Array<ResourceSource>,
|
||||
onChooseResource: ChooseResourceFunction,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
androidIconResourceNames: Array<string>,
|
||||
iosIconResourceNames: Array<string>,
|
||||
|};
|
||||
|
||||
const androidSizes = [192, 144, 96, 72, 48, 36];
|
||||
const iosSizes = [
|
||||
180,
|
||||
167,
|
||||
152,
|
||||
144,
|
||||
120,
|
||||
114,
|
||||
100,
|
||||
80,
|
||||
76,
|
||||
72,
|
||||
60,
|
||||
58,
|
||||
57,
|
||||
50,
|
||||
40,
|
||||
29,
|
||||
];
|
||||
|
||||
export default class PlatformSpecificAssetsDialog extends React.Component<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = this._loadFrom(props.project);
|
||||
}
|
||||
|
||||
_loadFrom(project: gdProject): State {
|
||||
return {
|
||||
androidIconResourceNames: androidSizes.map(size =>
|
||||
project.getPlatformSpecificAssets().get('android', `icon-${size}`)
|
||||
),
|
||||
iosIconResourceNames: iosSizes.map(size =>
|
||||
project.getPlatformSpecificAssets().get('ios', `icon-${size}`)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
if (
|
||||
(!this.props.open && newProps.open) ||
|
||||
(newProps.open && this.props.project !== newProps.project)
|
||||
) {
|
||||
this.setState(this._loadFrom(newProps.project));
|
||||
}
|
||||
}
|
||||
|
||||
_generateFromFile = () => {
|
||||
const { project, resourceSources, onChooseResource } = this.props;
|
||||
|
||||
const sources = resourceSources.filter(source => source.kind === 'image');
|
||||
if (!sources.length) return;
|
||||
|
||||
onChooseResource(sources[0].name, false).then(resources => {
|
||||
if (!resources.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resourcesManager = project.getResourcesManager();
|
||||
const projectPath = path.dirname(project.getProjectFile());
|
||||
const fullPath = path.resolve(projectPath, resources[0].getFile());
|
||||
|
||||
Promise.all([
|
||||
...androidSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `android-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
...iosSizes.map(size =>
|
||||
resizeImage(
|
||||
fullPath,
|
||||
path.join(projectPath, `ios-icon-${size}.png`),
|
||||
{
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
)
|
||||
),
|
||||
]).then(results => {
|
||||
if (results.indexOf(false) !== -1) {
|
||||
showErrorBox('Some icons could not be generated!');
|
||||
return;
|
||||
}
|
||||
|
||||
const createOrUpdateResource = name => {
|
||||
if (!resourcesManager.hasResource(name)) {
|
||||
const imageResource = new gd.ImageResource();
|
||||
imageResource.setFile(name);
|
||||
imageResource.setName(name);
|
||||
|
||||
resourcesManager.addResource(imageResource);
|
||||
imageResource.delete();
|
||||
} else {
|
||||
resourcesManager.getResource(name).setFile(name);
|
||||
}
|
||||
};
|
||||
|
||||
androidSizes.forEach(size =>
|
||||
createOrUpdateResource(`android-icon-${size}.png`)
|
||||
);
|
||||
iosSizes.forEach(size =>
|
||||
createOrUpdateResource(`ios-icon-${size}.png`)
|
||||
);
|
||||
|
||||
ResourcesLoader.burstUrlsCache();
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
androidIconResourceNames: androidSizes.map(
|
||||
size => `android-icon-${size}.png`
|
||||
),
|
||||
iosIconResourceNames: iosSizes.map(size => `ios-icon-${size}.png`),
|
||||
});
|
||||
}, 200 /* Let a bit of time so that image files can be found */);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_onApply = () => {
|
||||
const { project } = this.props;
|
||||
const { androidIconResourceNames, iosIconResourceNames } = this.state;
|
||||
|
||||
androidSizes.forEach((size, index) => {
|
||||
project
|
||||
.getPlatformSpecificAssets()
|
||||
.set('android', `icon-${size}`, androidIconResourceNames[index]);
|
||||
});
|
||||
iosSizes.forEach((size, index) => {
|
||||
project
|
||||
.getPlatformSpecificAssets()
|
||||
.set('ios', `icon-${size}`, iosIconResourceNames[index]);
|
||||
});
|
||||
|
||||
this.props.onApply();
|
||||
};
|
||||
|
||||
render() {
|
||||
const actions = [
|
||||
<FlatButton
|
||||
label="Cancel"
|
||||
primary={false}
|
||||
onClick={this.props.onClose}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Apply"
|
||||
primary={true}
|
||||
keyboardFocused={true}
|
||||
onClick={this._onApply}
|
||||
/>,
|
||||
];
|
||||
const { project, resourceSources, onChooseResource } = this.props;
|
||||
const { androidIconResourceNames, iosIconResourceNames } = this.state;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
actions={actions}
|
||||
open={this.props.open}
|
||||
onRequestClose={this.props.onClose}
|
||||
autoScrollBodyContent
|
||||
>
|
||||
<Line justifyContent="center">
|
||||
{isResizeSupported() ? (
|
||||
<RaisedButton
|
||||
primary
|
||||
label="Generate icons from a file"
|
||||
onClick={this._generateFromFile}
|
||||
/>
|
||||
) : (
|
||||
<p>
|
||||
Download GDevelop desktop version to generate the Android and iOS
|
||||
icons of your game.
|
||||
</p>
|
||||
)}
|
||||
</Line>
|
||||
<p>Android icons:</p>
|
||||
{androidSizes.map((size, index) => (
|
||||
<ResourceSelectorWithThumbnail
|
||||
key={size}
|
||||
floatingLabelText={`Android icon (${size}x${size} px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceKind="image"
|
||||
resourceName={androidIconResourceNames[index]}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
onChange={resourceName => {
|
||||
const newIcons = [...androidIconResourceNames];
|
||||
newIcons[index] = resourceName;
|
||||
this.setState({
|
||||
androidIconResourceNames: newIcons,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<p>iOS (iPhone and iPad) icons:</p>
|
||||
{iosSizes.map((size, index) => (
|
||||
<ResourceSelectorWithThumbnail
|
||||
key={size}
|
||||
floatingLabelText={`iOS icon (${size}x${size} px)`}
|
||||
project={project}
|
||||
resourceSources={resourceSources}
|
||||
onChooseResource={onChooseResource}
|
||||
resourceKind="image"
|
||||
resourceName={iosIconResourceNames[index]}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
onChange={resourceName => {
|
||||
const newIcons = [...iosIconResourceNames];
|
||||
newIcons[index] = resourceName;
|
||||
this.setState({
|
||||
iosIconResourceNames: newIcons,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,21 +1,6 @@
|
||||
// @flow
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
|
||||
//TODO: Layout, ExternalEvents and ExternalLayout should be moved to a common type definition file
|
||||
//for all GDevelop.js
|
||||
type Layout = {
|
||||
getName: Function,
|
||||
setName: Function,
|
||||
};
|
||||
type ExternalLayout = {
|
||||
getName: Function,
|
||||
setName: Function,
|
||||
};
|
||||
type ExternalEvents = {
|
||||
getName: Function,
|
||||
setName: Function,
|
||||
};
|
||||
|
||||
export const enumerateLayouts = (project: any) =>
|
||||
mapFor(0, project.getLayoutsCount(), i => project.getLayoutAt(i));
|
||||
|
||||
@@ -30,7 +15,7 @@ export const enumerateExternalLayouts = (project: any) =>
|
||||
);
|
||||
|
||||
export const filterProjectItemsList = (
|
||||
list: Array<Layout> | Array<ExternalLayout> | Array<ExternalEvents>,
|
||||
list: Array<gdLayout> | Array<gdExternalLayout> | Array<gdExternalEvents>,
|
||||
searchText: string
|
||||
) => {
|
||||
if (!searchText) return list;
|
||||
|
@@ -1,25 +1,48 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Dialog from '../UI/Dialog';
|
||||
|
||||
export default class ProjectPropertiesDialog extends Component {
|
||||
constructor(props) {
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
open: boolean,
|
||||
onClose: Function,
|
||||
onApply: Function,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
windowDefaultWidth: number,
|
||||
windowDefaultHeight: number,
|
||||
name: string,
|
||||
author: string,
|
||||
packageName: string,
|
||||
orientation: string,
|
||||
|};
|
||||
|
||||
export default class ProjectPropertiesDialog extends React.Component<
|
||||
Props,
|
||||
State
|
||||
> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { ...this._loadFrom(props.project) };
|
||||
this.state = this._loadFrom(props.project);
|
||||
}
|
||||
|
||||
_loadFrom(project) {
|
||||
_loadFrom(project: gdProject): State {
|
||||
return {
|
||||
windowDefaultWidth: project.getMainWindowDefaultWidth(),
|
||||
windowDefaultHeight: project.getMainWindowDefaultHeight(),
|
||||
name: project.getName(),
|
||||
author: project.getAuthor(),
|
||||
packageName: project.getPackageName(),
|
||||
orientation: project.getOrientation(),
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
if (
|
||||
(!this.props.open && newProps.open) ||
|
||||
(newProps.open && this.props.project !== newProps.project)
|
||||
@@ -30,12 +53,22 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
|
||||
_onApply = () => {
|
||||
const { project } = this.props;
|
||||
project.setDefaultWidth(this.state.windowDefaultWidth);
|
||||
project.setDefaultHeight(this.state.windowDefaultHeight);
|
||||
project.setName(this.state.name);
|
||||
project.setAuthor(this.state.author);
|
||||
project.setPackageName(this.state.packageName);
|
||||
if (this.props.onApply) this.props.onApply();
|
||||
const {
|
||||
windowDefaultWidth,
|
||||
windowDefaultHeight,
|
||||
name,
|
||||
author,
|
||||
packageName,
|
||||
orientation,
|
||||
} = this.state;
|
||||
project.setDefaultWidth(windowDefaultWidth);
|
||||
project.setDefaultHeight(windowDefaultHeight);
|
||||
project.setName(name);
|
||||
project.setAuthor(author);
|
||||
project.setPackageName(packageName);
|
||||
project.setOrientation(orientation);
|
||||
|
||||
this.props.onApply();
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -52,6 +85,14 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
onClick={this._onApply}
|
||||
/>,
|
||||
];
|
||||
const {
|
||||
name,
|
||||
windowDefaultWidth,
|
||||
windowDefaultHeight,
|
||||
author,
|
||||
packageName,
|
||||
orientation,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
@@ -59,20 +100,19 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
open={this.props.open}
|
||||
onRequestClose={this.props.onClose}
|
||||
autoScrollBodyContent={true}
|
||||
contentStyle={{ width: '350px' }}
|
||||
>
|
||||
<TextField
|
||||
floatingLabelText="Game name"
|
||||
fullWidth
|
||||
type="text"
|
||||
value={this.state.name}
|
||||
value={name}
|
||||
onChange={(e, value) => this.setState({ name: value })}
|
||||
/>
|
||||
<TextField
|
||||
floatingLabelText="Game's window width"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={this.state.windowDefaultWidth}
|
||||
value={windowDefaultWidth}
|
||||
onChange={(e, value) =>
|
||||
this.setState({
|
||||
windowDefaultWidth: Math.max(0, parseInt(value, 10)),
|
||||
@@ -82,7 +122,7 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
floatingLabelText="Game's window height"
|
||||
fullWidth
|
||||
type="number"
|
||||
value={this.state.windowDefaultHeight}
|
||||
value={windowDefaultHeight}
|
||||
onChange={(e, value) =>
|
||||
this.setState({
|
||||
windowDefaultHeight: Math.max(0, parseInt(value, 10)),
|
||||
@@ -93,7 +133,7 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
fullWidth
|
||||
hintText="Your name"
|
||||
type="text"
|
||||
value={this.state.author}
|
||||
value={author}
|
||||
onChange={(e, value) => this.setState({ author: value })}
|
||||
/>
|
||||
<TextField
|
||||
@@ -101,16 +141,19 @@ export default class ProjectPropertiesDialog extends Component {
|
||||
fullWidth
|
||||
hintText="com.example.mygame"
|
||||
type="text"
|
||||
value={this.state.packageName}
|
||||
value={packageName}
|
||||
onChange={(e, value) => this.setState({ packageName: value })}
|
||||
/>
|
||||
<TextField
|
||||
floatingLabelText="Icon"
|
||||
<SelectField
|
||||
fullWidth
|
||||
type="text"
|
||||
disabled
|
||||
value="Coming soon"
|
||||
/>
|
||||
floatingLabelText="Device orientation (for iOS and Android)"
|
||||
value={orientation}
|
||||
onChange={(e, i, value) => this.setState({orientation: value})}
|
||||
>
|
||||
<MenuItem value="default" primaryText="Platform default" />
|
||||
<MenuItem value="landscape" primaryText="Landscape" />
|
||||
<MenuItem value="portrait" primaryText="Portrait" />
|
||||
</SelectField>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { List, ListItem } from 'material-ui/List';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import SearchBar from 'material-ui-search-bar';
|
||||
@@ -65,7 +66,10 @@ const ThemableProjectStructureItem = ({ muiTheme, ...otherProps }) => (
|
||||
|
||||
const ProjectStructureItem = muiThemeable()(ThemableProjectStructureItem);
|
||||
|
||||
class ThemableItem extends Component {
|
||||
class ThemableItem extends React.Component<*, *> {
|
||||
textField: ?Object;
|
||||
_iconMenu: ?Object;
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!prevProps.editingName && this.props.editingName) {
|
||||
setTimeout(() => {
|
||||
@@ -126,7 +130,7 @@ class ThemableItem extends Component {
|
||||
onKeyPress={event => {
|
||||
if (event.charCode === 13) {
|
||||
// enter key pressed
|
||||
this.textField.blur();
|
||||
if (this.textField) this.textField.blur();
|
||||
this.props.onRename(event.target.value);
|
||||
}
|
||||
}}
|
||||
@@ -157,33 +161,65 @@ const Item = muiThemeable()(ThemableItem);
|
||||
|
||||
const AddItem = makeAddItem(ListItem);
|
||||
|
||||
export default class ProjectManager extends React.Component {
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
onDeleteLayout: gdLayout => void,
|
||||
onDeleteExternalEvents: gdExternalEvents => void,
|
||||
onDeleteExternalLayout: gdExternalLayout => void,
|
||||
onRenameLayout: (string, string) => void,
|
||||
onRenameExternalEvents: (string, string) => void,
|
||||
onRenameExternalLayout: (string, string) => void,
|
||||
onOpenLayout: string => void,
|
||||
onOpenExternalEvents: string => void,
|
||||
onOpenExternalLayout: string => void,
|
||||
onSaveProject: () => void,
|
||||
onCloseProject: () => void,
|
||||
onExportProject: () => void,
|
||||
onOpenPreferences: () => void,
|
||||
onOpenResources: () => void,
|
||||
onAddLayout: () => void,
|
||||
onAddExternalEvents: () => void,
|
||||
onAddExternalLayout: () => void,
|
||||
onOpenPlatformSpecificAssets: () => void,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
renamedItemKind: ?string,
|
||||
renamedItemName: string,
|
||||
searchText: string,
|
||||
projectPropertiesDialogOpen: boolean,
|
||||
variablesEditorOpen: boolean,
|
||||
|};
|
||||
|
||||
export default class ProjectManager extends React.Component<Props, State> {
|
||||
state = {
|
||||
renamedItemKind: null,
|
||||
renamedItemName: '',
|
||||
searchText: '',
|
||||
projectPropertiesDialogOpen: false,
|
||||
variablesEditorOpen: false,
|
||||
};
|
||||
|
||||
_onEditName = (kind, name) => {
|
||||
_onEditName = (kind: ?string, name: string) => {
|
||||
this.setState({
|
||||
renamedItemKind: kind,
|
||||
renamedItemName: name,
|
||||
});
|
||||
};
|
||||
|
||||
_copyLayout = layout => {
|
||||
_copyLayout = (layout: gdLayout) => {
|
||||
Clipboard.set(LAYOUT_CLIPBOARD_KIND, {
|
||||
layout: serializeToJSObject(layout),
|
||||
name: layout.getName(),
|
||||
});
|
||||
};
|
||||
|
||||
_cutLayout = layout => {
|
||||
_cutLayout = (layout: gdLayout) => {
|
||||
this._copyLayout(layout);
|
||||
this.props.onDeleteLayout(layout);
|
||||
};
|
||||
|
||||
_pasteLayout = index => {
|
||||
_pasteLayout = (index: number) => {
|
||||
if (!Clipboard.has(LAYOUT_CLIPBOARD_KIND)) return;
|
||||
|
||||
const { layout: copiedLayout, name } = Clipboard.get(LAYOUT_CLIPBOARD_KIND);
|
||||
@@ -202,23 +238,24 @@ export default class ProjectManager extends React.Component {
|
||||
project
|
||||
);
|
||||
newLayout.setName(newName);
|
||||
newLayout.updateBehaviorsSharedData(project);
|
||||
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
_copyExternalEvents = externalEvents => {
|
||||
_copyExternalEvents = (externalEvents: gdExternalEvents) => {
|
||||
Clipboard.set(EXTERNAL_EVENTS_CLIPBOARD_KIND, {
|
||||
externalEvents: serializeToJSObject(externalEvents),
|
||||
name: externalEvents.getName(),
|
||||
});
|
||||
};
|
||||
|
||||
_cutExternalEvents = externalEvents => {
|
||||
_cutExternalEvents = (externalEvents: gdExternalEvents) => {
|
||||
this._copyExternalEvents(externalEvents);
|
||||
this.props.onDeleteExternalEvents(externalEvents);
|
||||
};
|
||||
|
||||
_pasteExternalEvents = index => {
|
||||
_pasteExternalEvents = (index: number) => {
|
||||
if (!Clipboard.has(EXTERNAL_EVENTS_CLIPBOARD_KIND)) return;
|
||||
|
||||
const { externalEvents: copiedExternalEvents, name } = Clipboard.get(
|
||||
@@ -243,19 +280,19 @@ export default class ProjectManager extends React.Component {
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
_copyExternalLayout = externalLayout => {
|
||||
_copyExternalLayout = (externalLayout: gdExternalLayout) => {
|
||||
Clipboard.set(EXTERNAL_LAYOUT_CLIPBOARD_KIND, {
|
||||
externalLayout: serializeToJSObject(externalLayout),
|
||||
name: externalLayout.getName(),
|
||||
});
|
||||
};
|
||||
|
||||
_cutExternalLayout = externalLayout => {
|
||||
_cutExternalLayout = (externalLayout: gdExternalLayout) => {
|
||||
this._copyExternalLayout(externalLayout);
|
||||
this.props.onDeleteExternalLayout(externalLayout);
|
||||
};
|
||||
|
||||
_pasteExternalLayout = index => {
|
||||
_pasteExternalLayout = (index: number) => {
|
||||
if (!Clipboard.has(EXTERNAL_LAYOUT_CLIPBOARD_KIND)) return;
|
||||
|
||||
const { externalLayout: copiedExternalLayout, name } = Clipboard.get(
|
||||
@@ -327,10 +364,6 @@ export default class ProjectManager extends React.Component {
|
||||
<div style={styles.container}>
|
||||
<List style={styles.list}>
|
||||
{this._renderMenu()}
|
||||
{/* <ProjectStructureItem
|
||||
primaryText="Resources"
|
||||
leftIcon={<ListIcon src="res/ribbon_default/image32.png" />}
|
||||
/> */}
|
||||
<ProjectStructureItem
|
||||
primaryText="Game settings"
|
||||
leftIcon={
|
||||
@@ -353,6 +386,18 @@ export default class ProjectManager extends React.Component {
|
||||
leftIcon={<ListIcon src="res/ribbon_default/editname32.png" />}
|
||||
onClick={() => this.setState({ variablesEditorOpen: true })}
|
||||
/>,
|
||||
<ListItem
|
||||
key="icons"
|
||||
primaryText="Icons"
|
||||
leftIcon={<ListIcon src="res/ribbon_default/image32.png" />}
|
||||
onClick={() => this.props.onOpenPlatformSpecificAssets()}
|
||||
/>,
|
||||
<ListItem
|
||||
key="resources"
|
||||
primaryText="Resources"
|
||||
leftIcon={<ListIcon src="res/ribbon_default/image32.png" />}
|
||||
onClick={() => this.props.onOpenResources()}
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
<ProjectStructureItem
|
||||
@@ -366,7 +411,7 @@ export default class ProjectManager extends React.Component {
|
||||
enumerateLayouts(project),
|
||||
searchText
|
||||
)
|
||||
.map((layout, i) => {
|
||||
.map((layout: gdLayout, i: number) => {
|
||||
const name = layout.getName();
|
||||
return (
|
||||
<Item
|
||||
|
52
newIDE/app/src/PropertiesEditor/SemiControlledTextField.js
Normal file
52
newIDE/app/src/PropertiesEditor/SemiControlledTextField.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import TextField from 'material-ui/TextField';
|
||||
|
||||
type State = {
|
||||
focused: boolean,
|
||||
text: ?string,
|
||||
};
|
||||
|
||||
/**
|
||||
* This component works like a material-ui TextField, except that
|
||||
* the value passed as props is not forced into the text field when the user
|
||||
* is typing. This is useful if the parent component can do modifications on the value:
|
||||
* the user won't be interrupted or have the value changed until he blurs the field.
|
||||
*/
|
||||
export default class SemiControlledTextField extends React.Component<*, State> {
|
||||
state = {
|
||||
focused: false,
|
||||
text: null,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, onChange, ...otherProps } = this.props;
|
||||
|
||||
return (
|
||||
<TextField
|
||||
{...otherProps}
|
||||
value={this.state.focused ? this.state.text : value}
|
||||
onFocus={() => {
|
||||
this.setState({
|
||||
focused: true,
|
||||
text: this.props.value,
|
||||
});
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
this.setState({
|
||||
text: newValue,
|
||||
});
|
||||
|
||||
onChange(newValue);
|
||||
}}
|
||||
onBlur={event => {
|
||||
onChange(event.target.value);
|
||||
this.setState({
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import SemiControlledTextField from './SemiControlledTextField';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import Subheader from 'material-ui/Subheader';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
@@ -53,13 +53,13 @@ export default class PropertiesEditor extends Component {
|
||||
);
|
||||
} else if (field.valueType === 'number') {
|
||||
return (
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
value={this._getFieldValue(this.props.instances, field)}
|
||||
key={field.name}
|
||||
id={field.name}
|
||||
floatingLabelText={field.name}
|
||||
floatingLabelFixed={true}
|
||||
onChange={(event, newValue) => {
|
||||
floatingLabelFixed
|
||||
onChange={newValue => {
|
||||
this.props.instances.forEach(i =>
|
||||
field.setValue(i, parseFloat(newValue) || 0)
|
||||
);
|
||||
@@ -72,16 +72,17 @@ export default class PropertiesEditor extends Component {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<TextField
|
||||
<SemiControlledTextField
|
||||
value={this._getFieldValue(
|
||||
this.props.instances,
|
||||
field,
|
||||
'(Multiple values)'
|
||||
)}
|
||||
key={field.name}
|
||||
id={field.name}
|
||||
floatingLabelText={field.name}
|
||||
floatingLabelFixed={true}
|
||||
onChange={(event, newValue) => {
|
||||
floatingLabelFixed
|
||||
onChange={newValue => {
|
||||
this.props.instances.forEach(i =>
|
||||
field.setValue(i, newValue || '')
|
||||
);
|
||||
@@ -107,7 +108,7 @@ export default class PropertiesEditor extends Component {
|
||||
value={this._getFieldValue(this.props.instances, field)}
|
||||
key={field.name}
|
||||
floatingLabelText={field.name}
|
||||
floatingLabelFixed={true}
|
||||
floatingLabelFixed
|
||||
onChange={(event, index, newValue) => {
|
||||
this.props.instances.forEach(i =>
|
||||
field.setValue(i, parseFloat(newValue) || 0)
|
||||
@@ -130,7 +131,7 @@ export default class PropertiesEditor extends Component {
|
||||
)}
|
||||
key={field.name}
|
||||
floatingLabelText={field.name}
|
||||
floatingLabelFixed={true}
|
||||
floatingLabelFixed
|
||||
onChange={(event, index, newValue) => {
|
||||
this.props.instances.forEach(i =>
|
||||
field.setValue(i, newValue || '')
|
||||
|
101
newIDE/app/src/ResourcesEditor/ResourcePropertiesEditor/index.js
Normal file
101
newIDE/app/src/ResourcesEditor/ResourcePropertiesEditor/index.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import EmptyMessage from '../../UI/EmptyMessage';
|
||||
import PropertiesEditor from '../../PropertiesEditor';
|
||||
import ImagePreview from '../../ObjectEditor/ImagePreview'; //TODO: Move ImagePreview out of ObjectEditor
|
||||
import ResourcesLoader from '../../ResourcesLoader';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
imagePreview: { flex: 1 },
|
||||
propertiesContainer: {
|
||||
padding: 10,
|
||||
overflowY: 'scroll',
|
||||
overflowX: 'hidden',
|
||||
flex: 2,
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
resources: Array<gdResource>,
|
||||
|};
|
||||
|
||||
export default class ResourcePropertiesEditor extends React.Component<
|
||||
Props,
|
||||
{}
|
||||
> {
|
||||
schema = [
|
||||
{
|
||||
name: 'Resource name',
|
||||
valueType: 'string',
|
||||
disabled: true,
|
||||
getValue: (resource: gdResource) => resource.getName(),
|
||||
setValue: (resource: gdResource, newValue: string) =>
|
||||
resource.setName(newValue),
|
||||
},
|
||||
{
|
||||
name: 'File',
|
||||
valueType: 'string',
|
||||
getValue: (resource: gdResource) => resource.getFile(),
|
||||
setValue: (resource: gdResource, newValue: string) =>
|
||||
resource.setFile(newValue),
|
||||
},
|
||||
];
|
||||
|
||||
_renderEmpty() {
|
||||
return (
|
||||
<EmptyMessage>
|
||||
Resources are automatically added to your project whenever you add an
|
||||
image to an object. Choose a resource to display its properties.
|
||||
</EmptyMessage>
|
||||
);
|
||||
}
|
||||
|
||||
_renderResourcesProperties() {
|
||||
const { resources } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={styles.propertiesContainer}
|
||||
key={resources.map(resource => '' + resource.ptr).join(';')}
|
||||
>
|
||||
<PropertiesEditor schema={this.schema} instances={resources} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPreview() {
|
||||
const { resources, project, resourcesLoader } = this.props;
|
||||
if (!resources || !resources.length) return;
|
||||
|
||||
return (
|
||||
<ImagePreview
|
||||
style={styles.imagePreview}
|
||||
resourceName={resources[0].getName()}
|
||||
resourcesLoader={resourcesLoader}
|
||||
project={project}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { resources } = this.props;
|
||||
|
||||
return (
|
||||
<Paper style={styles.container}>
|
||||
{this._renderPreview()}
|
||||
{!resources || !resources.length
|
||||
? this._renderEmpty()
|
||||
: this._renderResourcesProperties()}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
}
|
39
newIDE/app/src/ResourcesEditor/Toolbar.js
Normal file
39
newIDE/app/src/ResourcesEditor/Toolbar.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// @flow
|
||||
import React, { PureComponent } from 'react';
|
||||
import { translate, type TranslatorProps } from 'react-i18next';
|
||||
import { ToolbarGroup } from 'material-ui/Toolbar';
|
||||
import ToolbarIcon from '../UI/ToolbarIcon';
|
||||
import ToolbarSeparator from '../UI/ToolbarSeparator';
|
||||
|
||||
type Props = {|
|
||||
onDeleteSelection: () => void,
|
||||
canDelete: boolean,
|
||||
onOpenProperties: () => void,
|
||||
|} & TranslatorProps;
|
||||
|
||||
type State = {||};
|
||||
|
||||
export class Toolbar extends PureComponent<Props, State> {
|
||||
render() {
|
||||
const { t, canDelete } = this.props;
|
||||
|
||||
return (
|
||||
<ToolbarGroup lastChild>
|
||||
<ToolbarIcon
|
||||
onClick={this.props.onOpenProperties}
|
||||
src="res/ribbon_default/editprop32.png"
|
||||
tooltip={t('Open the properties panel')}
|
||||
/>
|
||||
<ToolbarSeparator />
|
||||
<ToolbarIcon
|
||||
onClick={this.props.onDeleteSelection}
|
||||
src="res/ribbon_default/deleteselected32.png"
|
||||
disabled={!canDelete}
|
||||
tooltip={t('Delete the selected resource')}
|
||||
/>
|
||||
</ToolbarGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(Toolbar);
|
154
newIDE/app/src/ResourcesEditor/index.js
Normal file
154
newIDE/app/src/ResourcesEditor/index.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import ResourcesList from '../ResourcesList';
|
||||
import ResourcePropertiesEditor from './ResourcePropertiesEditor';
|
||||
import Toolbar from './Toolbar';
|
||||
import EditorMosaic, { MosaicWindow } from '../UI/EditorMosaic';
|
||||
import InfoBar from '../UI/Messages/InfoBar';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
};
|
||||
|
||||
type State = {
|
||||
showPropertiesInfoBar: boolean,
|
||||
selectedResource: ?gdResource,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
setToolbar: React.Node => void,
|
||||
project: gdProject,
|
||||
onDeleteResource: (resource: gdResource, cb: (boolean) => void) => void,
|
||||
onRenameResource: (
|
||||
resource: gdResource,
|
||||
newName: string,
|
||||
cb: (boolean) => void
|
||||
) => void,
|
||||
};
|
||||
|
||||
export default class InstancesFullEditor extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
setToolbar: () => {},
|
||||
};
|
||||
|
||||
editorMosaic: ?EditorMosaic = null;
|
||||
_propertiesEditor: ?ResourcePropertiesEditor = null;
|
||||
_resourcesList: ?ResourcesList = null;
|
||||
resourcesLoader = ResourcesLoader;
|
||||
state = {
|
||||
showPropertiesInfoBar: false,
|
||||
selectedResource: null,
|
||||
};
|
||||
|
||||
updateToolbar() {
|
||||
this.props.setToolbar(
|
||||
<Toolbar
|
||||
onOpenProperties={this.openProperties}
|
||||
canDelete={!!this.state.selectedResource}
|
||||
onDeleteSelection={() =>
|
||||
this.deleteResource(this.state.selectedResource)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
deleteResource = (resource: ?gdResource) => {
|
||||
const { project, onDeleteResource } = this.props;
|
||||
if (!resource) return;
|
||||
|
||||
//eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
"Are you sure you want to remove this resource? This can't be undone."
|
||||
);
|
||||
if (!answer) return;
|
||||
|
||||
onDeleteResource(resource, doRemove => {
|
||||
if (!doRemove || !resource) return;
|
||||
|
||||
project.getResourcesManager().removeResource(resource.getName());
|
||||
this.setState(
|
||||
{
|
||||
selectedResource: null,
|
||||
},
|
||||
() => {
|
||||
if (this._resourcesList) this._resourcesList.forceUpdateList();
|
||||
this.updateToolbar();
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
openProperties = () => {
|
||||
if (!this.editorMosaic) return;
|
||||
if (!this.editorMosaic.openEditor('properties')) {
|
||||
this.setState({
|
||||
showPropertiesInfoBar: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onResourceSelected = (selectedResource: ?gdResource) => {
|
||||
this.setState(
|
||||
{
|
||||
selectedResource,
|
||||
},
|
||||
() => {
|
||||
if (this._propertiesEditor) this._propertiesEditor.forceUpdate();
|
||||
this.updateToolbar();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { project, onRenameResource } = this.props;
|
||||
const { selectedResource } = this.state;
|
||||
|
||||
const editors = {
|
||||
properties: (
|
||||
<MosaicWindow
|
||||
title="Properties"
|
||||
// Pass resources to force MosaicWindow update when selectedResource is changed
|
||||
resources={selectedResource ? [selectedResource] : []}
|
||||
>
|
||||
<ResourcePropertiesEditor
|
||||
key={selectedResource ? selectedResource.ptr : undefined}
|
||||
resources={selectedResource ? [selectedResource] : []}
|
||||
project={project}
|
||||
resourcesLoader={this.resourcesLoader}
|
||||
ref={propertiesEditor =>
|
||||
(this._propertiesEditor = propertiesEditor)}
|
||||
/>
|
||||
</MosaicWindow>
|
||||
),
|
||||
'resources-list': (
|
||||
<ResourcesList
|
||||
project={project}
|
||||
onDeleteResource={this.deleteResource}
|
||||
onRenameResource={onRenameResource}
|
||||
onSelectResource={this._onResourceSelected}
|
||||
selectedResource={selectedResource}
|
||||
ref={resourcesList => (this._resourcesList = resourcesList)}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<EditorMosaic
|
||||
editors={editors}
|
||||
ref={editorMosaic => (this.editorMosaic = editorMosaic)}
|
||||
initialEditorNames={['properties', 'resources-list']}
|
||||
/>
|
||||
<InfoBar
|
||||
message="Properties panel is already opened"
|
||||
show={!!this.state.showPropertiesInfoBar}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
19
newIDE/app/src/ResourcesList/EnumerateResources.js
Normal file
19
newIDE/app/src/ResourcesList/EnumerateResources.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// @flow
|
||||
|
||||
export const filterResourcesList = (
|
||||
list: Array<gdResource>,
|
||||
searchText: string
|
||||
): Array<gdResource> => {
|
||||
if (!searchText) return list;
|
||||
|
||||
const lowercaseSearchText = searchText.toLowerCase();
|
||||
|
||||
return list.filter((resource: gdResource) => {
|
||||
return (
|
||||
resource
|
||||
.getName()
|
||||
.toLowerCase()
|
||||
.indexOf(lowercaseSearchText) !== -1
|
||||
);
|
||||
});
|
||||
};
|
@@ -20,6 +20,14 @@ export default class ResourceSelector extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.initialResourceName !== this.props.initialResourceName) {
|
||||
this.setState({
|
||||
resourceName: nextProps.initialResourceName || '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_getDefaultItems() {
|
||||
const sources = this.props.resourceSources || [];
|
||||
return [
|
||||
@@ -40,7 +48,7 @@ export default class ResourceSelector extends Component {
|
||||
}
|
||||
|
||||
_loadFrom(resourcesManager) {
|
||||
this.allResourcesNames = resourcesManager.getAllResourcesList().toJSArray();
|
||||
this.allResourcesNames = resourcesManager.getAllResourceNames().toJSArray();
|
||||
if (this.props.resourceKind) {
|
||||
this.allResourcesNames = this.allResourcesNames.filter(resourceName => {
|
||||
return (
|
14
newIDE/app/src/ResourcesList/ResourceSource.flow.js
Normal file
14
newIDE/app/src/ResourcesList/ResourceSource.flow.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
|
||||
export type ResourceSource = {
|
||||
name: string,
|
||||
displayName: string,
|
||||
kind: 'image' | 'audio',
|
||||
component: React.Component<*, *>,
|
||||
};
|
||||
|
||||
export type ChooseResourceFunction = (
|
||||
sourceName: string,
|
||||
multiSelection: boolean
|
||||
) => Promise<Array<gdResource>>;
|
187
newIDE/app/src/ResourcesList/index.js
Normal file
187
newIDE/app/src/ResourcesList/index.js
Normal file
@@ -0,0 +1,187 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { AutoSizer } from 'react-virtualized';
|
||||
import SortableVirtualizedItemList from '../UI/SortableVirtualizedItemList';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import SearchBar from 'material-ui-search-bar';
|
||||
import { showWarningBox } from '../UI/Messages/MessageBox';
|
||||
import { filterResourcesList } from './EnumerateResources';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
height: '100%',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
listContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
};
|
||||
|
||||
type State = {|
|
||||
renamedResource: ?gdResource,
|
||||
searchText: string,
|
||||
|};
|
||||
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
selectedResource: ?gdResource,
|
||||
onSelectResource: (resource: gdResource) => void,
|
||||
onDeleteResource: (resource: gdResource) => void,
|
||||
onRenameResource: (
|
||||
resource: gdResource,
|
||||
newName: string,
|
||||
cb: (boolean) => void
|
||||
) => void,
|
||||
|};
|
||||
|
||||
export default class ResourcesList extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
onDeleteResource: (resource: gdResource, cb: boolean => void) => cb(true),
|
||||
onRenameResource: (
|
||||
resource: gdResource,
|
||||
newName: string,
|
||||
cb: boolean => void
|
||||
) => cb(true),
|
||||
};
|
||||
|
||||
sortableList: any;
|
||||
state: State = {
|
||||
renamedResource: null,
|
||||
searchText: '',
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps: Props, nextState: State) {
|
||||
// The component is costly to render, so avoid any re-rendering as much
|
||||
// as possible.
|
||||
// We make the assumption that no changes to resources list is made outside
|
||||
// from the component.
|
||||
// If a change is made, the component won't notice it: you have to manually
|
||||
// call forceUpdate.
|
||||
|
||||
if (
|
||||
this.state.renamedResource !== nextState.renamedResource ||
|
||||
this.state.searchText !== nextState.searchText
|
||||
)
|
||||
return true;
|
||||
|
||||
if (
|
||||
this.props.project !== nextProps.project ||
|
||||
this.props.selectedResource !== nextProps.selectedResource
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_deleteResource = (resource: gdResource) => {
|
||||
this.props.onDeleteResource(resource);
|
||||
};
|
||||
|
||||
_editName = (resource: ?gdResource) => {
|
||||
this.setState(
|
||||
{
|
||||
renamedResource: resource,
|
||||
},
|
||||
() => this.sortableList.getWrappedInstance().forceUpdateGrid()
|
||||
);
|
||||
};
|
||||
|
||||
_rename = (resource: gdResource, newName: string) => {
|
||||
const { project } = this.props;
|
||||
this.setState({
|
||||
renamedResource: null,
|
||||
});
|
||||
|
||||
if (resource.getName() === newName) return;
|
||||
|
||||
if (project.getResourcesManager().hasResource(newName)) {
|
||||
showWarningBox('Another resource with this name already exists');
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onRenameResource(resource, newName, doRename => {
|
||||
if (!doRename) return;
|
||||
resource.setName(newName);
|
||||
this.forceUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
_move = (oldIndex: number, newIndex: number) => {
|
||||
const { project } = this.props;
|
||||
|
||||
project.getResourcesManager().moveResource(oldIndex, newIndex);
|
||||
this.forceUpdateList();
|
||||
};
|
||||
|
||||
forceUpdateList = () => {
|
||||
this.forceUpdate();
|
||||
this.sortableList.getWrappedInstance().forceUpdateGrid();
|
||||
};
|
||||
|
||||
_renderResourceMenuTemplate = (resource: gdResource) => {
|
||||
return [
|
||||
{
|
||||
label: 'Rename',
|
||||
click: () => this._editName(resource),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
click: () => this._deleteResource(resource),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
render() {
|
||||
const { project, selectedResource, onSelectResource } = this.props;
|
||||
const { searchText } = this.state;
|
||||
|
||||
const resourcesManager = project.getResourcesManager();
|
||||
const allResourcesList = resourcesManager
|
||||
.getAllResourceNames()
|
||||
.toJSArray()
|
||||
.map(resourceName => resourcesManager.getResource(resourceName));
|
||||
const filteredList = filterResourcesList(allResourcesList, searchText);
|
||||
|
||||
// Force List component to be mounted again if project or objectsContainer
|
||||
// has been changed. Avoid accessing to invalid objects that could
|
||||
// crash the app.
|
||||
const listKey = project.ptr;
|
||||
|
||||
return (
|
||||
<Paper style={styles.container}>
|
||||
<div style={styles.listContainer}>
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<SortableVirtualizedItemList
|
||||
key={listKey}
|
||||
ref={sortableList => (this.sortableList = sortableList)}
|
||||
fullList={filteredList}
|
||||
width={width}
|
||||
height={height}
|
||||
selectedItem={selectedResource}
|
||||
onItemSelected={onSelectResource}
|
||||
renamedItem={this.state.renamedResource}
|
||||
onRename={this._rename}
|
||||
onSortEnd={({ oldIndex, newIndex }) =>
|
||||
this._move(oldIndex, newIndex)}
|
||||
helperClass="sortable-helper"
|
||||
distance={30}
|
||||
buildMenuTemplate={this._renderResourceMenuTemplate}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
<SearchBar
|
||||
value={searchText}
|
||||
onRequestSearch={() => {}}
|
||||
onChange={text =>
|
||||
this.setState({
|
||||
searchText: text,
|
||||
})}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
}
|
107
newIDE/app/src/ResourcesLoader/index.js
Normal file
107
newIDE/app/src/ResourcesLoader/index.js
Normal file
@@ -0,0 +1,107 @@
|
||||
// @flow
|
||||
import optionalRequire from '../Utils/OptionalRequire.js';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
|
||||
class UrlsCache {
|
||||
projectCache = {};
|
||||
|
||||
_getProjectCache(project: gdProject) {
|
||||
const cache = this.projectCache[project.ptr];
|
||||
if (!cache) {
|
||||
return (this.projectCache[project.ptr] = {});
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
getCachedUrl(project: gdProject, filename: string) {
|
||||
const cache = this._getProjectCache(project);
|
||||
return cache[filename];
|
||||
}
|
||||
|
||||
cacheUrl(project: gdProject, url: string) {
|
||||
const cache = this._getProjectCache(project);
|
||||
return (cache[url] = url);
|
||||
}
|
||||
|
||||
cacheLocalFileUrl(
|
||||
project: gdProject,
|
||||
filename: string,
|
||||
systemFilename: string
|
||||
) {
|
||||
const cache = this._getProjectCache(project);
|
||||
|
||||
// The URL is cached with an extra "cache-bursting" parameter.
|
||||
// If the cache is emptied or changed, local files will have another
|
||||
// value for this parameter, forcing the browser to reload the images.
|
||||
return (cache[filename] = `${systemFilename}?cache=${Date.now()}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class globally used in the whole IDE to get URLs to resources of games
|
||||
* (notably images).
|
||||
*/
|
||||
export default class ResourcesLoader {
|
||||
static _cache = new UrlsCache();
|
||||
|
||||
static isLocalFile(filename: string): boolean {
|
||||
return (
|
||||
filename.indexOf('data:') !== 0 &&
|
||||
filename.indexOf('http://') !== 0 &&
|
||||
filename.indexOf('https://') !== 0 &&
|
||||
filename.indexOf('ftp://') !== 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-create a new cache for URLs. Call this to force local
|
||||
* file to be loaded again.
|
||||
*/
|
||||
static burstUrlsCache() {
|
||||
ResourcesLoader._cache = new UrlsCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified URL/filename for a filename relative to the project.
|
||||
*/
|
||||
static getFullUrl(project: gdProject, filename: string) {
|
||||
const cachedUrl = ResourcesLoader._cache.getCachedUrl(project, filename);
|
||||
if (cachedUrl) return cachedUrl;
|
||||
|
||||
if (electron && ResourcesLoader.isLocalFile(filename)) {
|
||||
// Support local filesystem with Electron
|
||||
const file = project.getProjectFile();
|
||||
const projectPath = path.dirname(file);
|
||||
const resourceAbsolutePath = path
|
||||
.resolve(projectPath, filename)
|
||||
.replace(/\\/g, '/');
|
||||
|
||||
console.info('Caching resolved local filename:', resourceAbsolutePath);
|
||||
return this._cache.cacheLocalFileUrl(
|
||||
project,
|
||||
filename,
|
||||
'file://' + resourceAbsolutePath
|
||||
);
|
||||
}
|
||||
|
||||
// URLs to non local files are unchanged
|
||||
return this._cache.cacheUrl(project, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fully qualified URL/filename associated with the given resource.
|
||||
*/
|
||||
static getResourceFullUrl(project: gdProject, resourceName: string) {
|
||||
if (project.getResourcesManager().hasResource(resourceName)) {
|
||||
const resourceRelativePath = project
|
||||
.getResourcesManager()
|
||||
.getResource(resourceName)
|
||||
.getFile();
|
||||
return ResourcesLoader.getFullUrl(project, resourceRelativePath);
|
||||
}
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
}
|
@@ -1,9 +1,13 @@
|
||||
import React, { Component } from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import Dialog from '../../UI/Dialog';
|
||||
import ColorField from '../../UI/ColorField';
|
||||
import EmptyMessage from '../../UI/EmptyMessage';
|
||||
import PropertiesEditor from '../../PropertiesEditor';
|
||||
import propertiesMapToSchema from '../../PropertiesEditor/PropertiesMapToSchema';
|
||||
import some from 'lodash/some';
|
||||
|
||||
export default class ScenePropertiesDialog extends Component {
|
||||
constructor(props) {
|
||||
@@ -43,20 +47,50 @@ export default class ScenePropertiesDialog extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { layout, project } = this.props;
|
||||
const actions = [
|
||||
// TODO: Add support for cancelling modifications made to BehaviorSharedData
|
||||
// (either by enhancing a function like propertiesMapToSchema or using copies)
|
||||
// and then re-enable cancel button.
|
||||
// <FlatButton
|
||||
// label="Cancel"
|
||||
// primary={false}
|
||||
// onClick={this.props.onClose}
|
||||
// />,
|
||||
<FlatButton
|
||||
label="Cancel"
|
||||
primary={false}
|
||||
onClick={this.props.onClose}
|
||||
/>,
|
||||
<FlatButton
|
||||
label="Apply"
|
||||
label="Ok"
|
||||
key="ok"
|
||||
primary={true}
|
||||
keyboardFocused={true}
|
||||
onClick={this._onApply}
|
||||
/>,
|
||||
];
|
||||
|
||||
const allBehaviorSharedDataNames = layout
|
||||
.getAllBehaviorSharedDataNames()
|
||||
.toJSArray();
|
||||
|
||||
const propertiesEditors = allBehaviorSharedDataNames.map(name => {
|
||||
const sharedData = layout.getBehaviorSharedData(name);
|
||||
|
||||
const properties = sharedData.getProperties(project);
|
||||
const propertiesSchema = propertiesMapToSchema(
|
||||
properties,
|
||||
sharedData => sharedData.getProperties(project),
|
||||
(sharedData, name, value) =>
|
||||
sharedData.updateProperty(name, value, project)
|
||||
);
|
||||
|
||||
return (
|
||||
!!propertiesSchema.length && (
|
||||
<PropertiesEditor
|
||||
schema={propertiesSchema}
|
||||
instances={[sharedData]}
|
||||
/>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
actions={actions}
|
||||
@@ -88,6 +122,13 @@ export default class ScenePropertiesDialog extends Component {
|
||||
this.props.onClose();
|
||||
}}
|
||||
/>
|
||||
{!some(propertiesEditors) && (
|
||||
<EmptyMessage>
|
||||
Any additional properties will appear here if you add behaviors to
|
||||
objects, like Physics behavior.
|
||||
</EmptyMessage>
|
||||
)}
|
||||
{propertiesEditors}
|
||||
{this.props.onOpenMoreSettings && (
|
||||
<RaisedButton
|
||||
label="Open advanced settings"
|
||||
|
@@ -279,9 +279,12 @@ export default class InstancesFullEditor extends Component {
|
||||
};
|
||||
|
||||
_onInstancesMoved = instances => {
|
||||
this.setState({
|
||||
history: saveToHistory(this.state.history, this.props.initialInstances),
|
||||
}, () => this.forceUpdatePropertiesEditor());
|
||||
this.setState(
|
||||
{
|
||||
history: saveToHistory(this.state.history, this.props.initialInstances),
|
||||
},
|
||||
() => this.forceUpdatePropertiesEditor()
|
||||
);
|
||||
};
|
||||
|
||||
_onInstancesModified = instances => {
|
||||
@@ -476,6 +479,11 @@ export default class InstancesFullEditor extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
updateBehaviorsSharedData = () => {
|
||||
const { layout, project } = this.props;
|
||||
layout.updateBehaviorsSharedData(project);
|
||||
};
|
||||
|
||||
forceUpdateObjectsList = () => {
|
||||
if (this._objectsList) this._objectsList.forceUpdateList();
|
||||
};
|
||||
@@ -544,6 +552,7 @@ export default class InstancesFullEditor extends Component {
|
||||
onEditObject={this.props.onEditObject || this.editObject}
|
||||
onDeleteObject={this._onDeleteObject}
|
||||
onRenameObject={this._onRenameObject}
|
||||
onObjectPasted={() => this.updateBehaviorsSharedData()}
|
||||
ref={objectsList => (this._objectsList = objectsList)}
|
||||
/>
|
||||
</MosaicWindow>
|
||||
@@ -581,6 +590,7 @@ export default class InstancesFullEditor extends Component {
|
||||
onCancel={() => this.editObject(null)}
|
||||
onApply={() => {
|
||||
this.editObject(null);
|
||||
this.updateBehaviorsSharedData();
|
||||
this.forceUpdateObjectsList();
|
||||
}}
|
||||
/>
|
||||
@@ -672,6 +682,7 @@ export default class InstancesFullEditor extends Component {
|
||||
/>
|
||||
<ScenePropertiesDialog
|
||||
open={!!this.state.scenePropertiesDialogOpen}
|
||||
project={project}
|
||||
layout={layout}
|
||||
onClose={() => this.openSceneProperties(false)}
|
||||
onApply={() => this.openSceneProperties(false)}
|
||||
|
61
newIDE/app/src/UI/CloseConfirmDialog.js
Normal file
61
newIDE/app/src/UI/CloseConfirmDialog.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import Window from '../Utils/Window';
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
type Props = {|
|
||||
shouldPrompt: boolean,
|
||||
|};
|
||||
|
||||
export default class CloseConfirmDialog extends React.Component<Props, *> {
|
||||
_delayElectronClose = true;
|
||||
|
||||
componentDidMount() {
|
||||
this._setup(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
if (newProps.shouldPrompt !== this.props.shouldPrompt)
|
||||
this._setup(newProps);
|
||||
}
|
||||
|
||||
_setup(props: Props) {
|
||||
if (Window.isDev()) return; // Don't prevent live-reload in development
|
||||
|
||||
const { shouldPrompt } = props;
|
||||
const message =
|
||||
'Are you sure you want to quit GDevelop? Any unsaved changes will be lost.';
|
||||
|
||||
if (electron) {
|
||||
window.onbeforeunload = e => {
|
||||
if (this._delayElectronClose && shouldPrompt) {
|
||||
//eslint-disable-next-line
|
||||
const answer = confirm(message);
|
||||
setTimeout(() => {
|
||||
if (answer) {
|
||||
// If answer is positive, re-trigger the close
|
||||
this._delayElectronClose = false;
|
||||
electron.remote.getCurrentWindow().close();
|
||||
}
|
||||
});
|
||||
|
||||
// First, prevents closing the window immediately
|
||||
e.returnValue = true;
|
||||
} else {
|
||||
// Returning undefined will let the window close
|
||||
}
|
||||
};
|
||||
} else if (window) {
|
||||
if (shouldPrompt) {
|
||||
window.onbeforeunload = () => message;
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
116
newIDE/app/src/UI/SortableVirtualizedItemList/ItemRow.js
Normal file
116
newIDE/app/src/UI/SortableVirtualizedItemList/ItemRow.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { ListItem } from 'material-ui/List';
|
||||
import IconMenu from '../Menu/IconMenu';
|
||||
import ListIcon from '../ListIcon';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import { type Item } from '.';
|
||||
|
||||
const styles = {
|
||||
itemName: {
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
textField: {
|
||||
top: -16,
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {
|
||||
index: number,
|
||||
item: Item,
|
||||
onRename: (string) => void,
|
||||
editingName: boolean,
|
||||
getThumbnail?: () => string,
|
||||
selected: true,
|
||||
onItemSelected: () => void,
|
||||
buildMenuTemplate: Item => any,
|
||||
};
|
||||
|
||||
class ThemableItemRow extends React.Component<Props, *> {
|
||||
_renderItemMenu(item) {
|
||||
return (
|
||||
<IconMenu
|
||||
ref={iconMenu => (this._iconMenu = iconMenu)}
|
||||
iconButtonElement={
|
||||
<IconButton>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
}
|
||||
buildMenuTemplate={() => this.props.buildMenuTemplate(item)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!prevProps.editingName && this.props.editingName) {
|
||||
setTimeout(() => {
|
||||
if (this.textField) this.textField.focus();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
_onContextMenu = event => {
|
||||
if (this._iconMenu) this._iconMenu.open(event);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { item, selected, style, getThumbnail, muiTheme } = this.props;
|
||||
|
||||
const itemName = item.getName();
|
||||
const label = this.props.editingName ? (
|
||||
<TextField
|
||||
id="rename-item-field"
|
||||
ref={textField => (this.textField = textField)}
|
||||
defaultValue={itemName}
|
||||
onBlur={e => this.props.onRename(e.target.value)}
|
||||
onKeyPress={event => {
|
||||
if (event.charCode === 13) {
|
||||
// enter key pressed
|
||||
this.textField.blur();
|
||||
}
|
||||
}}
|
||||
fullWidth
|
||||
style={styles.textField}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
...styles.itemName,
|
||||
color: selected ? muiTheme.listItem.selectedTextColor : undefined,
|
||||
}}
|
||||
>
|
||||
{itemName}
|
||||
</div>
|
||||
);
|
||||
|
||||
const itemStyle = {
|
||||
borderBottom: `1px solid ${muiTheme.listItem.separatorColor}`,
|
||||
backgroundColor: selected
|
||||
? muiTheme.listItem.selectedBackgroundColor
|
||||
: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
style={{ ...itemStyle, ...style }}
|
||||
onContextMenu={this._onContextMenu}
|
||||
primaryText={label}
|
||||
leftIcon={getThumbnail && <ListIcon src={getThumbnail()} />}
|
||||
rightIconButton={this._renderItemMenu(item)}
|
||||
onClick={() => {
|
||||
if (!this.props.onItemSelected) return;
|
||||
if (this.props.editingName) return;
|
||||
|
||||
this.props.onItemSelected(selected ? null : item);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ItemRow = muiThemeable()(ThemableItemRow);
|
||||
export default ItemRow;
|
108
newIDE/app/src/UI/SortableVirtualizedItemList/index.js
Normal file
108
newIDE/app/src/UI/SortableVirtualizedItemList/index.js
Normal file
@@ -0,0 +1,108 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { List } from 'react-virtualized';
|
||||
import { ListItem } from 'material-ui/List';
|
||||
import ItemRow from './ItemRow';
|
||||
import { makeAddItem } from '../ListAddItem';
|
||||
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
||||
|
||||
const listItemHeight = 48; // TODO: Move this into theme?
|
||||
|
||||
const AddItemRow = makeAddItem(ListItem);
|
||||
|
||||
const SortableItemRow = SortableElement(props => {
|
||||
const { style, ...otherProps } = props;
|
||||
return (
|
||||
<div style={style}>
|
||||
<ItemRow {...otherProps} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const SortableAddItemRow = SortableElement(props => {
|
||||
return <AddItemRow {...props} />;
|
||||
});
|
||||
|
||||
export type Item = {
|
||||
key: string | number,
|
||||
getName: () => string,
|
||||
};
|
||||
|
||||
type ItemsListProps = {
|
||||
height: number,
|
||||
width: number,
|
||||
fullList: Array<Item>,
|
||||
selectedItem: ?Item,
|
||||
onAddNewItem?: () => void,
|
||||
onRename: (Item, string) => void,
|
||||
getThumbnail?: Item => string,
|
||||
onItemSelected: ?Item => void,
|
||||
renamedItem: ?Item,
|
||||
addNewItemLabel: React.Node | string,
|
||||
buildMenuTemplate: Item => any,
|
||||
};
|
||||
|
||||
class ItemsList extends React.Component<ItemsListProps, *> {
|
||||
list: any;
|
||||
|
||||
forceUpdateGrid() {
|
||||
if (this.list) this.list.forceUpdateGrid();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
height,
|
||||
width,
|
||||
fullList,
|
||||
selectedItem,
|
||||
addNewItemLabel,
|
||||
renamedItem,
|
||||
getThumbnail,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<List
|
||||
ref={list => (this.list = list)}
|
||||
height={height}
|
||||
rowCount={fullList.length}
|
||||
rowHeight={listItemHeight}
|
||||
rowRenderer={({ index, key, style }) => {
|
||||
const item = fullList[index];
|
||||
if (item.key === 'add-item-row') {
|
||||
return (
|
||||
<SortableAddItemRow
|
||||
index={fullList.length}
|
||||
key={key}
|
||||
style={style}
|
||||
disabled
|
||||
onClick={this.props.onAddNewItem}
|
||||
primaryText={addNewItemLabel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const nameBeingEdited = renamedItem === item;
|
||||
|
||||
return (
|
||||
<SortableItemRow
|
||||
index={index}
|
||||
key={key}
|
||||
item={item}
|
||||
style={style}
|
||||
onRename={newName => this.props.onRename(item, newName)}
|
||||
editingName={nameBeingEdited}
|
||||
getThumbnail={getThumbnail ? () => getThumbnail(item) : undefined}
|
||||
selected={item === selectedItem}
|
||||
onItemSelected={this.props.onItemSelected}
|
||||
buildMenuTemplate={this.props.buildMenuTemplate}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SortableItemsList = SortableContainer(ItemsList, { withRef: true });
|
||||
export default SortableItemsList;
|
@@ -3,6 +3,10 @@ import i18n from 'i18next';
|
||||
i18n.init({
|
||||
fallbackLng: 'en',
|
||||
|
||||
// allow keys to be phrases having `:`, `.`
|
||||
nsSeparator: false,
|
||||
keySeparator: false,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // Not needed for react
|
||||
},
|
||||
|
@@ -1,89 +1,109 @@
|
||||
// @flow
|
||||
import Keen from 'keen-tracking';
|
||||
import Window from '../Window';
|
||||
import { getUserUUID } from './UserUUID';
|
||||
import Authentification from '../GDevelopServices/Authentification';
|
||||
import {
|
||||
getProgramOpeningCount,
|
||||
incrementProgramOpeningCount,
|
||||
} from './LocalStats';
|
||||
|
||||
const sessionCookie = Keen.utils.cookie('visitor-stats');
|
||||
const sessionTimer = Keen.utils.timer();
|
||||
sessionTimer.start();
|
||||
const isDev = Window.isDev();
|
||||
let client = null;
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
export const installAnalyticsEvents = (authentification: Authentification) => {
|
||||
const sessionCookie = Keen.utils.cookie('visitor-stats');
|
||||
const sessionTimer = Keen.utils.timer();
|
||||
sessionTimer.start();
|
||||
|
||||
var client = new Keen({
|
||||
projectId: '593d9f0595cfc907a1f8126a',
|
||||
writeKey:
|
||||
'B917F1DB50EE4C8949DBB374D2962845A22838B425AA43322A37138691A5270EB0358AEE45A4F61AFA7713B9765B4980517A1E276D4973A2E546EA851BF7757523706367ED430C041D2728A63BF61B5D1B2079C75E455DDDFAAC4324128AC2DB',
|
||||
});
|
||||
client = new Keen({
|
||||
projectId: '593d9f0595cfc907a1f8126a',
|
||||
writeKey:
|
||||
'B917F1DB50EE4C8949DBB374D2962845A22838B425AA43322A37138691A5270EB0358AEE45A4F61AFA7713B9765B4980517A1E276D4973A2E546EA851BF7757523706367ED430C041D2728A63BF61B5D1B2079C75E455DDDFAAC4324128AC2DB',
|
||||
});
|
||||
|
||||
client.extendEvents(function() {
|
||||
return {
|
||||
user: {
|
||||
uuid: getUserUUID(),
|
||||
},
|
||||
page: {
|
||||
title: document.title,
|
||||
url: document.location.href,
|
||||
// info: {} (add-on)
|
||||
},
|
||||
referrer: {
|
||||
url: document.referrer,
|
||||
// info: {} (add-on)
|
||||
},
|
||||
tech: {
|
||||
browser: Keen.helpers.getBrowserProfile(),
|
||||
// info: {} (add-on)
|
||||
ip: '${keen.ip}', // eslint-disable-line
|
||||
ua: '${keen.user_agent}', // eslint-disable-line
|
||||
},
|
||||
time: Keen.helpers.getDatetimeIndex(),
|
||||
visitor: {
|
||||
id: sessionCookie.get('user_id'),
|
||||
time_on_page: sessionTimer.value(),
|
||||
},
|
||||
// geo: {} (add-on)
|
||||
keen: {
|
||||
timestamp: new Date().toISOString(),
|
||||
addons: [
|
||||
{
|
||||
name: 'keen:ip_to_geo',
|
||||
input: {
|
||||
ip: 'tech.ip',
|
||||
client.extendEvents(function() {
|
||||
const userProfile = authentification.getUserProfileSync();
|
||||
|
||||
return {
|
||||
user: {
|
||||
uuid: getUserUUID(),
|
||||
uid: userProfile ? userProfile.uid : undefined,
|
||||
providerId: userProfile ? userProfile.providerId : undefined,
|
||||
email: userProfile ? userProfile.email : undefined,
|
||||
emailVerified: userProfile ? userProfile.emailVerified : undefined,
|
||||
},
|
||||
localStats: {
|
||||
programOpeningCount: getProgramOpeningCount(),
|
||||
},
|
||||
page: {
|
||||
title: document.title,
|
||||
url: document.location.href,
|
||||
// info: {} (add-on)
|
||||
},
|
||||
referrer: {
|
||||
url: document.referrer,
|
||||
// info: {} (add-on)
|
||||
},
|
||||
tech: {
|
||||
browser: Keen.helpers.getBrowserProfile(),
|
||||
// info: {} (add-on)
|
||||
ip: '${keen.ip}', // eslint-disable-line
|
||||
ua: '${keen.user_agent}', // eslint-disable-line
|
||||
},
|
||||
time: Keen.helpers.getDatetimeIndex(),
|
||||
visitor: {
|
||||
id: sessionCookie.get('user_id'),
|
||||
time_on_page: sessionTimer.value(),
|
||||
},
|
||||
// geo: {} (add-on)
|
||||
keen: {
|
||||
timestamp: new Date().toISOString(),
|
||||
addons: [
|
||||
{
|
||||
name: 'keen:ip_to_geo',
|
||||
input: {
|
||||
ip: 'tech.ip',
|
||||
},
|
||||
output: 'geo',
|
||||
},
|
||||
output: 'geo',
|
||||
},
|
||||
{
|
||||
name: 'keen:ua_parser',
|
||||
input: {
|
||||
ua_string: 'tech.ua',
|
||||
{
|
||||
name: 'keen:ua_parser',
|
||||
input: {
|
||||
ua_string: 'tech.ua',
|
||||
},
|
||||
output: 'tech.info',
|
||||
},
|
||||
output: 'tech.info',
|
||||
},
|
||||
{
|
||||
name: 'keen:url_parser',
|
||||
input: {
|
||||
url: 'page.url',
|
||||
{
|
||||
name: 'keen:url_parser',
|
||||
input: {
|
||||
url: 'page.url',
|
||||
},
|
||||
output: 'page.info',
|
||||
},
|
||||
output: 'page.info',
|
||||
},
|
||||
{
|
||||
name: 'keen:referrer_parser',
|
||||
input: {
|
||||
page_url: 'page.url',
|
||||
referrer_url: 'referrer.url',
|
||||
{
|
||||
name: 'keen:referrer_parser',
|
||||
input: {
|
||||
page_url: 'page.url',
|
||||
referrer_url: 'referrer.url',
|
||||
},
|
||||
output: 'referrer.info',
|
||||
},
|
||||
output: 'referrer.info',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const sendProgramOpening = () => {
|
||||
if (isDev) return;
|
||||
if (isDev || !client) return;
|
||||
|
||||
incrementProgramOpeningCount();
|
||||
client.recordEvent('program_opening');
|
||||
};
|
||||
|
||||
export const sendExportLaunched = exportKind => {
|
||||
if (isDev) return;
|
||||
export const sendExportLaunched = (exportKind: string) => {
|
||||
if (isDev || !client) return;
|
||||
|
||||
client.recordEvent('export_launched', {
|
||||
platform: 'GDevelop JS Platform', // Hardcoded here for now
|
||||
@@ -91,8 +111,8 @@ export const sendExportLaunched = exportKind => {
|
||||
});
|
||||
};
|
||||
|
||||
export const sendNewGameCreated = templateName => {
|
||||
if (isDev) return;
|
||||
export const sendNewGameCreated = (templateName: string) => {
|
||||
if (isDev || !client) return;
|
||||
|
||||
client.recordEvent('new_game_creation', {
|
||||
platform: 'GDevelop JS Platform', // Hardcoded here for now
|
||||
@@ -100,16 +120,20 @@ export const sendNewGameCreated = templateName => {
|
||||
});
|
||||
};
|
||||
|
||||
export const sendTutorialOpened = tutorialName => {
|
||||
if (isDev) return;
|
||||
export const sendTutorialOpened = (tutorialName: string) => {
|
||||
if (isDev || !client) return;
|
||||
|
||||
client.recordEvent('tutorial_opened', {
|
||||
tutorialName,
|
||||
});
|
||||
};
|
||||
|
||||
export const sendErrorMessage = (errorMessage, type, rawError) => {
|
||||
if (isDev) return;
|
||||
export const sendErrorMessage = (
|
||||
errorMessage: string,
|
||||
type: string,
|
||||
rawError: any
|
||||
) => {
|
||||
if (isDev || !client) return;
|
||||
|
||||
client.recordEvent('error_message', {
|
||||
message: errorMessage,
|
||||
@@ -118,8 +142,8 @@ export const sendErrorMessage = (errorMessage, type, rawError) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const sendSignupDone = email => {
|
||||
if (isDev) return;
|
||||
export const sendSignupDone = (email: string) => {
|
||||
if (isDev || !client) return;
|
||||
|
||||
client.recordEvent('signup', {
|
||||
email,
|
||||
|
@@ -1,9 +1,32 @@
|
||||
import { getUserUUID } from './UserUUID';
|
||||
const FS = global.FS;
|
||||
import optionalRequire from '../OptionalRequire';
|
||||
import Window from '../Window';
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
export const installFullstory = () => {
|
||||
if (FS) {
|
||||
FS.identify(getUserUUID(), {
|
||||
// prettier-ignore
|
||||
if (!electron && !Window.isDev()) {
|
||||
window['_fs_debug'] = false;
|
||||
window['_fs_host'] = 'fullstory.com';
|
||||
window['_fs_org'] = '8DWZ1';
|
||||
window['_fs_namespace'] = 'FS';
|
||||
(function(m,n,e,t,l,o,g,y){
|
||||
if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
|
||||
g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[];
|
||||
o=n.createElement(t);o.async=1;o.src='https://'+_fs_host+'/s/fs.js'; //eslint-disable-line
|
||||
y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
|
||||
g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){g(l,v)};
|
||||
g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
|
||||
g.clearUserCookie=function(c,d,i){if(!c || document.cookie.match('fs_uid=[`;`]*`[`;`]*`[`;`]*`')){
|
||||
d=n.domain;while(1){n.cookie='fs_uid=;domain='+d+
|
||||
';path=/;expires='+new Date(0).toUTCString();i=d.indexOf('.');if(i<0)break;d=d.slice(i+1)}}};
|
||||
})(window,document,window['_fs_namespace'],'script','user');
|
||||
} else {
|
||||
console.info("Electron or development build - Fullstory disabled");
|
||||
}
|
||||
|
||||
if (window.FS) {
|
||||
window.FS.identify(getUserUUID(), {
|
||||
// displayName: 'Daniel Falko',
|
||||
// email: 'danielfalko@example.com',
|
||||
});
|
||||
|
23
newIDE/app/src/Utils/Analytics/LocalStats.js
Normal file
23
newIDE/app/src/Utils/Analytics/LocalStats.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// @flow
|
||||
const localStoragePrefix = 'gd-local-stats';
|
||||
|
||||
export const getProgramOpeningCount = (): number => {
|
||||
try {
|
||||
const count = localStorage.getItem(`${localStoragePrefix}-program-opening`);
|
||||
if (count !== null) return parseInt(count, 10);
|
||||
} catch (e) {
|
||||
console.warn('Unable to load stored program opening count', e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const incrementProgramOpeningCount = () => {
|
||||
const count = getProgramOpeningCount() + 1;
|
||||
|
||||
try {
|
||||
localStorage.setItem(`${localStoragePrefix}-program-opening`, '' + count);
|
||||
} catch (e) {
|
||||
console.warn('Unable to store program opening count', e);
|
||||
}
|
||||
};
|
@@ -4,7 +4,7 @@ import { GDevelopFirebaseConfig } from './ApiConfigs';
|
||||
|
||||
export type Profile = {
|
||||
uid: string, // This represents the userId
|
||||
picture: string,
|
||||
providerId: string,
|
||||
email: string,
|
||||
emailVerified: boolean,
|
||||
};
|
||||
@@ -76,6 +76,10 @@ export default class Authentification {
|
||||
cb(null, this.user);
|
||||
};
|
||||
|
||||
getUserProfileSync = (): ?Profile => {
|
||||
return this.user;
|
||||
};
|
||||
|
||||
logout = () => {
|
||||
firebase
|
||||
.auth()
|
||||
|
@@ -12,7 +12,7 @@ export type Build = {
|
||||
bucket?: string,
|
||||
logsKey?: string,
|
||||
apkKey?: string,
|
||||
status: 'pending' | 'complete' | 'errored',
|
||||
status: 'pending' | 'complete' | 'error',
|
||||
type: 'cordova-build',
|
||||
createdAt: number,
|
||||
updatedAt: number,
|
||||
|
@@ -8,7 +8,7 @@ import { type Profile } from '../Utils/GDevelopServices/Authentification';
|
||||
|
||||
export const profileForIndieUser: Profile = {
|
||||
uid: 'indie-user',
|
||||
picture: '',
|
||||
providerId: 'fake-provider.com',
|
||||
email: 'indie-user@example.com',
|
||||
emailVerified: true,
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
import 'element-closest';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
@@ -6,10 +7,10 @@ import Window from './Utils/Window';
|
||||
import ExportDialog from './Export/ExportDialog';
|
||||
import CreateProjectDialog from './ProjectCreation/CreateProjectDialog';
|
||||
import Authentification from './Utils/GDevelopServices/Authentification';
|
||||
import { sendProgramOpening } from './Utils/Analytics/EventSender';
|
||||
import { sendProgramOpening, installAnalyticsEvents } from './Utils/Analytics/EventSender';
|
||||
import { installRaven } from './Utils/Analytics/Raven';
|
||||
import { installFullstory } from './Utils/Analytics/Fullstory';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
import { unregister } from './registerServiceWorker';
|
||||
import './UI/iconmoon-font.css'; // Styles for Iconmoon font.
|
||||
import 'react-virtualized/styles.css'; // Styles for react-virtualized Table
|
||||
|
||||
@@ -18,7 +19,7 @@ import BrowserExamples from './ProjectCreation/BrowserExamples';
|
||||
import BrowserProjectOpener from './ProjectsStorage/BrowserProjectOpener';
|
||||
import BrowserSaveDialog from './ProjectsStorage/BrowserSaveDialog';
|
||||
import BrowserIntroDialog from './MainFrame/BrowserIntroDialog';
|
||||
import browserResourceSources from './ResourcesEditor/BrowserResourceSources';
|
||||
import browserResourceSources from './ResourcesList/BrowserResourceSources';
|
||||
import BrowserS3PreviewLauncher from './Export/BrowserExporters/BrowserS3PreviewLauncher';
|
||||
import { getBrowserExporters } from './Export/BrowserExporters';
|
||||
|
||||
@@ -26,7 +27,7 @@ import { getBrowserExporters } from './Export/BrowserExporters';
|
||||
import ExternalEditor from './ExternalEditor';
|
||||
import optionalRequire from './Utils/OptionalRequire.js';
|
||||
import LocalExamples from './ProjectCreation/LocalExamples';
|
||||
import localResourceSources from './ResourcesEditor/LocalResourceSources';
|
||||
import localResourceSources from './ResourcesList/LocalResourceSources';
|
||||
import LocalProjectWriter from './ProjectsStorage/LocalProjectWriter';
|
||||
import LocalProjectOpener from './ProjectsStorage/LocalProjectOpener';
|
||||
import LocalPreviewLauncher from './Export/LocalExporters/LocalPreviewLauncher';
|
||||
@@ -35,12 +36,14 @@ import ElectronEventsBridge from './MainFrame/ElectronEventsBridge';
|
||||
import LocalIntroDialog from './MainFrame/LocalIntroDialog';
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
|
||||
const authentification = new Authentification();
|
||||
installAnalyticsEvents(authentification);
|
||||
installRaven();
|
||||
installFullstory();
|
||||
|
||||
Window.setUpContextMenu();
|
||||
|
||||
const authentification = new Authentification();
|
||||
let app = null;
|
||||
|
||||
if (electron) {
|
||||
@@ -97,6 +100,10 @@ if (electron) {
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.render(app, document.getElementById('root'));
|
||||
registerServiceWorker();
|
||||
const rootElement = document.getElementById('root');
|
||||
if (rootElement) ReactDOM.render(app, rootElement);
|
||||
else console.error("No root element defined in index.html");
|
||||
|
||||
// registerServiceWorker();
|
||||
unregister();
|
||||
sendProgramOpening();
|
||||
|
@@ -43,7 +43,7 @@ import muiDecorator from './MuiDecorator';
|
||||
import paperDecorator from './PaperDecorator';
|
||||
import ValueStateHolder from './ValueStateHolder';
|
||||
import DragDropContextProvider from '../Utils/DragDropHelpers/DragDropContextProvider';
|
||||
import ResourcesLoader from '../ObjectsRendering/ResourcesLoader';
|
||||
import ResourcesLoader from '../ResourcesLoader';
|
||||
import VariablesList from '../VariablesList';
|
||||
import ExpressionSelector from '../EventsSheet/InstructionEditor/InstructionOrExpressionSelector/ExpressionSelector';
|
||||
import InstructionSelector from '../EventsSheet/InstructionEditor/InstructionOrExpressionSelector/InstructionSelector';
|
||||
@@ -539,8 +539,6 @@ storiesOf('ProfileDetails', module)
|
||||
<ProfileDetails
|
||||
profile={{
|
||||
email: 'test@example.com',
|
||||
picture:
|
||||
'"https://s.gravatar.com/avatar/d6fc8df7ddfe938cc379c53bfb5645fc?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Ffl.png',
|
||||
}}
|
||||
/>
|
||||
))
|
||||
|
@@ -1,4 +1,4 @@
|
||||
require('dotenv').config();
|
||||
require('dotenv').config({ path: __dirname + '/.env' });
|
||||
const electron = require('electron');
|
||||
const app = electron.app; // Module to control application life.
|
||||
const BrowserWindow = electron.BrowserWindow; // Module to create native browser window.
|
||||
|
597
newIDE/electron-app/app/package-lock.json
generated
597
newIDE/electron-app/app/package-lock.json
generated
@@ -1,9 +1,20 @@
|
||||
{
|
||||
"name": "gdevelop",
|
||||
"version": "5.0.0-beta19",
|
||||
"version": "5.0.0-beta24",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"fast-deep-equal": "1.0.0",
|
||||
"fast-json-stable-stringify": "2.0.0",
|
||||
"json-schema-traverse": "0.3.1"
|
||||
}
|
||||
},
|
||||
"archiver": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/archiver/-/archiver-2.1.1.tgz",
|
||||
@@ -40,6 +51,16 @@
|
||||
"sprintf-js": "1.0.3"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
|
||||
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"async": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
|
||||
@@ -48,6 +69,11 @@
|
||||
"lodash": "4.17.4"
|
||||
}
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.181.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.181.0.tgz",
|
||||
@@ -66,6 +92,16 @@
|
||||
"xmlbuilder": "4.2.1"
|
||||
}
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
|
||||
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@@ -76,6 +112,20 @@
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
|
||||
"integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw=="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
||||
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz",
|
||||
"integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg="
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
|
||||
@@ -97,6 +147,19 @@
|
||||
"bluebird": "3.5.1"
|
||||
}
|
||||
},
|
||||
"bmp-js": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz",
|
||||
"integrity": "sha1-ZBE+nHzxICs3btYHvzBibr5XsYo="
|
||||
},
|
||||
"boom": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
|
||||
"integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
|
||||
"requires": {
|
||||
"hoek": "4.2.0"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
@@ -121,6 +184,11 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
|
||||
},
|
||||
"buffer-equal": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
|
||||
"integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs="
|
||||
},
|
||||
"builder-util-runtime": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.0.1.tgz",
|
||||
@@ -139,6 +207,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
@@ -148,6 +221,19 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
|
||||
"integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
},
|
||||
"compress-commons": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz",
|
||||
@@ -207,6 +293,32 @@
|
||||
"sha.js": "2.4.9"
|
||||
}
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
|
||||
"integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
|
||||
"requires": {
|
||||
"boom": "5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"boom": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
|
||||
"integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
|
||||
"requires": {
|
||||
"hoek": "4.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
@@ -215,6 +327,30 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"dom-walk": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz",
|
||||
"integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg="
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz",
|
||||
"integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0="
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"electron-editor-context-menu": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-editor-context-menu/-/electron-editor-context-menu-1.1.1.tgz",
|
||||
@@ -277,6 +413,11 @@
|
||||
"once": "1.4.0"
|
||||
}
|
||||
},
|
||||
"es6-promise": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
||||
"integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
@@ -287,6 +428,59 @@
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
|
||||
},
|
||||
"exif-parser": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
||||
"integrity": "sha1-WKnS1ywCwfbwKg70qRZicrd2CSI="
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
||||
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
|
||||
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"file-type": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
|
||||
"integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek="
|
||||
},
|
||||
"for-each": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz",
|
||||
"integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=",
|
||||
"requires": {
|
||||
"is-function": "1.0.1"
|
||||
}
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
|
||||
"integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"mime-types": "2.1.17"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
|
||||
@@ -331,6 +525,14 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||
@@ -344,11 +546,34 @@
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"global": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz",
|
||||
"integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=",
|
||||
"requires": {
|
||||
"min-document": "2.19.0",
|
||||
"process": "0.5.2"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
|
||||
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
|
||||
"requires": {
|
||||
"ajv": "5.5.2",
|
||||
"har-schema": "2.0.0"
|
||||
}
|
||||
},
|
||||
"hash-base": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
|
||||
@@ -357,6 +582,32 @@
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"hawk": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
|
||||
"integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
|
||||
"requires": {
|
||||
"boom": "4.3.1",
|
||||
"cryptiles": "3.1.2",
|
||||
"hoek": "4.2.0",
|
||||
"sntp": "2.1.0"
|
||||
}
|
||||
},
|
||||
"hoek": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
|
||||
"integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ=="
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"jsprim": "1.4.1",
|
||||
"sshpk": "1.13.1"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
|
||||
@@ -376,16 +627,64 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"ip-regex": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz",
|
||||
"integrity": "sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0="
|
||||
},
|
||||
"is-function": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz",
|
||||
"integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU="
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jimp": {
|
||||
"version": "0.2.28",
|
||||
"resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz",
|
||||
"integrity": "sha1-3VKak3GQ9ClXp5N9Gsw6d2KZbqI=",
|
||||
"requires": {
|
||||
"bignumber.js": "2.4.0",
|
||||
"bmp-js": "0.0.3",
|
||||
"es6-promise": "3.3.1",
|
||||
"exif-parser": "0.1.12",
|
||||
"file-type": "3.9.0",
|
||||
"jpeg-js": "0.2.0",
|
||||
"load-bmfont": "1.3.0",
|
||||
"mime": "1.6.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"pixelmatch": "4.0.2",
|
||||
"pngjs": "3.3.1",
|
||||
"read-chunk": "1.0.1",
|
||||
"request": "2.83.0",
|
||||
"stream-to-buffer": "0.1.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"url-regex": "3.2.0"
|
||||
}
|
||||
},
|
||||
"jmespath": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
|
||||
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
|
||||
},
|
||||
"jpeg-js": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz",
|
||||
"integrity": "sha1-U+RI7J0mPmgyZkZ+lELSxaLvVII="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||
@@ -395,6 +694,27 @@
|
||||
"esprima": "4.0.0"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
||||
"optional": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
|
||||
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
|
||||
@@ -403,6 +723,17 @@
|
||||
"graceful-fs": "4.1.11"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"lazy-val": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
|
||||
@@ -416,6 +747,20 @@
|
||||
"readable-stream": "2.3.3"
|
||||
}
|
||||
},
|
||||
"load-bmfont": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.3.0.tgz",
|
||||
"integrity": "sha1-u358cQ3mvK/LE8s7jIHgwBMey8k=",
|
||||
"requires": {
|
||||
"buffer-equal": "0.0.1",
|
||||
"mime": "1.6.0",
|
||||
"parse-bmfont-ascii": "1.0.6",
|
||||
"parse-bmfont-binary": "1.0.6",
|
||||
"parse-bmfont-xml": "1.1.3",
|
||||
"xhr": "2.4.1",
|
||||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
|
||||
@@ -456,6 +801,32 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.30.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
|
||||
"integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.17",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
|
||||
"integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
|
||||
"requires": {
|
||||
"mime-db": "1.30.0"
|
||||
}
|
||||
},
|
||||
"min-document": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
|
||||
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
|
||||
"requires": {
|
||||
"dom-walk": "0.1.1"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
@@ -469,6 +840,21 @@
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -482,6 +868,11 @@
|
||||
"remove-trailing-separator": "1.1.0"
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
|
||||
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -490,11 +881,62 @@
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"parse-bmfont-ascii": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz",
|
||||
"integrity": "sha1-Eaw8P/WPfCAgqyJ2kHkQjU36AoU="
|
||||
},
|
||||
"parse-bmfont-binary": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz",
|
||||
"integrity": "sha1-0Di0dtPp3Z2x4RoLDlOiJ5K2kAY="
|
||||
},
|
||||
"parse-bmfont-xml": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.3.tgz",
|
||||
"integrity": "sha1-1rZqNxr9OcUAfZ8O6yYqTyzOe3w=",
|
||||
"requires": {
|
||||
"xml-parse-from-string": "1.0.1",
|
||||
"xml2js": "0.4.17"
|
||||
}
|
||||
},
|
||||
"parse-headers": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.1.tgz",
|
||||
"integrity": "sha1-aug6eqJanZtwCswoaYzR8e1+lTY=",
|
||||
"requires": {
|
||||
"for-each": "0.3.2",
|
||||
"trim": "0.0.1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"pixelmatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
|
||||
"integrity": "sha1-j0fc7FARtHe2fbA8JDvB8wheiFQ=",
|
||||
"requires": {
|
||||
"pngjs": "3.3.1"
|
||||
}
|
||||
},
|
||||
"pngjs": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.1.tgz",
|
||||
"integrity": "sha512-ggXCTsqHRIsGMkHlCEhbHhUmNTA2r1lpkE0NL4Q9S8spkXbm4vE9TVmPso2AGYn90Gltdz8W5CyzhcIGg2Gejg=="
|
||||
},
|
||||
"process": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz",
|
||||
"integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
||||
@@ -505,11 +947,21 @@
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
|
||||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"read-chunk": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz",
|
||||
"integrity": "sha1-X2jKswfmY/GZk1J9m1icrORmEZQ="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||
@@ -547,6 +999,35 @@
|
||||
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
|
||||
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.83.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
|
||||
"integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
|
||||
"requires": {
|
||||
"aws-sign2": "0.7.0",
|
||||
"aws4": "1.6.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.3.1",
|
||||
"har-validator": "5.0.3",
|
||||
"hawk": "6.0.2",
|
||||
"http-signature": "1.2.0",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.17",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "2.1.0",
|
||||
"qs": "6.5.1",
|
||||
"safe-buffer": "5.1.1",
|
||||
"stringstream": "0.0.5",
|
||||
"tough-cookie": "2.3.3",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.1.0"
|
||||
}
|
||||
},
|
||||
"ripemd160": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
|
||||
@@ -580,6 +1061,14 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"sntp": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
|
||||
"integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
|
||||
"requires": {
|
||||
"hoek": "4.2.0"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -598,6 +1087,34 @@
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
|
||||
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jsbn": "0.1.1",
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"stream-to": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz",
|
||||
"integrity": "sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0="
|
||||
},
|
||||
"stream-to-buffer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz",
|
||||
"integrity": "sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk=",
|
||||
"requires": {
|
||||
"stream-to": "0.2.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
@@ -606,6 +1123,11 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
|
||||
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.5.tgz",
|
||||
@@ -617,6 +1139,45 @@
|
||||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
|
||||
"integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"trim": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
|
||||
"integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"optional": true
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
|
||||
@@ -631,6 +1192,14 @@
|
||||
"querystring": "0.2.0"
|
||||
}
|
||||
},
|
||||
"url-regex": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz",
|
||||
"integrity": "sha1-260eDJ4p4QXdCx8J9oYvf9tIJyQ=",
|
||||
"requires": {
|
||||
"ip-regex": "1.0.3"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -641,11 +1210,37 @@
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
|
||||
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "1.3.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"xhr": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/xhr/-/xhr-2.4.1.tgz",
|
||||
"integrity": "sha512-pAIU5vBr9Hiy5cpFIbPnwf0C18ZF86DBsZKrlsf87N5De/JbA6RJ83UP/cv+aljl4S40iRVMqP4pr4sF9Dnj0A==",
|
||||
"requires": {
|
||||
"global": "4.3.2",
|
||||
"is-function": "1.0.1",
|
||||
"parse-headers": "2.0.1",
|
||||
"xtend": "4.0.1"
|
||||
}
|
||||
},
|
||||
"xml-parse-from-string": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz",
|
||||
"integrity": "sha1-qQKekp09vN7RafPG4oI42VpdWig="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz",
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "gdevelop",
|
||||
"productName": "GDevelop 5",
|
||||
"description": "GDevelop 5 IDE running on the Electron runtime",
|
||||
"version": "5.0.0-beta21",
|
||||
"version": "5.0.0-beta25",
|
||||
"author": "Florian Rival",
|
||||
"license": "MIT",
|
||||
"homepage": "http://compilgames.net",
|
||||
@@ -11,11 +11,13 @@
|
||||
"archiver": "^2.1.1",
|
||||
"async": "^2.6.0",
|
||||
"aws-sdk": "^2.58.0",
|
||||
"dotenv": "^4.0.0",
|
||||
"electron-editor-context-menu": "^1.1.1",
|
||||
"electron-is": "^2.4.0",
|
||||
"electron-log": "^2.2.14",
|
||||
"electron-updater": "^2.18.2",
|
||||
"fs-extra": "^3.0.1",
|
||||
"jimp": "^0.2.28",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"minimist": "^1.2.0",
|
||||
"recursive-readdir": "^2.2.1"
|
||||
|
3
newIDE/electron-app/package-lock.json
generated
3
newIDE/electron-app/package-lock.json
generated
@@ -664,7 +664,8 @@
|
||||
"dotenv": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-4.0.0.tgz",
|
||||
"integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0="
|
||||
"integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=",
|
||||
"dev": true
|
||||
},
|
||||
"dotenv-expand": {
|
||||
"version": "4.0.1",
|
||||
|
@@ -41,7 +41,6 @@
|
||||
"shelljs": "^0.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^4.0.0",
|
||||
"electron-is": "^2.4.0"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user