mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
32 Commits
v5.0.0-bet
...
4.0.97
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fcfb71132c | ||
![]() |
2017d34197 | ||
![]() |
662c7bd397 | ||
![]() |
cf5011c149 | ||
![]() |
954520ae3b | ||
![]() |
6ffcd6dfb0 | ||
![]() |
80e7a6010a | ||
![]() |
0dc023ba89 | ||
![]() |
e70021d0dd | ||
![]() |
eb63bda7d2 | ||
![]() |
10833aa45d | ||
![]() |
eed844357e | ||
![]() |
6d91676dab | ||
![]() |
69410d62ea | ||
![]() |
977425e700 | ||
![]() |
49d409260a | ||
![]() |
a50b62a2d8 | ||
![]() |
6b21ebcc9b | ||
![]() |
3c8aa4a249 | ||
![]() |
a8e9fa5895 | ||
![]() |
c9f8b4a8ed | ||
![]() |
6b38479166 | ||
![]() |
4ccbc1b958 | ||
![]() |
54d7d284c8 | ||
![]() |
58ed74e020 | ||
![]() |
e8ce83b162 | ||
![]() |
9b91f06011 | ||
![]() |
17247cbbf1 | ||
![]() |
10b81dd232 | ||
![]() |
3f3a5dbd3b | ||
![]() |
6ff8ee749d | ||
![]() |
db5f146818 |
25
.vscode/c_cpp_properties.json
vendored
25
.vscode/c_cpp_properties.json
vendored
@@ -24,9 +24,9 @@
|
||||
"__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 *\/",
|
||||
"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",
|
||||
@@ -45,7 +45,10 @@
|
||||
"macFrameworkPath": [
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Frameworks"
|
||||
]
|
||||
],
|
||||
"compilerPath": "/usr/bin/clang",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
{
|
||||
"name": "Linux",
|
||||
@@ -66,9 +69,9 @@
|
||||
"__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 *\/",
|
||||
"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",
|
||||
@@ -102,9 +105,9 @@
|
||||
"__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 *\/",
|
||||
"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",
|
||||
@@ -119,4 +122,4 @@
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
}
|
||||
}
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -89,5 +89,6 @@
|
||||
"newIDE/electron-app/app/www": true
|
||||
},
|
||||
// Support for Flowtype:
|
||||
"javascript.validate.enable": false
|
||||
"javascript.validate.enable": false,
|
||||
"flow.useNPMPackagedFlow": true
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ IF NOT EXIST "%INNOSETUP_EXE%" set INNOSETUP_EXE=C:\Program Files\Inno Setup 5\I
|
||||
IF EXIST "%INNOSETUP_EXE%" (
|
||||
echo "Note: InnoSetup found at %INNOSETUP_EXE%"
|
||||
) ELSE (
|
||||
echo Warning: InnoSetup not found! Skipping installer creation.
|
||||
echo Warning: InnoSetup Unicode not found! Skipping installer creation.
|
||||
SET SKIPINSTALLER=1
|
||||
)
|
||||
|
||||
|
@@ -13,17 +13,17 @@ AllowNoIcons=yes
|
||||
LicenseFile=..\Output\Release_Windows\License-en.rtf
|
||||
InfoBeforeFile=..\Output\Release_Windows\Informations-en.rtf
|
||||
OutputDir=.\
|
||||
OutputBaseFilename=gd4096
|
||||
OutputBaseFilename=gd4097
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
SetupIconFile=..\Output\Release_Windows\res\icon.ico
|
||||
VersionInfoVersion=4.0
|
||||
WizardImageFile=Setup bitmap\wizbmp.bmp
|
||||
WizardSmallImageFile=Setup bitmap/smallicon.bmp
|
||||
AppCopyright=2008-2017 Florian Rival
|
||||
AppCopyright=2008-2018 Florian Rival
|
||||
VersionInfoCompany=Florian Rival
|
||||
VersionInfoDescription=GDevelop setup
|
||||
VersionInfoCopyright=2008-2016 Florian Rival
|
||||
VersionInfoCopyright=2008-2018 Florian Rival
|
||||
VersionInfoProductName=GDevelop
|
||||
VersionInfoProductVersion=4.0
|
||||
|
||||
|
@@ -78,6 +78,7 @@
|
||||
* The installation is fairly simple :<br>
|
||||
* <br>
|
||||
* - Launch the installer.<br>
|
||||
* - Uncheck "Check for updated files on the TDM-GCC server" (otherwise you won't get TDM-GCC 4.9.2 but a more recent version that won't be compatible with wxWidgets pre-compiled binaries)
|
||||
* - Choose Create.<br>
|
||||
|
||||
\image html compilerInstall1.png
|
||||
|
@@ -26,14 +26,24 @@ public:
|
||||
mutable std::vector<sf::Vector2f> edges; ///< Edges. Can be computed from vertices using ComputeEdges()
|
||||
|
||||
/**
|
||||
* Moves each vertices from the given amount.
|
||||
* \brief Get the vertices composing the polygon.
|
||||
*/
|
||||
std::vector<sf::Vector2f> & GetVertices() { return vertices; }
|
||||
|
||||
/**
|
||||
* \brief Get the vertices composing the polygon.
|
||||
*/
|
||||
const std::vector<sf::Vector2f> & GetVertices() const { return vertices; }
|
||||
|
||||
/**
|
||||
* \brief Moves each vertices from the given amount.
|
||||
*
|
||||
* \note Edges are updated, there is no need to call ComputeEdges after calling Move.
|
||||
*/
|
||||
void Move(float x, float y);
|
||||
|
||||
/**
|
||||
* Rotate the polygon.
|
||||
* \brief Rotate the polygon.
|
||||
* \param angle Angle in radians
|
||||
*
|
||||
* \warning Rotation is made clockwise
|
||||
@@ -42,18 +52,18 @@ public:
|
||||
void Rotate(float angle);
|
||||
|
||||
/**
|
||||
* Automatically fill edges vector using vertices.
|
||||
* \brief Automatically fill edges vector using vertices.
|
||||
*/
|
||||
void ComputeEdges() const;
|
||||
|
||||
/**
|
||||
* Check if the polygon is convex.
|
||||
* \brief Check if the polygon is convex.
|
||||
* \return true if the polygon is convex
|
||||
*/
|
||||
bool IsConvex() const;
|
||||
|
||||
/**
|
||||
* Return the position of the center of the polygon
|
||||
* \brief Return the position of the center of the polygon
|
||||
*/
|
||||
sf::Vector2f ComputeCenter() const;
|
||||
|
||||
@@ -62,7 +72,7 @@ public:
|
||||
*/
|
||||
///@{
|
||||
/**
|
||||
* Create a rectangle
|
||||
* \brief Create a rectangle
|
||||
*/
|
||||
static Polygon2d CreateRectangle(float width, float height);
|
||||
///@}
|
||||
|
@@ -103,6 +103,7 @@ bool Sprite::SetDefaultCenterPoint(bool enabled)
|
||||
|
||||
std::vector<Polygon2d> Sprite::GetCollisionMask() const
|
||||
{
|
||||
//TODO(perf): Cache to avoid re-creating a mask at every call
|
||||
#if !defined(EMSCRIPTEN)
|
||||
if ( automaticCollisionMask )
|
||||
{
|
||||
|
@@ -56,6 +56,16 @@ public:
|
||||
*/
|
||||
std::vector<Polygon2d> GetCollisionMask() const;
|
||||
|
||||
/**
|
||||
* \brief Get the custom collision mask.
|
||||
*/
|
||||
std::vector<Polygon2d> & GetCustomCollisionMask() { return customCollisionMask; };
|
||||
|
||||
/**
|
||||
* \brief Get the custom collision mask.
|
||||
*/
|
||||
const std::vector<Polygon2d> & GetCustomCollisionMask() const { return customCollisionMask; };
|
||||
|
||||
/**
|
||||
* \brief Set the custom collision mask.
|
||||
* Call then `SetCollisionMaskAutomatic(false)` to use it.
|
||||
|
@@ -225,13 +225,13 @@ void ChooseVariableDialog::RefreshVariable(wxTreeListItem item, const gd::String
|
||||
variablesList->SetItemText(item, 1, "(Structure)");
|
||||
|
||||
//Add/update children
|
||||
const std::map<gd::String, gd::Variable> & children = variable.GetAllChildren();
|
||||
const auto & children = variable.GetAllChildren();
|
||||
wxTreeListItem currentChildItem = variablesList->GetFirstChild(item);
|
||||
wxTreeListItem lastChildItem;
|
||||
for(std::map<gd::String, gd::Variable>::const_iterator it = children.begin();it != children.end();++it)
|
||||
for(auto it = children.begin();it != children.end();++it)
|
||||
{
|
||||
if ( !currentChildItem.IsOk() ) currentChildItem = variablesList->AppendItem(item, it->first);
|
||||
RefreshVariable(currentChildItem, it->first, it->second);
|
||||
RefreshVariable(currentChildItem, it->first, *it->second);
|
||||
lastChildItem = currentChildItem;
|
||||
|
||||
currentChildItem = variablesList->GetNextSibling(currentChildItem);
|
||||
@@ -255,10 +255,11 @@ void ChooseVariableDialog::RefreshAll()
|
||||
|
||||
for (std::size_t i = 0;i<temporaryContainer->Count();++i)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & variable = temporaryContainer->Get(i);
|
||||
const gd::String & name = temporaryContainer->GetNameAt(i);
|
||||
const auto & variable = temporaryContainer->Get(i);
|
||||
|
||||
wxTreeListItem item = variablesList->AppendItem(variablesList->GetRootItem(), variable.first);
|
||||
RefreshVariable(item, variable.first, variable.second);
|
||||
wxTreeListItem item = variablesList->AppendItem(variablesList->GetRootItem(), name);
|
||||
RefreshVariable(item, name, variable);
|
||||
variablesList->Expand(item);
|
||||
}
|
||||
|
||||
@@ -330,20 +331,11 @@ void ChooseVariableDialog::OnAddVarSelected(wxCommandEvent& event)
|
||||
void ChooseVariableDialog::OnMoveUpVarSelected(wxCommandEvent& event)
|
||||
{
|
||||
UpdateSelectedAndParentVariable();
|
||||
for (std::size_t i = 1;i<temporaryContainer->Count();++i)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & currentVar = temporaryContainer->Get(i);
|
||||
if ( currentVar.first == selectedVariableName)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & prevVar = temporaryContainer->Get(i-1);
|
||||
temporaryContainer->Swap(i, i-1);
|
||||
RefreshAll();
|
||||
|
||||
modificationCount++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto position = temporaryContainer->GetPosition(selectedVariableName);
|
||||
temporaryContainer->Move(position, position-1);
|
||||
RefreshAll();
|
||||
|
||||
modificationCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,21 +344,11 @@ void ChooseVariableDialog::OnMoveUpVarSelected(wxCommandEvent& event)
|
||||
void ChooseVariableDialog::OnMoveDownVarSelected(wxCommandEvent& event)
|
||||
{
|
||||
UpdateSelectedAndParentVariable();
|
||||
for (std::size_t i = 0;i<temporaryContainer->Count()-1;++i)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & currentVar = temporaryContainer->Get(i);
|
||||
if ( currentVar.first == selectedVariableName)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & nextVar = temporaryContainer->Get(i+1);
|
||||
|
||||
temporaryContainer->Swap(i, i+1);
|
||||
RefreshAll();
|
||||
|
||||
modificationCount++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto position = temporaryContainer->GetPosition(selectedVariableName);
|
||||
temporaryContainer->Move(position, position+1);
|
||||
RefreshAll();
|
||||
|
||||
modificationCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -5,24 +5,23 @@
|
||||
*/
|
||||
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/String.h"
|
||||
#include <sstream>
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace gd
|
||||
{
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* Get value as a double
|
||||
*/
|
||||
double Variable::GetValue() const
|
||||
{
|
||||
if (!isNumber)
|
||||
{
|
||||
stringstream ss; ss << str;
|
||||
if (!isNumber) {
|
||||
stringstream ss;
|
||||
ss << str;
|
||||
ss >> value;
|
||||
isNumber = true;
|
||||
}
|
||||
@@ -30,11 +29,11 @@ double Variable::GetValue() const
|
||||
return value;
|
||||
}
|
||||
|
||||
const gd::String & Variable::GetString() const
|
||||
const gd::String& Variable::GetString() const
|
||||
{
|
||||
if (isNumber)
|
||||
{
|
||||
stringstream s; s << (value);
|
||||
if (isNumber) {
|
||||
stringstream s;
|
||||
s << (value);
|
||||
str = s.str();
|
||||
isNumber = false;
|
||||
}
|
||||
@@ -42,7 +41,7 @@ const gd::String & Variable::GetString() const
|
||||
return str;
|
||||
}
|
||||
|
||||
bool Variable::HasChild(const gd::String & name) const
|
||||
bool Variable::HasChild(const gd::String& name) const
|
||||
{
|
||||
return isStructure && children.find(name) != children.end();
|
||||
}
|
||||
@@ -53,15 +52,15 @@ bool Variable::HasChild(const gd::String & name) const
|
||||
* If the variable is not a structure or has not
|
||||
* the specified child, an empty variable is returned.
|
||||
*/
|
||||
Variable & Variable::GetChild(const gd::String & name)
|
||||
Variable& Variable::GetChild(const gd::String& name)
|
||||
{
|
||||
std::map<gd::String, Variable>::iterator it = children.find(name);
|
||||
if ( it != children.end() ) return it->second;
|
||||
auto it = children.find(name);
|
||||
if (it != children.end())
|
||||
return *it->second;
|
||||
|
||||
isStructure = true;
|
||||
Variable newEmptyVariable;
|
||||
children[name] = newEmptyVariable;
|
||||
return children[name];
|
||||
children[name] = std::make_shared<gd::Variable>();
|
||||
return *children[name];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,26 +69,28 @@ Variable & Variable::GetChild(const gd::String & name)
|
||||
* If the variable is not a structure or has not
|
||||
* the specified child, an empty variable is returned.
|
||||
*/
|
||||
const Variable & Variable::GetChild(const gd::String & name) const
|
||||
const Variable& Variable::GetChild(const gd::String& name) const
|
||||
{
|
||||
std::map<gd::String, Variable>::iterator it = children.find(name);
|
||||
if ( it != children.end() ) return it->second;
|
||||
auto it = children.find(name);
|
||||
if (it != children.end())
|
||||
return *it->second;
|
||||
|
||||
isStructure = true;
|
||||
Variable newEmptyVariable;
|
||||
children[name] = newEmptyVariable;
|
||||
return children[name];
|
||||
children[name] = std::make_shared<gd::Variable>();
|
||||
return *children[name];
|
||||
}
|
||||
|
||||
void Variable::RemoveChild(const gd::String & name)
|
||||
void Variable::RemoveChild(const gd::String& name)
|
||||
{
|
||||
if ( !isStructure ) return;
|
||||
if (!isStructure)
|
||||
return;
|
||||
children.erase(name);
|
||||
}
|
||||
|
||||
bool Variable::RenameChild(const gd::String & oldName, const gd::String & newName)
|
||||
bool Variable::RenameChild(const gd::String& oldName, const gd::String& newName)
|
||||
{
|
||||
if ( !isStructure || !HasChild(oldName)|| HasChild(newName) ) return false;
|
||||
if (!isStructure || !HasChild(oldName) || HasChild(newName))
|
||||
return false;
|
||||
|
||||
children[newName] = children[oldName];
|
||||
children.erase(oldName);
|
||||
@@ -99,91 +100,114 @@ bool Variable::RenameChild(const gd::String & oldName, const gd::String & newNam
|
||||
|
||||
void Variable::ClearChildren()
|
||||
{
|
||||
if ( !isStructure ) return;
|
||||
if (!isStructure)
|
||||
return;
|
||||
children.clear();
|
||||
}
|
||||
|
||||
void Variable::SerializeTo(SerializerElement & element) const
|
||||
void Variable::SerializeTo(SerializerElement& element) const
|
||||
{
|
||||
if (!isStructure)
|
||||
element.SetAttribute("value", GetString());
|
||||
else
|
||||
{
|
||||
SerializerElement & childrenElement = element.AddChild("children");
|
||||
else {
|
||||
SerializerElement& childrenElement = element.AddChild("children");
|
||||
childrenElement.ConsiderAsArrayOf("variable");
|
||||
for (std::map<gd::String, gd::Variable>::iterator i = children.begin(); i != children.end(); ++i)
|
||||
{
|
||||
SerializerElement & variableElement = childrenElement.AddChild("variable");
|
||||
for (auto i = children.begin(); i != children.end(); ++i) {
|
||||
SerializerElement& variableElement = childrenElement.AddChild("variable");
|
||||
variableElement.SetAttribute("name", i->first);
|
||||
i->second.SerializeTo(variableElement);
|
||||
i->second->SerializeTo(variableElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Variable::UnserializeFrom(const SerializerElement & element)
|
||||
void Variable::UnserializeFrom(const SerializerElement& element)
|
||||
{
|
||||
isStructure = element.HasChild("children", "Children");
|
||||
|
||||
if (isStructure)
|
||||
{
|
||||
const SerializerElement & childrenElement = element.GetChild("children", 0, "Children");
|
||||
if (isStructure) {
|
||||
const SerializerElement& childrenElement = element.GetChild("children", 0, "Children");
|
||||
childrenElement.ConsiderAsArrayOf("variable", "Variable");
|
||||
for (int i = 0; i < childrenElement.GetChildrenCount(); ++i)
|
||||
{
|
||||
const SerializerElement & childElement = childrenElement.GetChild(i);
|
||||
for (int i = 0; i < childrenElement.GetChildrenCount(); ++i) {
|
||||
const SerializerElement& childElement = childrenElement.GetChild(i);
|
||||
gd::String name = childElement.GetStringAttribute("name", "", "Name");
|
||||
|
||||
gd::Variable childVariable;
|
||||
childVariable.UnserializeFrom(childElement);
|
||||
children[name] = childVariable;
|
||||
children[name] = std::make_shared<gd::Variable>();
|
||||
children[name]->UnserializeFrom(childElement);
|
||||
}
|
||||
}
|
||||
else
|
||||
} else
|
||||
SetString(element.GetStringAttribute("value", "", "Value"));
|
||||
}
|
||||
|
||||
void Variable::SaveToXml(TiXmlElement * element) const
|
||||
void Variable::SaveToXml(TiXmlElement* element) const
|
||||
{
|
||||
if (!element) return;
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
if ( !isStructure )
|
||||
if (!isStructure)
|
||||
element->SetAttribute("Value", GetString().c_str());
|
||||
else
|
||||
{
|
||||
TiXmlElement * childrenElem = new TiXmlElement( "Children" );
|
||||
element->LinkEndChild( childrenElem );
|
||||
for (std::map<gd::String, gd::Variable>::iterator i = children.begin(); i != children.end(); ++i)
|
||||
{
|
||||
TiXmlElement * variable = new TiXmlElement( "Variable" );
|
||||
childrenElem->LinkEndChild( variable );
|
||||
else {
|
||||
TiXmlElement* childrenElem = new TiXmlElement("Children");
|
||||
element->LinkEndChild(childrenElem);
|
||||
for (auto i = children.begin(); i != children.end(); ++i) {
|
||||
TiXmlElement* variable = new TiXmlElement("Variable");
|
||||
childrenElem->LinkEndChild(variable);
|
||||
|
||||
variable->SetAttribute("Name", i->first.c_str());
|
||||
i->second.SaveToXml(variable);
|
||||
i->second->SaveToXml(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Variable::LoadFromXml(const TiXmlElement * element)
|
||||
void Variable::LoadFromXml(const TiXmlElement* element)
|
||||
{
|
||||
if (!element) return;
|
||||
if (!element)
|
||||
return;
|
||||
|
||||
isStructure = element->FirstChildElement("Children") != NULL;
|
||||
|
||||
if ( isStructure )
|
||||
{
|
||||
const TiXmlElement * child = element->FirstChildElement("Children")->FirstChildElement();
|
||||
while ( child )
|
||||
{
|
||||
if (isStructure) {
|
||||
const TiXmlElement* child = element->FirstChildElement("Children")->FirstChildElement();
|
||||
while (child) {
|
||||
gd::String name = child->Attribute("Name") ? child->Attribute("Name") : "";
|
||||
gd::Variable childVariable;
|
||||
childVariable.LoadFromXml(child);
|
||||
children[name] = childVariable;
|
||||
children[name] = std::make_shared<gd::Variable>();
|
||||
children[name]->LoadFromXml(child);
|
||||
|
||||
child = child->NextSiblingElement();
|
||||
}
|
||||
}
|
||||
else if (element->Attribute("Value"))
|
||||
} else if (element->Attribute("Value"))
|
||||
SetString(element->Attribute("Value"));
|
||||
}
|
||||
|
||||
std::vector<gd::String> Variable::GetAllChildrenNames() const
|
||||
{
|
||||
std::vector<gd::String> names;
|
||||
for (auto& it : children) {
|
||||
names.push_back(it.first);
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
bool Variable::Contains(const gd::Variable& variableToSearch, bool recursive) const
|
||||
{
|
||||
for (auto& it : children) {
|
||||
if (it.second.get() == &variableToSearch)
|
||||
return true;
|
||||
if (recursive && it.second->Contains(variableToSearch, true))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Variable::RemoveRecursively(const gd::Variable& variableToRemove)
|
||||
{
|
||||
for (auto it = children.begin(); it != children.end();) {
|
||||
if (it->second.get() == &variableToRemove) {
|
||||
it = children.erase(it);
|
||||
} else {
|
||||
it->second->RemoveRecursively(variableToRemove);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_VARIABLE_H
|
||||
#include "GDCore/String.h"
|
||||
#include <map>
|
||||
#include <memory>
|
||||
namespace gd { class SerializerElement; }
|
||||
class TiXmlElement;
|
||||
|
||||
@@ -148,11 +149,30 @@ public:
|
||||
*/
|
||||
void ClearChildren();
|
||||
|
||||
/**
|
||||
* \brief Get the count of children that the variable has.
|
||||
*/
|
||||
size_t GetChildrenCount() const { return children.size(); };
|
||||
|
||||
/**
|
||||
* \brief Get the names of all children
|
||||
*/
|
||||
std::vector<gd::String> GetAllChildrenNames() const;
|
||||
|
||||
/**
|
||||
* \brief Get the map containing all the children.
|
||||
*/
|
||||
const std::map<gd::String, Variable> & GetAllChildren() const { return children; }
|
||||
const std::map<gd::String, std::shared_ptr<Variable>> & GetAllChildren() const { return children; }
|
||||
|
||||
/**
|
||||
* \brief Search if a variable is part of the children, optionally recursively
|
||||
*/
|
||||
bool Contains(const gd::Variable & variableToSearch, bool recursive) const;
|
||||
|
||||
/**
|
||||
* \brief Remove the specified variable if it can be found in the children
|
||||
*/
|
||||
void RemoveRecursively(const gd::Variable & variableToRemove);
|
||||
///@}
|
||||
|
||||
/** \name Serialization
|
||||
@@ -186,7 +206,7 @@ private:
|
||||
mutable gd::String str;
|
||||
mutable bool isNumber; ///< True if the type of the variable is a number.
|
||||
mutable bool isStructure; ///< False when the variable is a primitive ( i.e: Number or String ), true when it is a structure and has may have children.
|
||||
mutable std::map<gd::String, Variable> children; ///<Children, when the variable is considered as a structure.
|
||||
mutable std::map<gd::String, std::shared_ptr<Variable>> children; ///<Children, when the variable is considered as a structure.
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -3,177 +3,192 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
#include <iostream>
|
||||
#include "GDCore/String.h"
|
||||
#include <algorithm>
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
#include "GDCore/Project/VariablesContainer.h"
|
||||
#include "GDCore/Project/Variable.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/TinyXml/tinyxml.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace gd
|
||||
{
|
||||
namespace gd {
|
||||
|
||||
std::pair<gd::String, Variable> VariablesContainer::badVariable;
|
||||
gd::Variable VariablesContainer::badVariable;
|
||||
gd::String VariablesContainer::badName;
|
||||
|
||||
namespace {
|
||||
|
||||
//Tool functor used below
|
||||
class VariableHasName
|
||||
{
|
||||
public:
|
||||
VariableHasName(gd::String const& name_) : name(name_) { }
|
||||
//Tool functor used below
|
||||
class VariableHasName {
|
||||
public:
|
||||
VariableHasName(gd::String const& name_)
|
||||
: name(name_)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator () (const std::pair<gd::String, gd::Variable> & p)
|
||||
{
|
||||
return (p.first == name);
|
||||
}
|
||||
|
||||
gd::String name;
|
||||
};
|
||||
bool operator()(const std::pair<gd::String, std::shared_ptr<gd::Variable>>& p)
|
||||
{
|
||||
return (p.first == name);
|
||||
}
|
||||
|
||||
gd::String name;
|
||||
};
|
||||
}
|
||||
|
||||
VariablesContainer::VariablesContainer()
|
||||
{
|
||||
}
|
||||
|
||||
bool VariablesContainer::Has(const gd::String & name) const
|
||||
bool VariablesContainer::Has(const gd::String& name) const
|
||||
{
|
||||
std::vector < std::pair<gd::String, gd::Variable> >::const_iterator i =
|
||||
std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
auto i = std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
return (i != variables.end());
|
||||
}
|
||||
|
||||
std::pair<gd::String, gd::Variable> & VariablesContainer::Get(std::size_t index)
|
||||
Variable& VariablesContainer::Get(const gd::String& name)
|
||||
{
|
||||
if ( index < variables.size() )
|
||||
return variables[index];
|
||||
auto i = std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
if (i != variables.end())
|
||||
return *i->second;
|
||||
|
||||
return badVariable;
|
||||
}
|
||||
|
||||
const std::pair<gd::String, gd::Variable> & VariablesContainer::Get(std::size_t index) const
|
||||
const Variable& VariablesContainer::Get(const gd::String& name) const
|
||||
{
|
||||
if ( index < variables.size() )
|
||||
return variables[index];
|
||||
auto i = std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
if (i != variables.end())
|
||||
return *i->second;
|
||||
|
||||
return badVariable;
|
||||
}
|
||||
|
||||
Variable & VariablesContainer::Get(const gd::String & name)
|
||||
Variable & VariablesContainer::Get(std::size_t index)
|
||||
{
|
||||
std::vector < std::pair<gd::String, gd::Variable> >::iterator i =
|
||||
std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
if (i != variables.end())
|
||||
return i->second;
|
||||
if (index < variables.size())
|
||||
return *variables[index].second;
|
||||
|
||||
return badVariable.second;
|
||||
return badVariable;
|
||||
}
|
||||
|
||||
const Variable & VariablesContainer::Get(const gd::String & name) const
|
||||
const Variable & VariablesContainer::Get(std::size_t index) const
|
||||
{
|
||||
std::vector < std::pair<gd::String, gd::Variable> >::const_iterator i =
|
||||
std::find_if(variables.begin(), variables.end(), VariableHasName(name));
|
||||
if (i != variables.end())
|
||||
return i->second;
|
||||
if (index < variables.size())
|
||||
return *variables[index].second;
|
||||
|
||||
return badVariable.second;
|
||||
return badVariable;
|
||||
}
|
||||
|
||||
Variable & VariablesContainer::Insert(const gd::String & name, const gd::Variable & variable, std::size_t position)
|
||||
const gd::String & VariablesContainer::GetNameAt(std::size_t index) const
|
||||
{
|
||||
if (position<variables.size())
|
||||
{
|
||||
variables.insert(variables.begin()+position, std::make_pair(name, variable));
|
||||
return variables[position].second;
|
||||
}
|
||||
else
|
||||
{
|
||||
variables.push_back(std::make_pair(name, variable));
|
||||
return variables.back().second;
|
||||
if (index < variables.size())
|
||||
return variables[index].first;
|
||||
|
||||
return badName;
|
||||
}
|
||||
|
||||
Variable& VariablesContainer::Insert(const gd::String& name, const gd::Variable& variable, std::size_t position)
|
||||
{
|
||||
auto newVariable = std::make_shared<gd::Variable>(variable);
|
||||
if (position < variables.size()) {
|
||||
variables.insert(variables.begin() + position, std::make_pair(name, newVariable));
|
||||
return *variables[position].second;
|
||||
} else {
|
||||
variables.push_back(std::make_pair(name, newVariable));
|
||||
return *variables.back().second;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
void VariablesContainer::Remove(const gd::String & varName)
|
||||
void VariablesContainer::Remove(const gd::String& varName)
|
||||
{
|
||||
variables.erase(std::remove_if(variables.begin(), variables.end(),
|
||||
VariableHasName(varName)), variables.end() );
|
||||
VariableHasName(varName)),
|
||||
variables.end());
|
||||
}
|
||||
|
||||
std::size_t VariablesContainer::GetPosition(const gd::String & name) const
|
||||
void VariablesContainer::RemoveRecursively(const gd::Variable& variableToRemove)
|
||||
{
|
||||
for(std::size_t i = 0;i<variables.size();++i)
|
||||
{
|
||||
if ( variables[i].first == name )
|
||||
variables.erase(std::remove_if(variables.begin(), variables.end(),
|
||||
[&variableToRemove](const std::pair<gd::String, std::shared_ptr<gd::Variable>>& nameAndVariable) {
|
||||
return &variableToRemove == nameAndVariable.second.get();
|
||||
}),
|
||||
variables.end());
|
||||
|
||||
for (auto& it : variables) {
|
||||
it.second->RemoveRecursively(variableToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t VariablesContainer::GetPosition(const gd::String& name) const
|
||||
{
|
||||
for (std::size_t i = 0; i < variables.size(); ++i) {
|
||||
if (variables[i].first == name)
|
||||
return i;
|
||||
}
|
||||
|
||||
return gd::String::npos;
|
||||
}
|
||||
|
||||
Variable & VariablesContainer::InsertNew(const gd::String & name, std::size_t position)
|
||||
Variable& VariablesContainer::InsertNew(const gd::String& name, std::size_t position)
|
||||
{
|
||||
Variable newVariable;
|
||||
return Insert(name, newVariable, position);
|
||||
}
|
||||
|
||||
bool VariablesContainer::Rename(const gd::String & oldName, const gd::String & newName)
|
||||
bool VariablesContainer::Rename(const gd::String& oldName, const gd::String& newName)
|
||||
{
|
||||
if (Has(newName)) return false;
|
||||
if (Has(newName))
|
||||
return false;
|
||||
|
||||
std::vector < std::pair<gd::String, gd::Variable> >::iterator i =
|
||||
std::find_if(variables.begin(), variables.end(), VariableHasName(oldName));
|
||||
if (i != variables.end()) i->first = newName;
|
||||
auto i = std::find_if(variables.begin(), variables.end(), VariableHasName(oldName));
|
||||
if (i != variables.end())
|
||||
i->first = newName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VariablesContainer::Swap(std::size_t firstVariableIndex, std::size_t secondVariableIndex)
|
||||
{
|
||||
if ( firstVariableIndex >= variables.size() || secondVariableIndex >= variables.size() )
|
||||
if (firstVariableIndex >= variables.size() || secondVariableIndex >= variables.size())
|
||||
return;
|
||||
|
||||
std::pair<gd::String, gd::Variable> temp = variables[firstVariableIndex];
|
||||
auto temp = variables[firstVariableIndex];
|
||||
variables[firstVariableIndex] = variables[secondVariableIndex];
|
||||
variables[secondVariableIndex] = temp;
|
||||
}
|
||||
|
||||
void VariablesContainer::Move(std::size_t oldIndex, std::size_t newIndex)
|
||||
{
|
||||
if ( oldIndex >= variables.size() || newIndex >= variables.size() )
|
||||
if (oldIndex >= variables.size() || newIndex >= variables.size())
|
||||
return;
|
||||
|
||||
auto nameAndVariable = variables[oldIndex];
|
||||
variables.erase(variables.begin() + oldIndex);
|
||||
Insert(nameAndVariable.first, nameAndVariable.second, newIndex);
|
||||
variables.insert(variables.begin() + newIndex, nameAndVariable);
|
||||
}
|
||||
#endif
|
||||
|
||||
void VariablesContainer::SerializeTo(SerializerElement & element) const
|
||||
void VariablesContainer::SerializeTo(SerializerElement& element) const
|
||||
{
|
||||
element.ConsiderAsArrayOf("variable");
|
||||
for ( std::size_t j = 0;j < variables.size();j++ )
|
||||
{
|
||||
SerializerElement & variableElement = element.AddChild("variable");
|
||||
for (std::size_t j = 0; j < variables.size(); j++) {
|
||||
SerializerElement& variableElement = element.AddChild("variable");
|
||||
variableElement.SetAttribute("name", variables[j].first);
|
||||
variables[j].second.SerializeTo(variableElement);
|
||||
variables[j].second->SerializeTo(variableElement);
|
||||
}
|
||||
}
|
||||
|
||||
void VariablesContainer::UnserializeFrom(const SerializerElement & element)
|
||||
void VariablesContainer::UnserializeFrom(const SerializerElement& element)
|
||||
{
|
||||
Clear();
|
||||
element.ConsiderAsArrayOf("variable", "Variable");
|
||||
for ( std::size_t j = 0;j < element.GetChildrenCount();j++ )
|
||||
{
|
||||
const SerializerElement & variableElement = element.GetChild(j);
|
||||
for (std::size_t j = 0; j < element.GetChildrenCount(); j++) {
|
||||
const SerializerElement& variableElement = element.GetChild(j);
|
||||
|
||||
Variable variable;
|
||||
variable.UnserializeFrom(variableElement);
|
||||
Insert(variableElement.GetStringAttribute("name", "", "Name" ), variable, -1);
|
||||
Insert(variableElement.GetStringAttribute("name", "", "Name"), variable, -1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_VARIABLESCONTAINER_H
|
||||
#include "GDCore/String.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "GDCore/Project/Variable.h"
|
||||
namespace gd { class SerializerElement; }
|
||||
class TiXmlElement;
|
||||
@@ -52,18 +53,14 @@ public:
|
||||
const Variable & Get(const gd::String & name) const;
|
||||
|
||||
/**
|
||||
* \brief Return a pair containing the name and the variable at position \index in the container.
|
||||
*
|
||||
* \note If index is invalid, an empty variable is returned.
|
||||
* \brief Return a reference to the variable at the specified position in the list.
|
||||
*/
|
||||
std::pair<gd::String, gd::Variable> & Get(std::size_t index);
|
||||
Variable & Get(std::size_t index);
|
||||
|
||||
/**
|
||||
* \brief Return a pair containing the name and the variable at position \index in the container.
|
||||
*
|
||||
* \note If index is invalid, an empty variable is returned.
|
||||
* \brief Return a reference to the variable at the specified position in the list.
|
||||
*/
|
||||
const std::pair<gd::String, gd::Variable> & Get(std::size_t index) const;
|
||||
const Variable & Get(std::size_t index) const;
|
||||
|
||||
/**
|
||||
* Must add a new variable constructed from the variable passed as parameter.
|
||||
@@ -79,6 +76,11 @@ public:
|
||||
*/
|
||||
std::size_t Count() const { return variables.size(); };
|
||||
|
||||
/**
|
||||
* \brief Return the name of the variable at a position
|
||||
*/
|
||||
const gd::String & GetNameAt(std::size_t index) const;
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
/**
|
||||
* \brief return the position of the variable called "name" in the variable list
|
||||
@@ -94,10 +96,16 @@ public:
|
||||
Variable & InsertNew(const gd::String & name, std::size_t position = -1);
|
||||
|
||||
/**
|
||||
* \brief Remove the specified variable from the container.
|
||||
* \brief Remove the variable with the specified name from the container.
|
||||
* \note This operation is not recursive on variable children
|
||||
*/
|
||||
void Remove(const gd::String & name);
|
||||
|
||||
/**
|
||||
* \brief Remove the specified variable from the container.
|
||||
*/
|
||||
void RemoveRecursively(const gd::Variable & variable);
|
||||
|
||||
/**
|
||||
* \brief Rename a variable.
|
||||
* \return true if the variable was renamed, false otherwise.
|
||||
@@ -138,8 +146,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
std::vector < std::pair<gd::String, gd::Variable> > variables;
|
||||
static std::pair<gd::String, Variable> badVariable;
|
||||
std::vector < std::pair<gd::String, std::shared_ptr<gd::Variable>> > variables;
|
||||
static gd::Variable badVariable;
|
||||
static gd::String badName;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -66,11 +66,11 @@ public:
|
||||
for(auto & child : variable.GetAllChildren())
|
||||
{
|
||||
const gd::String & name = child.first;
|
||||
const gd::Variable & serializedItem = child.second;
|
||||
inventory.SetMaximum(name, serializedItem.GetChild("maxCount").GetValue());
|
||||
inventory.SetUnlimited(name, serializedItem.GetChild("unlimited").GetString() == "true");
|
||||
inventory.SetCount(name, serializedItem.GetChild("count").GetValue());
|
||||
inventory.Equip(name, serializedItem.GetChild("equipped").GetString() == "true");
|
||||
const auto & serializedItem = child.second;
|
||||
inventory.SetMaximum(name, serializedItem->GetChild("maxCount").GetValue());
|
||||
inventory.SetUnlimited(name, serializedItem->GetChild("unlimited").GetString() == "true");
|
||||
inventory.SetCount(name, serializedItem->GetChild("count").GetValue());
|
||||
inventory.Equip(name, serializedItem->GetChild("equipped").GetString() == "true");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,11 +4,11 @@
|
||||
Light_Manager::Light_Manager() :
|
||||
commonBlurEffectLoaded(false)
|
||||
{
|
||||
std::cout << "Creating Light Manager";
|
||||
std::cout << "Creating Light Manager" << std::endl;
|
||||
}
|
||||
Light_Manager::~Light_Manager()
|
||||
{
|
||||
std::cout << "Destroying Light Manager";
|
||||
std::cout << "Destroying Light Manager" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -245,11 +245,10 @@ gd::String GD_API VariableStructureToJSON(const gd::Variable & variable)
|
||||
|
||||
gd::String str = "{";
|
||||
bool firstChild = true;
|
||||
for(std::map<gd::String, gd::Variable>::const_iterator i = variable.GetAllChildren().begin();
|
||||
i != variable.GetAllChildren().end();++i)
|
||||
for(auto i = variable.GetAllChildren().begin(); i != variable.GetAllChildren().end();++i)
|
||||
{
|
||||
if ( !firstChild ) str += ",";
|
||||
str += gd::String::FromUTF8(StringToQuotedJSONString(i->first.c_str()))+": "+VariableStructureToJSON(i->second);
|
||||
str += gd::String::FromUTF8(StringToQuotedJSONString(i->first.c_str()))+": "+VariableStructureToJSON(*i->second);
|
||||
|
||||
firstChild = false;
|
||||
}
|
||||
|
@@ -287,7 +287,7 @@ unsigned int GD_API GetVariableChildCount(gd::Variable & variable)
|
||||
{
|
||||
if (variable.IsStructure() == false) return 0;
|
||||
|
||||
return variable.GetAllChildren().size();
|
||||
return variable.GetChildrenCount();
|
||||
}
|
||||
|
||||
double GD_API GetVariableValue(const gd::Variable & variable)
|
||||
|
@@ -403,7 +403,7 @@ RaycastResult RuntimeObject::RaycastTest(float x, float y, float angle, float di
|
||||
|
||||
if ( sqrt(diffX*diffX + diffY*diffY) > boundingRadius + dist )
|
||||
return result;
|
||||
|
||||
|
||||
float endX = x + dist*cos(angle*3.14159/180.0);
|
||||
float endY = y + dist*sin(angle*3.14159/180.0);
|
||||
float testSqDist = closest ? dist*dist : 0.0f;
|
||||
@@ -412,7 +412,7 @@ RaycastResult RuntimeObject::RaycastTest(float x, float y, float angle, float di
|
||||
for (std::size_t i = 0; i < hitboxes.size(); ++i)
|
||||
{
|
||||
RaycastResult res = PolygonRaycastTest(hitboxes[i], x, y, endX, endY);
|
||||
|
||||
|
||||
if ( res.collision ) {
|
||||
if ( closest && (res.closeSqDist < testSqDist) ) {
|
||||
testSqDist = res.closeSqDist;
|
||||
@@ -424,7 +424,7 @@ RaycastResult RuntimeObject::RaycastTest(float x, float y, float angle, float di
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -752,5 +752,5 @@ void RuntimeObject::VariableClearChildren(gd::Variable & variable)
|
||||
unsigned int RuntimeObject::GetVariableChildCount(gd::Variable & variable)
|
||||
{
|
||||
if (variable.IsStructure() == false) return 0;
|
||||
return variable.GetAllChildren().size();
|
||||
return variable.GetChildrenCount();
|
||||
}
|
||||
|
@@ -38,15 +38,16 @@ void RuntimeVariablesContainer::Merge(const gd::VariablesContainer & container)
|
||||
{
|
||||
for ( std::size_t i = 0; i<container.Count();++i)
|
||||
{
|
||||
const std::pair<gd::String, gd::Variable> & variable = container.Get(i);
|
||||
const gd::String & name = container.GetNameAt(i);
|
||||
const gd::Variable & variable = container.Get(i);
|
||||
|
||||
if ( Has(variable.first) )
|
||||
Get(variable.first) = variable.second;
|
||||
if ( Has(name) )
|
||||
Get(name) = variable;
|
||||
else
|
||||
{
|
||||
gd::Variable * newVariable = new gd::Variable(variable.second);
|
||||
gd::Variable * newVariable = new gd::Variable(variable);
|
||||
variablesArray.push_back(newVariable);
|
||||
variables[variable.first] = newVariable;
|
||||
variables[name] = newVariable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ Getting started [ to download GD! |
|
||||
| Download GDevelop to make games | Go on [GDevelop website](https://gdevelop-app.com) to download GD! |
|
||||
| Contribute to the new editor | Download [Node.js] and follow this [README](newIDE/README.md). |
|
||||
| Contribute to the old C++ editor | Download and launch [CMake], choose this directory as the source, generate the Makefile and launch it. Be sure to have [required development libraries](http://4ian.github.io/GD-Documentation/GDCore%20Documentation/setup_dev_env.html) installed. <br><br> Fully detailed instructions are [available here](http://4ian.github.io/GD-Documentation). |
|
||||
| Help to translate GDevelop | Go on the [GDevelop project on Crowdin](https://crowdin.com/project/gdevelop). |
|
||||
@@ -36,7 +36,7 @@ Links
|
||||
### Community
|
||||
|
||||
* [GDevelop forums](http://forum.compilgames.net)
|
||||
* [GDevelop homepage](http://www.compilgames.net) ([open source](https://github.com/4ian/GDevelop-website))
|
||||
* [GDevelop homepage](https://gdevelop-app.com) ([open-source](https://github.com/4ian/GDevelop-website))
|
||||
* [GDevelop wiki](http://wiki.compilgames.net)
|
||||
* Help translate GD in your language: [GDevelop project on Crowdin](https://crowdin.com/project/gdevelop).
|
||||
* [GDevelop Nightly Builds](http://nightlies.gd.victorlevasseur.com/) (powered by a [Buildbot](https://github.com/victorlevasseur/GD-Buildbot)) ([open source](https://github.com/victorlevasseur/GD-Nightlies-Website))
|
||||
|
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(SET CMP0015 NEW)
|
||||
|
||||
project(GDVersion)
|
||||
set(GD_VERSION_STR "4.0.96")
|
||||
set(GD_VERSION_STR "4.0.97-0-release")
|
||||
|
||||
if(FULL_VERSION_NUMBER)
|
||||
set(GENERATE_VERSION_SCRIPT ${PROJECT_SOURCE_DIR}/GenerateVersionFull.cmake)
|
||||
|
@@ -23,7 +23,8 @@ yarn start #or npm start
|
||||
|
||||
This will open the app in your web browser.
|
||||
|
||||
Images resources, GDJS Runtime, extensions will be copied in resources, and [libGD.js](https://github.com/4ian/GDevelop.js) will be downloaded automatically.
|
||||
Images resources, GDJS Runtime, extensions will be copied in resources, and [libGD.js](https://github.com/4ian/GDevelop.js) will be downloaded automatically. If you wish, you can
|
||||
[build libGD.js by yourself](https://github.com/4ian/GDevelop.js) (useful if you modified GDevelop native code like extensions).
|
||||
|
||||
### Development of the standalone app
|
||||
|
||||
@@ -97,7 +98,6 @@ yarn deploy #or npm run deploy
|
||||
This new editor is still in development and is missing some features:
|
||||
|
||||
- [ ] Support for translations (See an [example of a component that can be translated](https://github.com/4ian/GD/blob/master/newIDE/app/src/MainFrame/Toolbar.js#L44))
|
||||
- [ ] [Collision mask editor](https://trello.com/c/2Kzwj61r/47-collision-masks-editors-for-sprite-objects-in-the-new-ide)
|
||||
- [ ] Support for native games
|
||||
- [ ] More [documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) about how to package for iOS/Android with Cordova/PhoneGap Build or Cocos2d-JS.
|
||||
- [ ] Search in events
|
||||
|
4
newIDE/app/flow-typed/libGD.js
vendored
4
newIDE/app/flow-typed/libGD.js
vendored
@@ -24,5 +24,9 @@ declare type gdParameterMetadata = EmscriptenObject;
|
||||
declare type gdVariable = EmscriptenObject;
|
||||
declare type gdVariablesContainer = EmscriptenObject;
|
||||
|
||||
declare type gdVectorPolygon2d = EmscriptenObject;
|
||||
|
||||
declare type gdSpriteObject = EmscriptenObject;
|
||||
|
||||
//Represents all objects that have serializeTo and unserializeFrom methods.
|
||||
declare type gdSerializable = EmscriptenObject;
|
||||
|
@@ -38,7 +38,7 @@ if (shell.test('-f', sourceFile)) {
|
||||
|
||||
var file = fs.createWriteStream('../public/libGD.js');
|
||||
https.get(
|
||||
'https://github.com/4ian/GDevelop.js/releases/download/4.0.96/libGD.js',
|
||||
'https://github.com/4ian/GDevelop.js/releases/download/5.0.0-beta28/libGD.js',
|
||||
function(response) {
|
||||
if (response.statusCode !== 200) {
|
||||
shell.echo(
|
||||
|
@@ -87,11 +87,20 @@ export default class InstructionParametersEditor extends Component {
|
||||
<div key={type} style={styles.parametersContainer}>
|
||||
{mapFor(0, instructionMetadata.getParametersCount(), i => {
|
||||
const parameterMetadata = instructionMetadata.getParameter(i);
|
||||
const parameterMetadataType = parameterMetadata.getType();
|
||||
const ParameterComponent = ParameterRenderingService.getParameterComponent(
|
||||
parameterMetadata.getType()
|
||||
parameterMetadataType
|
||||
);
|
||||
|
||||
if (parameterMetadata.isCodeOnly()) return null;
|
||||
if (!ParameterComponent) {
|
||||
console.warn(
|
||||
'Missing parameter component for',
|
||||
parameterMetadataType
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ParameterComponent
|
||||
parameterMetadata={parameterMetadata}
|
||||
|
@@ -17,8 +17,7 @@ export const enumerateVariables = (
|
||||
if (!variable.isStructure()) return names;
|
||||
|
||||
variable
|
||||
.getAllChildren()
|
||||
.keys()
|
||||
.getAllChildrenNames()
|
||||
.toJSArray()
|
||||
.forEach(childName => {
|
||||
enumerateVariableAndChildrenNames(
|
||||
@@ -36,10 +35,9 @@ export const enumerateVariables = (
|
||||
mapFor(0, variablesContainer.count(), i => {
|
||||
if (!variablesContainer) return [];
|
||||
|
||||
const variableAndName = variablesContainer.getAt(i);
|
||||
return enumerateVariableAndChildrenNames(
|
||||
variableAndName.getName(),
|
||||
variableAndName.getVariable()
|
||||
variablesContainer.getNameAt(i),
|
||||
variablesContainer.getAt(i)
|
||||
);
|
||||
})
|
||||
);
|
||||
|
@@ -1,6 +1,4 @@
|
||||
// @flow
|
||||
import ParameterRenderingService from '../ParameterRenderingService';
|
||||
|
||||
export type ParameterFieldProps = {|
|
||||
parameterMetadata?: gdParameterMetadata,
|
||||
project: gdProject,
|
||||
@@ -8,5 +6,8 @@ export type ParameterFieldProps = {|
|
||||
onChange: string => void,
|
||||
value: string,
|
||||
isInline?: boolean,
|
||||
parameterRenderingService?: typeof ParameterRenderingService,
|
||||
parameterRenderingService?: {
|
||||
components: any,
|
||||
getParameterComponent: (type: string) => any,
|
||||
},
|
||||
|};
|
||||
|
@@ -5,6 +5,7 @@ import CircularProgress from 'material-ui/CircularProgress';
|
||||
import LinearProgress from 'material-ui/LinearProgress';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import { Line, Spacer } from '../../../UI/Grid';
|
||||
import HelpButton from '../../../UI/HelpButton';
|
||||
|
||||
export default ({
|
||||
exportStep,
|
||||
@@ -16,6 +17,7 @@ export default ({
|
||||
buildMax,
|
||||
buildProgress,
|
||||
errored,
|
||||
onPlayStore,
|
||||
}) => (
|
||||
<Stepper
|
||||
activeStep={
|
||||
@@ -69,9 +71,7 @@ export default ({
|
||||
<StepLabel>Build</StepLabel>
|
||||
<StepContent>
|
||||
{errored ? (
|
||||
<p>
|
||||
Something wrong happened :(
|
||||
</p>
|
||||
<p>Something wrong happened :(</p>
|
||||
) : exportStep === 'waiting-for-build' ? (
|
||||
<Line alignItems="center">
|
||||
<CircularProgress size={20} />
|
||||
@@ -95,6 +95,10 @@ export default ({
|
||||
<StepContent>
|
||||
<Line>
|
||||
<RaisedButton label="Download" primary onClick={onDownload} />
|
||||
<HelpButton
|
||||
label="Upload to Play Store"
|
||||
helpPagePath="/publishing/android_and_ios/play-store"
|
||||
/>
|
||||
<FlatButton label="See logs" onClick={onDownloadLogs} />
|
||||
</Line>
|
||||
<Line expand>
|
||||
|
@@ -12,7 +12,7 @@ export default class InstancesResizer {
|
||||
}
|
||||
|
||||
_roundXPosition(x) {
|
||||
if (!this.options.snap || !this.options.grid) return x;
|
||||
if (!this.options.snap || !this.options.grid) return Math.round(x);
|
||||
|
||||
return (
|
||||
Math.round((x - this.options.gridOffsetX) / this.options.gridWidth) *
|
||||
@@ -22,7 +22,7 @@ export default class InstancesResizer {
|
||||
}
|
||||
|
||||
_roundYPosition(y) {
|
||||
if (!this.options.snap || !this.options.grid) return y;
|
||||
if (!this.options.snap || !this.options.grid) return Math.round(y);
|
||||
|
||||
return (
|
||||
Math.round((y - this.options.gridOffsetY) / this.options.gridHeight) *
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import PIXI from 'pixi.js';
|
||||
import transformRect from '../Utils/TransformRect';
|
||||
import { rgbToHexNumber } from '../Utils/ColorTransformer';
|
||||
|
||||
export default class WindowBorder {
|
||||
constructor({ project, toCanvasCoordinates }) {
|
||||
constructor({ project, layout, toCanvasCoordinates }) {
|
||||
this.project = project;
|
||||
this.layout = layout;
|
||||
this.toCanvasCoordinates = toCanvasCoordinates;
|
||||
|
||||
this.pixiRectangle = new PIXI.Graphics();
|
||||
@@ -28,7 +30,15 @@ export default class WindowBorder {
|
||||
|
||||
this.pixiRectangle.clear();
|
||||
this.pixiRectangle.beginFill(0x000000);
|
||||
this.pixiRectangle.lineStyle(1, 0x000000, 1);
|
||||
this.pixiRectangle.lineStyle(
|
||||
1,
|
||||
rgbToHexNumber(
|
||||
(128 + this.layout.getBackgroundColorRed() % 256),
|
||||
(128 + this.layout.getBackgroundColorBlue() % 256),
|
||||
(128 + this.layout.getBackgroundColorGreen() % 256)
|
||||
),
|
||||
1
|
||||
);
|
||||
this.pixiRectangle.alpha = 1;
|
||||
this.pixiRectangle.fillAlpha = 0;
|
||||
this.pixiRectangle.drawRect(
|
||||
|
@@ -212,6 +212,7 @@ export default class InstancesEditorContainer extends Component {
|
||||
});
|
||||
this.windowBorder = new WindowBorder({
|
||||
project: props.project,
|
||||
layout: props.layout,
|
||||
toCanvasCoordinates: this.viewPosition.toCanvasCoordinates,
|
||||
});
|
||||
this.windowMask = new WindowMask({
|
||||
|
@@ -8,7 +8,7 @@ export default class BaseEditor extends React.Component<*,*> {
|
||||
setToolbar: () => {},
|
||||
};
|
||||
|
||||
getProject(): gdProject {
|
||||
getProject(): ?gdProject {
|
||||
return this.props.project;
|
||||
}
|
||||
|
||||
|
16
newIDE/app/src/MainFrame/Editors/StartPage/GDevelopLogo.js
Normal file
16
newIDE/app/src/MainFrame/Editors/StartPage/GDevelopLogo.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
|
||||
const styles = {
|
||||
logo: {
|
||||
width: '100%',
|
||||
},
|
||||
};
|
||||
|
||||
const ThemableGDevelopLogo = ({ muiTheme }) => (
|
||||
<img src={muiTheme.logo.src} alt="" style={styles.logo} />
|
||||
);
|
||||
|
||||
const GDevelopLogo = muiThemeable()(ThemableGDevelopLogo);
|
||||
export default GDevelopLogo;
|
@@ -0,0 +1,25 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
|
||||
const styles = {
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
overflowY: 'scroll',
|
||||
},
|
||||
};
|
||||
|
||||
const ThemableScrollBackground = ({ muiTheme, children }) => (
|
||||
<div
|
||||
style={{
|
||||
...styles.scrollContainer,
|
||||
backgroundColor: muiTheme.palette.canvasColor,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
const ScrollBackground = muiThemeable()(ThemableScrollBackground);
|
||||
export default ScrollBackground;
|
@@ -2,8 +2,8 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||
import defaultTheme from '../../UI/Theme/DefaultTheme';
|
||||
import StartPage from './StartPage';
|
||||
import defaultTheme from '../../../UI/Theme/DefaultTheme';
|
||||
import StartPage from '.';
|
||||
const gd = global.gd;
|
||||
|
||||
describe('StartPage', () => {
|
@@ -48,7 +48,7 @@ exports[`StartPage renders the start page with a project 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"WebkitTapHighlightColor": "rgba(0,0,0,0)",
|
||||
"backgroundColor": "#f7f7f7",
|
||||
"backgroundColor": "#f0f0f0",
|
||||
"borderRadius": 2,
|
||||
"boxShadow": "0 1px 6px rgba(0, 0, 0, 0.12),
|
||||
0 1px 4px rgba(0, 0, 0, 0.12)",
|
||||
@@ -634,7 +634,7 @@ exports[`StartPage renders the start page with no project opened 1`] = `
|
||||
style={
|
||||
Object {
|
||||
"WebkitTapHighlightColor": "rgba(0,0,0,0)",
|
||||
"backgroundColor": "#f7f7f7",
|
||||
"backgroundColor": "#f0f0f0",
|
||||
"borderRadius": 2,
|
||||
"boxShadow": "0 1px 6px rgba(0, 0, 0, 0.12),
|
||||
0 1px 4px rgba(0, 0, 0, 0.12)",
|
@@ -1,18 +1,15 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import Paper from 'material-ui/Paper';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import BaseEditor from './BaseEditor';
|
||||
import Window from '../../Utils/Window';
|
||||
import { Line } from '../../UI/Grid';
|
||||
import BaseEditor from '../BaseEditor';
|
||||
import Window from '../../../Utils/Window';
|
||||
import { Line } from '../../../UI/Grid';
|
||||
import GDevelopLogo from './GDevelopLogo';
|
||||
import ScrollBackground from './ScrollBackground';
|
||||
|
||||
const styles = {
|
||||
scrollContainer: {
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
overflowY: 'scroll',
|
||||
},
|
||||
innerContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@@ -35,14 +32,11 @@ const styles = {
|
||||
buttonsPaper: {
|
||||
width: '100%',
|
||||
},
|
||||
logo: {
|
||||
width: '100%',
|
||||
},
|
||||
};
|
||||
|
||||
class ThemableStartPage extends BaseEditor {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
class StartPage extends BaseEditor {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
aboutDialogOpen: false,
|
||||
@@ -65,28 +59,21 @@ class ThemableStartPage extends BaseEditor {
|
||||
onCreate,
|
||||
onOpenProjectManager,
|
||||
onCloseProject,
|
||||
muiTheme,
|
||||
onOpenAboutDialog,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: muiTheme.palette.canvasColor,
|
||||
...styles.scrollContainer,
|
||||
}}
|
||||
>
|
||||
<ScrollBackground>
|
||||
<div style={styles.innerContainer}>
|
||||
<Line expand justifyContent="center">
|
||||
<div style={styles.centerContainer}>
|
||||
<Paper
|
||||
zDepth={1}
|
||||
style={{
|
||||
backgroundColor: muiTheme.startPage.backgroundColor,
|
||||
...styles.logoPaper,
|
||||
}}
|
||||
>
|
||||
<img src={muiTheme.logo.src} alt="" style={styles.logo} />
|
||||
<GDevelopLogo />
|
||||
<p>
|
||||
GDevelop is an easy-to-use game creator with no programming
|
||||
language to learn.
|
||||
@@ -160,10 +147,9 @@ class ThemableStartPage extends BaseEditor {
|
||||
</div>
|
||||
</Line>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollBackground>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const StartPage = muiThemeable()(ThemableStartPage);
|
||||
export default StartPage;
|
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { TableRow, TableRowColumn } from 'material-ui/Table';
|
||||
import Add from 'material-ui/svg-icons/content/add';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import styles from './styles';
|
||||
|
||||
const AddPolygonRow = ({ onAdd }) => (
|
||||
<TableRow>
|
||||
<TableRowColumn style={styles.handleColumn} />
|
||||
<TableRowColumn />
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
<IconButton onClick={onAdd}>
|
||||
<Add />
|
||||
</IconButton>
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
export default AddPolygonRow;
|
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { TableRow, TableRowColumn } from 'material-ui/Table';
|
||||
import AddCircle from 'material-ui/svg-icons/content/add-circle';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import styles from './styles';
|
||||
|
||||
const AddVerticeRow = ({ onAdd }) => (
|
||||
<TableRow>
|
||||
<TableRowColumn style={styles.handleColumn} />
|
||||
<TableRowColumn />
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
<IconButton onClick={onAdd}>
|
||||
<AddCircle />
|
||||
</IconButton>
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
export default AddVerticeRow;
|
@@ -0,0 +1,92 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { mapVector } from '../../../../Utils/MapFor';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
position: 'relative',
|
||||
},
|
||||
svg: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {|
|
||||
polygons: gdVectorPolygon2d,
|
||||
isDefaultBoundingBox: boolean,
|
||||
imageWidth: number,
|
||||
imageHeight: number,
|
||||
|};
|
||||
|
||||
export default class CollisionMasksPreview extends React.Component<
|
||||
Props,
|
||||
void
|
||||
> {
|
||||
_renderBoundingBox() {
|
||||
const { imageWidth, imageHeight } = this.props;
|
||||
|
||||
return (
|
||||
<polygon
|
||||
fill="rgba(255,0,0,0.2)"
|
||||
stroke="rgba(255,0,0,0.5)"
|
||||
strokeWidth={1}
|
||||
fileRule="evenodd"
|
||||
points={`0,0 ${imageWidth},0 ${imageWidth},${imageHeight} 0,${imageHeight}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
_renderPolygons() {
|
||||
const { polygons } = this.props;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{mapVector(polygons, (polygon, i) => {
|
||||
const vertices = polygon.getVertices();
|
||||
return (
|
||||
<polygon
|
||||
key={`polygon-${i}`}
|
||||
fill="rgba(255,0,0,0.2)"
|
||||
stroke="rgba(255,0,0,0.5)"
|
||||
strokeWidth={1}
|
||||
fileRule="evenodd"
|
||||
points={mapVector(
|
||||
vertices,
|
||||
(vertex, j) => `${vertex.get_x()},${vertex.get_y()}`
|
||||
).join(' ')}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{mapVector(polygons, (polygon, i) => {
|
||||
const vertices = polygon.getVertices();
|
||||
return mapVector(vertices, (vertex, j) => (
|
||||
<circle
|
||||
key={`polygon-${i}-vertex-${j}`}
|
||||
fill="rgba(255,0,0,0.5)"
|
||||
strokeWidth={1}
|
||||
cx={vertex.get_x()}
|
||||
cy={vertex.get_y()}
|
||||
r={3}
|
||||
/>
|
||||
));
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isDefaultBoundingBox } = this.props;
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={this.props.imageWidth}
|
||||
height={this.props.imageHeight}
|
||||
style={styles.svg}
|
||||
>
|
||||
{isDefaultBoundingBox && this._renderBoundingBox()}
|
||||
{!isDefaultBoundingBox && this._renderPolygons()}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { TableRow, TableRowColumn } from 'material-ui/Table';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import styles from './styles';
|
||||
|
||||
const ThemablePolygonRow = ({ onRemove, isConvex, verticesCount, muiTheme }) => {
|
||||
return (
|
||||
<TableRow
|
||||
style={{
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
}}
|
||||
>
|
||||
<TableRowColumn style={styles.handleColumn}>
|
||||
{/* <DragHandle /> Reordering polygons is not supported for now */}
|
||||
</TableRowColumn>
|
||||
{isConvex && (
|
||||
<TableRowColumn>
|
||||
{verticesCount === 3 && `Triangle`}
|
||||
{verticesCount === 4 && `Quadrilateral`}
|
||||
{verticesCount >= 5 && `Polygon with ${verticesCount} vertices`}
|
||||
</TableRowColumn>
|
||||
)}
|
||||
{!isConvex && <TableRowColumn>Polygon is not convex!</TableRowColumn>}
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.coordinateColumn} />
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
{!!onRemove && (
|
||||
<IconButton onClick={onRemove}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
)}
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
const PointRow = muiThemeable()(ThemablePolygonRow);
|
||||
export default PointRow;
|
@@ -0,0 +1,182 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import flatten from 'lodash/flatten';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableHeader,
|
||||
TableHeaderColumn,
|
||||
TableRow,
|
||||
TableRowColumn,
|
||||
} from 'material-ui/Table';
|
||||
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
||||
import { mapVector } from '../../../../Utils/MapFor';
|
||||
import styles from './styles';
|
||||
import VerticeRow from './VerticeRow';
|
||||
import PolygonRow from './PolygonRow';
|
||||
import AddVerticeRow from './AddVerticeRow';
|
||||
import AddPolygonRow from './AddPolygonRow';
|
||||
const gd = global.gd;
|
||||
|
||||
const SortableAddPolygonRow = SortableElement(AddPolygonRow);
|
||||
const SortableAddVerticeRow = SortableElement(AddVerticeRow);
|
||||
const SortableVerticeRow = SortableElement(VerticeRow);
|
||||
const SortablePolygonRow = SortableElement(PolygonRow);
|
||||
|
||||
type PolygonsListBodyProps = {|
|
||||
polygons: gdVectorPolygon2d,
|
||||
onPolygonsUpdated: () => void,
|
||||
|
||||
// Sprite size is useful to make sure polygon vertices
|
||||
// are not put outside the sprite bounding box, which is not supported:
|
||||
spriteWidth: number,
|
||||
spriteHeight: number,
|
||||
|};
|
||||
|
||||
class PolygonsListBody extends Component<PolygonsListBodyProps, void> {
|
||||
_onPolygonUpdated() {
|
||||
this.forceUpdate();
|
||||
this.props.onPolygonsUpdated();
|
||||
}
|
||||
|
||||
updateVerticeX = (vertice, newValue) => {
|
||||
// Ensure vertice stays inside the sprite bounding box.
|
||||
vertice.set_x(Math.min(this.props.spriteWidth, Math.max(newValue, 0)));
|
||||
this._onPolygonUpdated();
|
||||
};
|
||||
|
||||
updateVerticeY = (vertice, newValue) => {
|
||||
// Ensure vertice stays inside the sprite bounding box.
|
||||
vertice.set_y(Math.min(this.props.spriteHeight, Math.max(newValue, 0)));
|
||||
this._onPolygonUpdated();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { polygons } = this.props;
|
||||
|
||||
const polygonRows = flatten(
|
||||
mapVector(polygons, (polygon, i) => {
|
||||
const vertices = polygon.getVertices();
|
||||
const isConvex = polygon.isConvex();
|
||||
|
||||
return [
|
||||
<SortablePolygonRow
|
||||
index={i}
|
||||
disabled
|
||||
key={'polygon-' + i}
|
||||
polygon={polygon}
|
||||
onRemove={() => {
|
||||
gd.removeFromVectorPolygon2d(polygons, i);
|
||||
this._onPolygonUpdated();
|
||||
}}
|
||||
isConvex={isConvex}
|
||||
verticesCount={vertices.size()}
|
||||
/>,
|
||||
mapVector(vertices, (vertice, j) => (
|
||||
<SortableVerticeRow
|
||||
index={i}
|
||||
disabled
|
||||
key={`polygon-${i}-vertice-${j}`}
|
||||
verticeX={vertice.get_x()}
|
||||
verticeY={vertice.get_y()}
|
||||
onChangeVerticeX={newValue =>
|
||||
this.updateVerticeX(vertice, newValue)}
|
||||
onChangeVerticeY={newValue =>
|
||||
this.updateVerticeY(vertice, newValue)}
|
||||
onRemove={() => {
|
||||
gd.removeFromVectorVector2f(polygon.getVertices(), j);
|
||||
this._onPolygonUpdated();
|
||||
}}
|
||||
canRemove={vertices.size() > 3}
|
||||
hasWarning={!isConvex}
|
||||
/>
|
||||
)),
|
||||
<SortableAddVerticeRow
|
||||
index={0}
|
||||
key={`polygon-${i}-add-vertice-row`}
|
||||
disabled
|
||||
onAdd={() => {
|
||||
const newVertice = new gd.Vector2f();
|
||||
polygon.getVertices().push_back(newVertice);
|
||||
newVertice.delete();
|
||||
|
||||
this._onPolygonUpdated();
|
||||
}}
|
||||
/>,
|
||||
];
|
||||
})
|
||||
);
|
||||
|
||||
const addRow = (
|
||||
<SortableAddPolygonRow
|
||||
index={0}
|
||||
key={'add-polygon-row'}
|
||||
disabled
|
||||
onAdd={() => {
|
||||
const newPolygon = gd.Polygon2d.createRectangle(32, 32);
|
||||
newPolygon.move(
|
||||
this.props.spriteWidth / 2,
|
||||
this.props.spriteHeight / 2
|
||||
);
|
||||
polygons.push_back(newPolygon);
|
||||
|
||||
this._onPolygonUpdated();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<TableBody
|
||||
displayRowCheckbox={false}
|
||||
deselectOnClickaway={true}
|
||||
showRowHover={true}
|
||||
>
|
||||
{[...polygonRows, addRow]}
|
||||
</TableBody>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SortablePolygonsListBody = SortableContainer(PolygonsListBody);
|
||||
SortablePolygonsListBody.muiName = 'TableBody';
|
||||
|
||||
type Props = {|
|
||||
polygons: gdVectorPolygon2d,
|
||||
onPolygonsUpdated: () => void,
|
||||
spriteWidth: number,
|
||||
spriteHeight: number,
|
||||
|};
|
||||
|
||||
export default class PolygonsList extends Component<Props, void> {
|
||||
render() {
|
||||
return (
|
||||
<Table selectable={false}>
|
||||
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
|
||||
<TableRow>
|
||||
<TableHeaderColumn style={styles.handleColumn} />
|
||||
<TableHeaderColumn>Polygon</TableHeaderColumn>
|
||||
<TableHeaderColumn style={styles.coordinateColumn}>
|
||||
X
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn style={styles.coordinateColumn}>
|
||||
Y
|
||||
</TableHeaderColumn>
|
||||
<TableRowColumn style={styles.toolColumn} />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<SortablePolygonsListBody
|
||||
polygons={this.props.polygons}
|
||||
onPolygonsUpdated={this.props.onPolygonsUpdated}
|
||||
spriteWidth={this.props.spriteWidth}
|
||||
spriteHeight={this.props.spriteHeight}
|
||||
onSortEnd={({ oldIndex, newIndex }) => {
|
||||
// Reordering polygons is not supported for now
|
||||
}}
|
||||
helperClass="sortable-helper"
|
||||
useDragHandle
|
||||
lockToContainerEdges
|
||||
/>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { TableRow, TableRowColumn } from 'material-ui/Table';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import TextField from 'material-ui/TextField';
|
||||
import Warning from 'material-ui/svg-icons/alert/warning';
|
||||
import muiThemeable from 'material-ui/styles/muiThemeable';
|
||||
import styles from './styles';
|
||||
|
||||
const ThemableVerticeRow = ({
|
||||
hasWarning,
|
||||
canRemove,
|
||||
onRemove,
|
||||
verticeX,
|
||||
verticeY,
|
||||
onChangeVerticeX,
|
||||
onChangeVerticeY,
|
||||
muiTheme,
|
||||
}) => (
|
||||
<TableRow style={{
|
||||
backgroundColor: muiTheme.list.itemsBackgroundColor,
|
||||
}}>
|
||||
<TableRowColumn style={styles.handleColumn}>
|
||||
{/* <DragHandle /> Reordering vertices is not supported for now */}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn>
|
||||
{hasWarning && <Warning />}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={styles.coordinateColumn}>
|
||||
<TextField
|
||||
value={verticeX}
|
||||
type="number"
|
||||
id="vertice-x"
|
||||
onChange={(e, value) => onChangeVerticeX(parseFloat(value || 0, 10))}
|
||||
/>
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={styles.coordinateColumn}>
|
||||
<TextField
|
||||
value={verticeY}
|
||||
type="number"
|
||||
id="vertice-y"
|
||||
onChange={(e, value) => onChangeVerticeY(parseFloat(value || 0, 10))}
|
||||
/>
|
||||
</TableRowColumn>
|
||||
<TableRowColumn style={styles.toolColumn}>
|
||||
{!!onRemove && (
|
||||
<IconButton onClick={onRemove} disabled={!canRemove}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
)}
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
const PointRow = muiThemeable()(
|
||||
ThemableVerticeRow
|
||||
);
|
||||
export default PointRow;
|
@@ -0,0 +1,279 @@
|
||||
import React, { Component } from 'react';
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import EmptyMessage from '../../../../UI/EmptyMessage';
|
||||
import { Line, Column } from '../../../../UI/Grid';
|
||||
import { mapFor } from '../../../../Utils/MapFor';
|
||||
import PolygonsList from './PolygonsList';
|
||||
import CollisionMasksPreview from './CollisionMasksPreview';
|
||||
import ImagePreview from '../../../ImagePreview';
|
||||
import {
|
||||
getCurrentElements,
|
||||
allSpritesHaveSameCollisionMasksAs,
|
||||
copyAnimationsSpriteCollisionMasks,
|
||||
} from '../Utils/SpriteObjectHelper';
|
||||
import SpriteSelector from '../Utils/SpriteSelector';
|
||||
import every from 'lodash/every';
|
||||
const gd = global.gd;
|
||||
|
||||
export default class CollisionMasksEditor extends Component {
|
||||
state = {
|
||||
animationIndex: 0,
|
||||
directionIndex: 0,
|
||||
spriteIndex: 0,
|
||||
sameCollisionMasksForAnimations: true,
|
||||
sameCollisionMasksForSprites: true,
|
||||
spriteWidth: 0,
|
||||
spriteHeight: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this._updateSameCollisionMasksToggles();
|
||||
}
|
||||
|
||||
_updateCollisionMasks = () => {
|
||||
const { object } = this.props;
|
||||
const { animationIndex, directionIndex, spriteIndex } = this.state;
|
||||
const spriteObject = gd.asSpriteObject(object);
|
||||
|
||||
const { animation, sprite } = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex
|
||||
);
|
||||
|
||||
if (animation && sprite) {
|
||||
if (this.state.sameCollisionMasksForAnimations) {
|
||||
mapFor(0, spriteObject.getAnimationsCount(), i => {
|
||||
const otherAnimation = spriteObject.getAnimation(i);
|
||||
copyAnimationsSpriteCollisionMasks(sprite, otherAnimation);
|
||||
});
|
||||
} else if (this.state.sameCollisionMasksForSprites) {
|
||||
copyAnimationsSpriteCollisionMasks(sprite, animation);
|
||||
}
|
||||
}
|
||||
|
||||
this.forceUpdate(); // Refresh the preview
|
||||
if (this.props.onCollisionMasksUpdated)
|
||||
this.props.onCollisionMasksUpdated();
|
||||
};
|
||||
|
||||
chooseAnimation = index => {
|
||||
this.setState(
|
||||
{
|
||||
animationIndex: index,
|
||||
directionIndex: 0,
|
||||
spriteIndex: 0,
|
||||
},
|
||||
() => this._updateSameCollisionMasksToggles()
|
||||
);
|
||||
};
|
||||
|
||||
chooseDirection = index => {
|
||||
this.setState({
|
||||
directionIndex: index,
|
||||
spriteIndex: 0,
|
||||
});
|
||||
};
|
||||
|
||||
chooseSprite = index => {
|
||||
this.setState({
|
||||
spriteIndex: index,
|
||||
});
|
||||
};
|
||||
|
||||
_updateSameCollisionMasksToggles = () => {
|
||||
const { object } = this.props;
|
||||
const { animationIndex, directionIndex, spriteIndex } = this.state;
|
||||
const spriteObject = gd.asSpriteObject(object);
|
||||
|
||||
const { animation, sprite } = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex
|
||||
);
|
||||
if (!animation || !sprite) return;
|
||||
|
||||
this.setState({
|
||||
sameCollisionMasksForAnimations: every(
|
||||
mapFor(0, spriteObject.getAnimationsCount(), i => {
|
||||
const otherAnimation = spriteObject.getAnimation(i);
|
||||
return allSpritesHaveSameCollisionMasksAs(sprite, otherAnimation);
|
||||
})
|
||||
),
|
||||
sameCollisionMasksForSprites: allSpritesHaveSameCollisionMasksAs(
|
||||
sprite,
|
||||
animation
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
_onSetCollisionMaskAutomatic = (automatic: boolean = true) => {
|
||||
const { object } = this.props;
|
||||
const { animationIndex, directionIndex, spriteIndex } = this.state;
|
||||
const spriteObject = gd.asSpriteObject(object);
|
||||
|
||||
const { sprite } = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex
|
||||
);
|
||||
if (!sprite) return;
|
||||
|
||||
sprite.setCollisionMaskAutomatic(automatic);
|
||||
this._updateCollisionMasks();
|
||||
};
|
||||
|
||||
_setSameCollisionMasksForAllAnimations = enable => {
|
||||
if (enable) {
|
||||
// eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
"Having the same collision masks for all animations will erase and reset all the other animations collision masks. This can't be undone. Are you sure you want to share these collision masks amongst all the animations of the object?"
|
||||
);
|
||||
if (!answer) return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
sameCollisionMasksForAnimations: enable,
|
||||
sameCollisionMasksForSprites: enable
|
||||
? true
|
||||
: this.state.sameCollisionMasksForSprites,
|
||||
},
|
||||
() => {
|
||||
this._updateCollisionMasks();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_setSameCollisionMasksForAllSprites = enable => {
|
||||
if (enable) {
|
||||
// eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
"Having the same collision masks for all frames will erase and reset all the other frames collision masks. This can't be undone. Are you sure you want to share these collision masks amongst all the frames of the animation?"
|
||||
);
|
||||
if (!answer) return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
sameCollisionMasksForAnimations: enable
|
||||
? this.state.sameCollisionMasksForAnimations
|
||||
: false,
|
||||
sameCollisionMasksForSprites: enable,
|
||||
},
|
||||
() => {
|
||||
this._updateCollisionMasks();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
_setCurrentSpriteSize = (spriteWidth: number, spriteHeight: number) => {
|
||||
this.setState({
|
||||
spriteWidth,
|
||||
spriteHeight,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const { object, resourcesLoader, project } = this.props;
|
||||
const {
|
||||
sameCollisionMasksForAnimations,
|
||||
sameCollisionMasksForSprites,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex,
|
||||
spriteWidth,
|
||||
spriteHeight,
|
||||
} = this.state;
|
||||
const spriteObject = gd.asSpriteObject(object);
|
||||
|
||||
if (!object.getAnimationsCount()) return null;
|
||||
const { hasValidSprite, sprite } = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ImagePreview
|
||||
resourceName={hasValidSprite ? sprite.getImageName() : ''}
|
||||
resourcesLoader={resourcesLoader}
|
||||
project={project}
|
||||
onImageLoaded={this._setCurrentSpriteSize}
|
||||
>
|
||||
{hasValidSprite && (
|
||||
<CollisionMasksPreview
|
||||
isDefaultBoundingBox={sprite.isCollisionMaskAutomatic()}
|
||||
polygons={sprite.getCustomCollisionMask()}
|
||||
/>
|
||||
)}
|
||||
</ImagePreview>
|
||||
<Line>
|
||||
<Column expand>
|
||||
<SpriteSelector
|
||||
spriteObject={spriteObject}
|
||||
animationIndex={animationIndex}
|
||||
directionIndex={directionIndex}
|
||||
spriteIndex={spriteIndex}
|
||||
chooseAnimation={this.chooseAnimation}
|
||||
chooseDirection={this.chooseDirection}
|
||||
chooseSprite={this.chooseSprite}
|
||||
sameForAllAnimations={sameCollisionMasksForAnimations}
|
||||
sameForAllSprites={sameCollisionMasksForSprites}
|
||||
setSameForAllAnimations={
|
||||
this._setSameCollisionMasksForAllAnimations
|
||||
}
|
||||
setSameForAllSprites={this._setSameCollisionMasksForAllSprites}
|
||||
setSameForAllAnimationsLabel="Share same collision masks for all animations"
|
||||
setSameForAllSpritesLabel="Share same collision masks for all sprites of this animation"
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
{!!sprite &&
|
||||
!sprite.isCollisionMaskAutomatic() && (
|
||||
<React.Fragment>
|
||||
<PolygonsList
|
||||
polygons={sprite.getCustomCollisionMask()}
|
||||
onPolygonsUpdated={this._updateCollisionMasks}
|
||||
spriteWidth={spriteWidth}
|
||||
spriteHeight={spriteHeight}
|
||||
/>
|
||||
<Line justifyContent="center">
|
||||
<FlatButton
|
||||
label="Restore the default collision mask"
|
||||
primary={false}
|
||||
onClick={() => this._onSetCollisionMaskAutomatic(true)}
|
||||
/>
|
||||
</Line>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{!!sprite &&
|
||||
sprite.isCollisionMaskAutomatic() && (
|
||||
<React.Fragment>
|
||||
<EmptyMessage>
|
||||
This sprite uses the default collision mask, a rectangle that is
|
||||
as large as the sprite.
|
||||
</EmptyMessage>
|
||||
<Line justifyContent="center">
|
||||
<FlatButton
|
||||
label="Use a custom collision mask"
|
||||
primary={false}
|
||||
onClick={() => this._onSetCollisionMaskAutomatic(false)}
|
||||
/>
|
||||
</Line>
|
||||
</React.Fragment>
|
||||
)}
|
||||
{!sprite && (
|
||||
<EmptyMessage>
|
||||
Choose an animation and frame to edit the collision masks
|
||||
</EmptyMessage>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
//TODO: Factor with styles.js from LayersList.
|
||||
export default {
|
||||
handleColumn: {
|
||||
width: 24,
|
||||
paddingLeft: 8,
|
||||
paddingRight: 0,
|
||||
},
|
||||
coordinateColumn: {
|
||||
width: 48,
|
||||
},
|
||||
toolColumn: {
|
||||
width: 48,
|
||||
},
|
||||
};
|
@@ -4,7 +4,7 @@ import Add from 'material-ui/svg-icons/content/add';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import styles from './styles';
|
||||
|
||||
const AddLayerRow = ({ onAdd }) => (
|
||||
const AddPointRow = ({ onAdd }) => (
|
||||
<TableRow>
|
||||
<TableRowColumn style={styles.handleColumn} />
|
||||
<TableRowColumn />
|
||||
@@ -18,4 +18,4 @@ const AddLayerRow = ({ onAdd }) => (
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
export default AddLayerRow;
|
||||
export default AddPointRow;
|
||||
|
@@ -19,12 +19,9 @@ const SortableAddPointRow = SortableElement(AddPointRow);
|
||||
const SortablePointRow = SortableElement(PointRow);
|
||||
|
||||
class PointsListBody extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
nameErrors: {},
|
||||
};
|
||||
}
|
||||
state = {
|
||||
nameErrors: {},
|
||||
};
|
||||
|
||||
_onPointsUpdated() {
|
||||
this.forceUpdate();
|
||||
|
@@ -1,7 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
import Toggle from 'material-ui/Toggle';
|
||||
import EmptyMessage from '../../../../UI/EmptyMessage';
|
||||
import { Line, Column } from '../../../../UI/Grid';
|
||||
import { mapFor } from '../../../../Utils/MapFor';
|
||||
@@ -13,6 +10,7 @@ import {
|
||||
allSpritesHaveSamePointsAs,
|
||||
copyAnimationsSpritePoints,
|
||||
} from '../Utils/SpriteObjectHelper';
|
||||
import SpriteSelector from '../Utils/SpriteSelector';
|
||||
import every from 'lodash/every';
|
||||
const gd = global.gd;
|
||||
|
||||
@@ -104,7 +102,7 @@ export default class PointsEditor extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
_onToggleSamePointsForAnimation = enable => {
|
||||
_setSamePointsForAllAnimations = enable => {
|
||||
if (enable) {
|
||||
// eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
@@ -124,7 +122,7 @@ export default class PointsEditor extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
_onToggleSamePointsForSprites = enable => {
|
||||
_setSamePointsForAllSprites = enable => {
|
||||
if (enable) {
|
||||
// eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
@@ -158,14 +156,7 @@ export default class PointsEditor extends Component {
|
||||
const spriteObject = gd.asSpriteObject(object);
|
||||
|
||||
if (!object.getAnimationsCount()) return null;
|
||||
const {
|
||||
hasValidAnimation,
|
||||
animation,
|
||||
hasValidDirection,
|
||||
direction,
|
||||
hasValidSprite,
|
||||
sprite,
|
||||
} = getCurrentElements(
|
||||
const { hasValidSprite, sprite } = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
@@ -183,76 +174,20 @@ export default class PointsEditor extends Component {
|
||||
</ImagePreview>
|
||||
<Line>
|
||||
<Column expand>
|
||||
<Toggle
|
||||
label="Share same points for all animations"
|
||||
labelPosition="right"
|
||||
toggled={samePointsForAnimations}
|
||||
onToggle={(e, checked) =>
|
||||
this._onToggleSamePointsForAnimation(checked)}
|
||||
/>
|
||||
<Line>
|
||||
{!samePointsForAnimations && (
|
||||
<SelectField
|
||||
floatingLabelText="Animation"
|
||||
value={this.state.animationIndex}
|
||||
onChange={(e, i, value) => this.chooseAnimation(value)}
|
||||
>
|
||||
{mapFor(0, spriteObject.getAnimationsCount(), i => {
|
||||
const animation = spriteObject.getAnimation(i);
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
value={i}
|
||||
primaryText={`Animation #${i} ${animation.getName()}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
)}
|
||||
{!samePointsForAnimations &&
|
||||
hasValidAnimation &&
|
||||
animation.getDirectionsCount() > 1 && (
|
||||
<SelectField
|
||||
floatingLabelText="Direction"
|
||||
value={this.state.directionIndex}
|
||||
onChange={(e, i, value) => this.chooseDirection(value)}
|
||||
>
|
||||
{mapFor(0, animation.getDirectionsCount(), i => {
|
||||
return (
|
||||
<MenuItem
|
||||
value={i}
|
||||
key={i}
|
||||
primaryText={`Direction #${i}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
)}
|
||||
{!samePointsForSprites &&
|
||||
hasValidDirection && (
|
||||
<SelectField
|
||||
floatingLabelText="Frame"
|
||||
value={this.state.spriteIndex}
|
||||
onChange={(e, i, value) => this.chooseSprite(value)}
|
||||
>
|
||||
{mapFor(0, direction.getSpritesCount(), i => {
|
||||
return (
|
||||
<MenuItem
|
||||
value={i}
|
||||
key={i}
|
||||
primaryText={`Frame #${i}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
)}
|
||||
</Line>
|
||||
<Toggle
|
||||
label="Share same points for all sprites of the animation"
|
||||
labelPosition="right"
|
||||
toggled={samePointsForSprites}
|
||||
onToggle={(e, checked) =>
|
||||
this._onToggleSamePointsForSprites(checked)}
|
||||
<SpriteSelector
|
||||
spriteObject={spriteObject}
|
||||
animationIndex={animationIndex}
|
||||
directionIndex={directionIndex}
|
||||
spriteIndex={spriteIndex}
|
||||
chooseAnimation={this.chooseAnimation}
|
||||
chooseDirection={this.chooseDirection}
|
||||
chooseSprite={this.chooseSprite}
|
||||
sameForAllAnimations={samePointsForAnimations}
|
||||
sameForAllSprites={samePointsForSprites}
|
||||
setSameForAllAnimations={this._setSamePointsForAllAnimations}
|
||||
setSameForAllSprites={this._setSamePointsForAllSprites}
|
||||
setSameForAllAnimationsLabel="Share same points for all animations"
|
||||
setSameForAllSpritesLabel="Share same points for all sprites of this animation"
|
||||
/>
|
||||
</Column>
|
||||
</Line>
|
||||
|
@@ -118,6 +118,85 @@ export const allSpritesHaveSamePointsAs = (originalSprite, animation) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const copySpritePolygons = (originalSprite, destinationSprite) => {
|
||||
if (originalSprite.ptr === destinationSprite.ptr) return;
|
||||
|
||||
destinationSprite.setCollisionMaskAutomatic(
|
||||
originalSprite.isCollisionMaskAutomatic()
|
||||
);
|
||||
|
||||
destinationSprite.getCustomCollisionMask().clear();
|
||||
mapVector(originalSprite.getCustomCollisionMask(), originalPolygon => {
|
||||
destinationSprite.getCustomCollisionMask().push_back(originalPolygon);
|
||||
});
|
||||
};
|
||||
|
||||
export const copyAnimationsSpriteCollisionMasks = (originalSprite, animation) => {
|
||||
mapFor(0, animation.getDirectionsCount(), i => {
|
||||
const direction = animation.getDirection(i);
|
||||
|
||||
mapFor(0, direction.getSpritesCount(), j => {
|
||||
const sprite = direction.getSprite(j);
|
||||
copySpritePolygons(originalSprite, sprite);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const isSamePolygon = (polygon1, polygon2) => {
|
||||
const polygon1Vertices = polygon1.getVertices();
|
||||
const polygon2Vertices = polygon2.getVertices();
|
||||
|
||||
if (polygon1Vertices.size() !== polygon2Vertices.size()) return false;
|
||||
|
||||
return every(
|
||||
mapVector(polygon1Vertices, (point1, index) => {
|
||||
const point2 = polygon2Vertices.at(index);
|
||||
return point1.get_x() === point2.get_x() && point1.get_y() === point2.get_y();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const haveSameCollisionMasks = (sprite1, sprite2) => {
|
||||
if (sprite1.isCollisionMaskAutomatic() !== sprite2.isCollisionMaskAutomatic())
|
||||
return false;
|
||||
|
||||
if (
|
||||
sprite1.isCollisionMaskAutomatic() &&
|
||||
sprite2.isCollisionMaskAutomatic()
|
||||
)
|
||||
return true;
|
||||
|
||||
const sprite1CollisionMask = sprite1.getCustomCollisionMask();
|
||||
const sprite2CollisionMask = sprite2.getCustomCollisionMask();
|
||||
|
||||
if (sprite1CollisionMask.size() !== sprite2CollisionMask.size()) return false;
|
||||
|
||||
return every(
|
||||
mapVector(sprite1CollisionMask, (sprite1Polygon, index) => {
|
||||
return isSamePolygon(sprite1Polygon, sprite2CollisionMask.at(index));
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const allSpritesHaveSameCollisionMasksAs = (
|
||||
originalSprite,
|
||||
animation
|
||||
) => {
|
||||
return every(
|
||||
mapFor(0, animation.getDirectionsCount(), i => {
|
||||
const direction = animation.getDirection(i);
|
||||
|
||||
return every(
|
||||
mapFor(0, direction.getSpritesCount(), j => {
|
||||
const sprite = direction.getSprite(j);
|
||||
|
||||
return haveSameCollisionMasks(sprite, originalSprite);
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteSpritesFromAnimation = (animation, spritePtrs) => {
|
||||
mapFor(0, animation.getDirectionsCount(), i => {
|
||||
const direction = animation.getDirection(i);
|
||||
|
@@ -3,121 +3,179 @@ import {
|
||||
allSpritesHaveSamePointsAs,
|
||||
copyAnimationsSpritePoints,
|
||||
deleteSpritesFromAnimation,
|
||||
haveSameCollisionMasks,
|
||||
} from './SpriteObjectHelper';
|
||||
const gd = global.gd;
|
||||
|
||||
describe('History', () => {
|
||||
it('can tell if two sprite have the exact same points', () => {
|
||||
const sprite1 = new gd.Sprite();
|
||||
const sprite2 = new gd.Sprite();
|
||||
describe('SpriteObjectHelper', () => {
|
||||
describe('Points related methods', () => {
|
||||
it('can tell if two sprite have the exact same points', () => {
|
||||
const sprite1 = new gd.Sprite();
|
||||
const sprite2 = new gd.Sprite();
|
||||
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
sprite1.getOrigin().setX(40);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.getOrigin().setX(40);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
sprite1.getOrigin().setX(40);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.getOrigin().setX(40);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
|
||||
sprite1.setDefaultCenterPoint(false);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.setDefaultCenterPoint(false);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
sprite1.setDefaultCenterPoint(false);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.setDefaultCenterPoint(false);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
|
||||
const customPoint1 = new gd.Point('CustomPoint');
|
||||
sprite1.addPoint(customPoint1);
|
||||
customPoint1.delete();
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
const customPoint2 = new gd.Point('CustomPoint');
|
||||
sprite2.addPoint(customPoint2);
|
||||
customPoint2.delete();
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
const customPoint1 = new gd.Point('CustomPoint');
|
||||
sprite1.addPoint(customPoint1);
|
||||
customPoint1.delete();
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
const customPoint2 = new gd.Point('CustomPoint');
|
||||
sprite2.addPoint(customPoint2);
|
||||
customPoint2.delete();
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
|
||||
sprite1.getPoint('CustomPoint').setY(10);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.getPoint('CustomPoint').setY(10);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
});
|
||||
sprite1.getPoint('CustomPoint').setY(10);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(false);
|
||||
sprite2.getPoint('CustomPoint').setY(10);
|
||||
expect(haveSamePoints(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSamePoints(sprite2, sprite1)).toBe(true);
|
||||
});
|
||||
|
||||
it('can tell if all sprites of animations have the exact same points', () => {
|
||||
const originalSprite = new gd.Sprite();
|
||||
it('can tell if all sprites of animations have the exact same points', () => {
|
||||
const originalSprite = new gd.Sprite();
|
||||
|
||||
const animation1 = new gd.Animation();
|
||||
animation1.setDirectionsCount(1);
|
||||
const sprite1 = new gd.Sprite();
|
||||
const sprite2 = new gd.Sprite();
|
||||
const animation1 = new gd.Animation();
|
||||
animation1.setDirectionsCount(1);
|
||||
const sprite1 = new gd.Sprite();
|
||||
const sprite2 = new gd.Sprite();
|
||||
|
||||
animation1.getDirection(0).addSprite(sprite1);
|
||||
animation1.getDirection(0).addSprite(sprite2);
|
||||
animation1.getDirection(0).addSprite(sprite1);
|
||||
animation1.getDirection(0).addSprite(sprite2);
|
||||
|
||||
const animation2 = new gd.Animation();
|
||||
animation2.setDirectionsCount(1);
|
||||
const sprite3 = new gd.Sprite();
|
||||
const sprite4 = new gd.Sprite();
|
||||
sprite4.setDefaultCenterPoint(false);
|
||||
sprite4.getCenter().setY(5);
|
||||
const animation2 = new gd.Animation();
|
||||
animation2.setDirectionsCount(1);
|
||||
const sprite3 = new gd.Sprite();
|
||||
const sprite4 = new gd.Sprite();
|
||||
sprite4.setDefaultCenterPoint(false);
|
||||
sprite4.getCenter().setY(5);
|
||||
|
||||
animation2.getDirection(0).addSprite(sprite3);
|
||||
animation2.getDirection(0).addSprite(sprite4);
|
||||
animation2.getDirection(0).addSprite(sprite3);
|
||||
animation2.getDirection(0).addSprite(sprite4);
|
||||
|
||||
expect(allSpritesHaveSamePointsAs(originalSprite, animation1)).toBe(true);
|
||||
expect(allSpritesHaveSamePointsAs(originalSprite, animation2)).toBe(false);
|
||||
});
|
||||
expect(allSpritesHaveSamePointsAs(originalSprite, animation1)).toBe(true);
|
||||
expect(allSpritesHaveSamePointsAs(originalSprite, animation2)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('can copy points of a sprite in all sprites of an animation', () => {
|
||||
const animation1 = new gd.Animation();
|
||||
animation1.setDirectionsCount(1);
|
||||
const emptySprite = new gd.Sprite();
|
||||
const spriteWithCustomPoints = new gd.Sprite();
|
||||
it('can copy points of a sprite in all sprites of an animation', () => {
|
||||
const animation1 = new gd.Animation();
|
||||
animation1.setDirectionsCount(1);
|
||||
const emptySprite = new gd.Sprite();
|
||||
const spriteWithCustomPoints = new gd.Sprite();
|
||||
|
||||
const point = new gd.Point('CustomPoint');
|
||||
spriteWithCustomPoints.addPoint(point);
|
||||
point.delete();
|
||||
spriteWithCustomPoints.setDefaultCenterPoint(false);
|
||||
spriteWithCustomPoints.getCenter().setY(5);
|
||||
spriteWithCustomPoints.getPoint('CustomPoint').setX(1);
|
||||
spriteWithCustomPoints.getPoint('CustomPoint').setY(2);
|
||||
const point = new gd.Point('CustomPoint');
|
||||
spriteWithCustomPoints.addPoint(point);
|
||||
point.delete();
|
||||
spriteWithCustomPoints.setDefaultCenterPoint(false);
|
||||
spriteWithCustomPoints.getCenter().setY(5);
|
||||
spriteWithCustomPoints.getPoint('CustomPoint').setX(1);
|
||||
spriteWithCustomPoints.getPoint('CustomPoint').setY(2);
|
||||
|
||||
animation1.getDirection(0).addSprite(emptySprite);
|
||||
animation1.getDirection(0).addSprite(spriteWithCustomPoints);
|
||||
animation1.getDirection(0).addSprite(emptySprite);
|
||||
animation1.getDirection(0).addSprite(emptySprite);
|
||||
animation1.getDirection(0).addSprite(spriteWithCustomPoints);
|
||||
animation1.getDirection(0).addSprite(emptySprite);
|
||||
|
||||
const animation2 = new gd.Animation();
|
||||
animation2.getDirection(0).addSprite(emptySprite);
|
||||
copyAnimationsSpritePoints(spriteWithCustomPoints, animation2);
|
||||
expect(allSpritesHaveSamePointsAs(spriteWithCustomPoints, animation2)).toBe(
|
||||
true
|
||||
);
|
||||
const animation2 = new gd.Animation();
|
||||
animation2.getDirection(0).addSprite(emptySprite);
|
||||
copyAnimationsSpritePoints(spriteWithCustomPoints, animation2);
|
||||
expect(
|
||||
allSpritesHaveSamePointsAs(spriteWithCustomPoints, animation2)
|
||||
).toBe(true);
|
||||
|
||||
copyAnimationsSpritePoints(
|
||||
animation1.getDirection(0).getSprite(1),
|
||||
animation1
|
||||
);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
animation1.getDirection(0).getSprite(0),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
copyAnimationsSpritePoints(
|
||||
animation1.getDirection(0).getSprite(1),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
animation1.getDirection(0).getSprite(2),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
animation1
|
||||
);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
animation1.getDirection(0).getSprite(0),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
animation1.getDirection(0).getSprite(1),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
haveSamePoints(
|
||||
animation1.getDirection(0).getSprite(2),
|
||||
spriteWithCustomPoints
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Collision masks related methods', () => {
|
||||
it('can tell if two sprite have the exact same collision masks', () => {
|
||||
const addVertice = (polygon, x, y) => {
|
||||
const vertice = new gd.Vector2f();
|
||||
vertice.x = x;
|
||||
vertice.y = y;
|
||||
|
||||
polygon.getVertices().push_back(vertice);
|
||||
vertice.delete();
|
||||
};
|
||||
|
||||
const sprite1 = new gd.Sprite();
|
||||
const sprite2 = new gd.Sprite();
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(true);
|
||||
|
||||
sprite1.setCollisionMaskAutomatic(false);
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(false);
|
||||
|
||||
{
|
||||
const polygon1 = new gd.Polygon2d();
|
||||
addVertice(polygon1, 0, 0);
|
||||
addVertice(polygon1, 0, 10);
|
||||
addVertice(polygon1, 10, 0);
|
||||
sprite1.getCustomCollisionMask().push_back(polygon1);
|
||||
polygon1.delete();
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
sprite2.setCollisionMaskAutomatic(false);
|
||||
const polygon2 = new gd.Polygon2d();
|
||||
addVertice(polygon2, 0, 0);
|
||||
addVertice(polygon2, 0, 10);
|
||||
addVertice(polygon2, 10, 0);
|
||||
sprite2.getCustomCollisionMask().push_back(polygon2);
|
||||
polygon2.delete();
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(true);
|
||||
|
||||
sprite2.getCustomCollisionMask().at(0).getVertices().at(1).set_x(-20);
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(false);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(false);
|
||||
|
||||
sprite1.getCustomCollisionMask().at(0).getVertices().at(1).set_x(-20);
|
||||
expect(haveSameCollisionMasks(sprite1, sprite2)).toBe(true);
|
||||
expect(haveSameCollisionMasks(sprite2, sprite1)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('can remove sprites using the sprites pointers', () => {
|
||||
|
@@ -0,0 +1,134 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import SelectField from 'material-ui/SelectField';
|
||||
import MenuItem from 'material-ui/MenuItem';
|
||||
|
||||
import Toggle from 'material-ui/Toggle';
|
||||
import { Line } from '../../../../UI/Grid';
|
||||
import { mapFor } from '../../../../Utils/MapFor';
|
||||
import { getCurrentElements } from './SpriteObjectHelper';
|
||||
|
||||
type Props = {|
|
||||
spriteObject: gdSpriteObject,
|
||||
|
||||
animationIndex: number,
|
||||
directionIndex: number,
|
||||
spriteIndex: number,
|
||||
|
||||
chooseAnimation: number => void,
|
||||
chooseDirection: number => void,
|
||||
chooseSprite: number => void,
|
||||
|
||||
sameForAllAnimations: boolean,
|
||||
sameForAllSprites: boolean,
|
||||
|
||||
setSameForAllAnimations: boolean => void,
|
||||
setSameForAllSprites: number => void,
|
||||
|
||||
setSameForAllAnimationsLabel: string,
|
||||
setSameForAllSpritesLabel: string,
|
||||
|};
|
||||
|
||||
/**
|
||||
* A component that displays selector to browse the animations/directions/sprite
|
||||
* of a Sprite object. Also have toggles so that the user can choose if the edited property
|
||||
* (typically, the points or the collision masks of the sprite) should be shared between
|
||||
* all sprites of an animation, or between all sprites of all animations of the object.
|
||||
*/
|
||||
export default class SpriteSelector extends React.Component<Props, void> {
|
||||
render() {
|
||||
const {
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex,
|
||||
sameForAllAnimations,
|
||||
sameForAllSprites,
|
||||
chooseAnimation,
|
||||
chooseDirection,
|
||||
chooseSprite,
|
||||
setSameForAllAnimations,
|
||||
setSameForAllSprites,
|
||||
setSameForAllAnimationsLabel,
|
||||
setSameForAllSpritesLabel,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
hasValidAnimation,
|
||||
animation,
|
||||
hasValidDirection,
|
||||
direction,
|
||||
} = getCurrentElements(
|
||||
spriteObject,
|
||||
animationIndex,
|
||||
directionIndex,
|
||||
spriteIndex
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Line>
|
||||
<SelectField
|
||||
floatingLabelText="Animation"
|
||||
value={this.props.animationIndex}
|
||||
onChange={(e, i, value) => chooseAnimation(value)}
|
||||
>
|
||||
{mapFor(0, spriteObject.getAnimationsCount(), i => {
|
||||
const animation = spriteObject.getAnimation(i);
|
||||
return (
|
||||
<MenuItem
|
||||
key={i}
|
||||
value={i}
|
||||
primaryText={`Animation #${i} ${animation.getName()}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
{hasValidAnimation &&
|
||||
animation.getDirectionsCount() > 1 && (
|
||||
<SelectField
|
||||
floatingLabelText="Direction"
|
||||
value={this.props.directionIndex}
|
||||
onChange={(e, i, value) => chooseDirection(value)}
|
||||
>
|
||||
{mapFor(0, animation.getDirectionsCount(), i => {
|
||||
return (
|
||||
<MenuItem
|
||||
value={i}
|
||||
key={i}
|
||||
primaryText={`Direction #${i}`}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
)}
|
||||
{hasValidDirection && (
|
||||
<SelectField
|
||||
floatingLabelText="Frame"
|
||||
value={this.props.spriteIndex}
|
||||
onChange={(e, i, value) => chooseSprite(value)}
|
||||
>
|
||||
{mapFor(0, direction.getSpritesCount(), i => {
|
||||
return (
|
||||
<MenuItem value={i} key={i} primaryText={`Frame #${i}`} />
|
||||
);
|
||||
})}
|
||||
</SelectField>
|
||||
)}
|
||||
</Line>
|
||||
<Toggle
|
||||
label={setSameForAllAnimationsLabel}
|
||||
labelPosition="right"
|
||||
toggled={sameForAllAnimations}
|
||||
onToggle={(e, checked) => setSameForAllAnimations(checked)}
|
||||
/>
|
||||
<Toggle
|
||||
label={setSameForAllSpritesLabel}
|
||||
labelPosition="right"
|
||||
toggled={sameForAllSprites}
|
||||
onToggle={(e, checked) => setSameForAllSprites(checked)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ import ContextMenu from '../../../UI/Menu/ContextMenu';
|
||||
import { showWarningBox } from '../../../UI/Messages/MessageBox';
|
||||
import ResourcesLoader from '../../../ResourcesLoader';
|
||||
import PointsEditor from './PointsEditor';
|
||||
import CollisionMasksEditor from './CollisionMasksEditor';
|
||||
import { deleteSpritesFromAnimation } from './Utils/SpriteObjectHelper';
|
||||
|
||||
const gd = global.gd;
|
||||
@@ -283,6 +284,7 @@ class AnimationsListContainer extends Component {
|
||||
export default class SpriteEditor extends Component {
|
||||
state = {
|
||||
pointsEditorOpen: false,
|
||||
collisionMasksEditorOpen: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -297,10 +299,10 @@ export default class SpriteEditor extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
openHitboxesEditor = (open = true) => {
|
||||
alert(
|
||||
"Hitboxes editor is not ready yet! We're working on it and it will be available soon."
|
||||
);
|
||||
openCollisionMasksEditor = (open = true) => {
|
||||
this.setState({
|
||||
collisionMasksEditorOpen: open,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -327,7 +329,7 @@ export default class SpriteEditor extends Component {
|
||||
<RaisedButton
|
||||
label="Edit hitboxes"
|
||||
primary={false}
|
||||
onClick={() => this.openHitboxesEditor(true)}
|
||||
onClick={() => this.openCollisionMasksEditor(true)}
|
||||
disabled={spriteObject.getAnimationsCount() === 0}
|
||||
/>
|
||||
<RaisedButton
|
||||
@@ -359,7 +361,31 @@ export default class SpriteEditor extends Component {
|
||||
resourcesLoader={this.resourcesLoader}
|
||||
project={project}
|
||||
onPointsUpdated={() =>
|
||||
this.forceUpdate() /*Force update to ensure dialog is properly positionned*/}
|
||||
this.forceUpdate() /*Force update to ensure dialog is properly positioned*/}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
{this.state.collisionMasksEditorOpen && (
|
||||
<Dialog
|
||||
actions={
|
||||
<FlatButton
|
||||
label="Close"
|
||||
primary
|
||||
onClick={() => this.openCollisionMasksEditor(false)}
|
||||
/>
|
||||
}
|
||||
autoScrollBodyContent
|
||||
noMargin
|
||||
modal
|
||||
onRequestClose={() => this.openCollisionMasksEditor(false)}
|
||||
open={this.state.collisionMasksEditorOpen}
|
||||
>
|
||||
<CollisionMasksEditor
|
||||
object={spriteObject}
|
||||
resourcesLoader={this.resourcesLoader}
|
||||
project={project}
|
||||
onCollisionMasksUpdated={() =>
|
||||
this.forceUpdate() /*Force update to ensure dialog is properly positioned*/}
|
||||
/>
|
||||
</Dialog>
|
||||
)}
|
||||
|
@@ -37,6 +37,7 @@ type Props = {|
|
||||
resourcesLoader: typeof ResourcesLoader,
|
||||
children?: any,
|
||||
style?: Object,
|
||||
onImageLoaded?: (number, number) => void,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
@@ -71,8 +72,7 @@ export default class ImagePreview extends React.Component<Props, State> {
|
||||
errored: false,
|
||||
imageWidth: null,
|
||||
imageHeight: null,
|
||||
imageSource:
|
||||
resourcesLoader.getResourceFullUrl(project, resourceName),
|
||||
imageSource: resourcesLoader.getResourceFullUrl(project, resourceName),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,10 +92,14 @@ export default class ImagePreview extends React.Component<Props, State> {
|
||||
_handleImageLoaded = (e: any) => {
|
||||
const imgElement = e.target;
|
||||
|
||||
const imageWidth = imgElement ? imgElement.clientWidth : 0;
|
||||
const imageHeight = imgElement ? imgElement.clientHeight : 0;
|
||||
this.setState({
|
||||
imageWidth: imgElement ? imgElement.clientWidth : 0,
|
||||
imageHeight: imgElement ? imgElement.clientHeight : 0,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
});
|
||||
if (this.props.onImageLoaded)
|
||||
this.props.onImageLoaded(imageWidth, imageHeight);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@@ -47,6 +47,11 @@ class ThemableObjectRow extends React.Component {
|
||||
enabled: !!this.props.onEdit,
|
||||
click: () => this.props.onEditName(),
|
||||
},
|
||||
{
|
||||
label: 'Set as a global object',
|
||||
enabled: !!this.props.onSetAsGlobalObject,
|
||||
click: () => this.props.onSetAsGlobalObject(),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
enabled: !!this.props.onEdit,
|
||||
|
@@ -69,6 +69,7 @@ export default class ObjectSelector extends Component {
|
||||
layout,
|
||||
allowedObjectType,
|
||||
noGroups,
|
||||
onBlur,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@@ -97,6 +98,8 @@ export default class ObjectSelector extends Component {
|
||||
focused: false,
|
||||
text: null,
|
||||
});
|
||||
|
||||
if (onBlur) onBlur(event);
|
||||
}}
|
||||
onNewRequest={data => {
|
||||
// Note that data may be a string or a {text, value} object.
|
||||
|
@@ -111,6 +111,11 @@ class ObjectsList extends Component<*, *> {
|
||||
onPaste={() => this.props.onPaste(objectWithContext)}
|
||||
onRename={newName =>
|
||||
this.props.onRename(objectWithContext, newName)}
|
||||
onSetAsGlobalObject={
|
||||
objectWithContext.global
|
||||
? undefined
|
||||
: () => this.props.onSetAsGlobalObject(objectWithContext)
|
||||
}
|
||||
onAddNewObject={this.props.onAddNewObject}
|
||||
editingName={nameBeingEdited}
|
||||
getThumbnail={this.props.getThumbnail}
|
||||
@@ -261,9 +266,10 @@ export default class ObjectsListContainer extends React.Component<
|
||||
const { project, objectsContainer, onObjectPasted } = this.props;
|
||||
|
||||
const newName = newNameGenerator(
|
||||
'CopyOf' + name,
|
||||
name,
|
||||
name =>
|
||||
objectsContainer.hasObjectNamed(name) || project.hasObjectNamed(name)
|
||||
objectsContainer.hasObjectNamed(name) || project.hasObjectNamed(name),
|
||||
'CopyOf'
|
||||
);
|
||||
|
||||
const newObject = global
|
||||
@@ -355,6 +361,35 @@ export default class ObjectsListContainer extends React.Component<
|
||||
this.forceUpdateList();
|
||||
};
|
||||
|
||||
_setAsGlobalObject = (objectWithContext: ObjectWithContext) => {
|
||||
const { object } = objectWithContext;
|
||||
const { project, objectsContainer } = this.props;
|
||||
|
||||
const objectName = object.getName();
|
||||
if (!objectsContainer.hasObjectNamed(objectName)) return;
|
||||
|
||||
if (project.hasObjectNamed(objectName)) {
|
||||
showWarningBox(
|
||||
'A global object with this name already exists. Please change the object name before setting it as a global object'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//eslint-disable-next-line
|
||||
const answer = confirm(
|
||||
"This object will be loaded and available in all the scenes. This is only recommended for objects that you reuse a lot and can't be undone. Make this object global?"
|
||||
);
|
||||
if (!answer) return;
|
||||
|
||||
project.insertObject(
|
||||
objectsContainer.getObject(objectName),
|
||||
project.getObjectsCount()
|
||||
);
|
||||
objectsContainer.removeObject(objectName);
|
||||
|
||||
this.forceUpdateList();
|
||||
};
|
||||
|
||||
forceUpdateList = () => {
|
||||
this.forceUpdate();
|
||||
this.sortableList.getWrappedInstance().forceUpdateGrid();
|
||||
@@ -403,6 +438,7 @@ export default class ObjectsListContainer extends React.Component<
|
||||
onEditObject={this.props.onEditObject}
|
||||
onCopyObject={this._copyObject}
|
||||
onCutObject={this._cutObject}
|
||||
onSetAsGlobalObject={this._setAsGlobalObject}
|
||||
onPaste={this._paste}
|
||||
onAddNewObject={() =>
|
||||
this.setState({ newObjectDialogOpen: true })}
|
||||
|
@@ -22,6 +22,7 @@ export default class SetupGridDialog extends Component {
|
||||
const actions = [
|
||||
<FlatButton
|
||||
label="Cancel"
|
||||
primary={false}
|
||||
onClick={this.props.onCancel}
|
||||
/>,
|
||||
<FlatButton
|
||||
|
@@ -90,11 +90,10 @@
|
||||
color: #E8DC59;
|
||||
}
|
||||
|
||||
.gd-events-sheet-dark-theme
|
||||
.instruction-parameter.object,
|
||||
.instruction-parameter.objectPtr,
|
||||
.instruction-parameter.objectList,
|
||||
.instruction-parameter.objectListWithoutPicking {
|
||||
.gd-events-sheet-dark-theme .instruction-parameter.object,
|
||||
.gd-events-sheet-dark-theme .instruction-parameter.objectPtr,
|
||||
.gd-events-sheet-dark-theme .instruction-parameter.objectList,
|
||||
.gd-events-sheet-dark-theme .instruction-parameter.objectListWithoutPicking {
|
||||
color: #B77CFF;
|
||||
font-style: italic;
|
||||
}
|
||||
|
@@ -95,9 +95,6 @@ const theme: Theme = {
|
||||
logo: {
|
||||
src: 'res/GD-logo.png',
|
||||
},
|
||||
startPage: {
|
||||
backgroundColor,
|
||||
},
|
||||
mosaicRootClassName: 'mosaic-gd-dark-theme',
|
||||
eventsSheetRootClassName: 'gd-events-sheet-dark-theme',
|
||||
};
|
||||
|
@@ -83,11 +83,10 @@
|
||||
color: rgb(27, 143, 1);
|
||||
}
|
||||
|
||||
.gd-events-sheet-default-theme
|
||||
.instruction-parameter.object,
|
||||
.instruction-parameter.objectPtr,
|
||||
.instruction-parameter.objectList,
|
||||
.instruction-parameter.objectListWithoutPicking {
|
||||
.gd-events-sheet-default-theme .instruction-parameter.object,
|
||||
.gd-events-sheet-default-theme .instruction-parameter.objectPtr,
|
||||
.gd-events-sheet-default-theme .instruction-parameter.objectList,
|
||||
.gd-events-sheet-default-theme .instruction-parameter.objectListWithoutPicking {
|
||||
color: #3c4698;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@@ -80,9 +80,6 @@ const theme = {
|
||||
logo: {
|
||||
src: 'res/GD-logo-big.png',
|
||||
},
|
||||
startPage: {
|
||||
backgroundColor,
|
||||
},
|
||||
mosaicRootClassName: 'mosaic-gd-default-theme',
|
||||
eventsSheetRootClassName: 'gd-events-sheet-default-theme',
|
||||
};
|
||||
|
@@ -79,7 +79,6 @@ export default class Authentification {
|
||||
getUserProfile = (cb: (any, ?Profile) => void) => {
|
||||
if (!this.isAuthenticated()) return cb({ unauthenticated: true });
|
||||
|
||||
console.log('User found', this.user);
|
||||
cb(null, this.user);
|
||||
};
|
||||
|
||||
|
43
newIDE/app/src/Utils/SelectionHandler.js
Normal file
43
newIDE/app/src/Utils/SelectionHandler.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// @flow
|
||||
/**
|
||||
* Export functions to manipulate a selection of objects.
|
||||
*/
|
||||
|
||||
import values from 'lodash/values';
|
||||
|
||||
type ObjectType = { ptr: number };
|
||||
|
||||
type SelectionState<T> = {
|
||||
[number]: ?T,
|
||||
};
|
||||
|
||||
export const getInitialSelection = () => ({});
|
||||
|
||||
export const clearSelection = () => getInitialSelection();
|
||||
|
||||
export const getSelection = <T: ObjectType>(
|
||||
selection: SelectionState<T>
|
||||
): Array<T> => values(selection).filter(value => !!value);
|
||||
|
||||
export const addToSelection = <T: ObjectType>(
|
||||
selection: SelectionState<T>,
|
||||
object: T,
|
||||
select: boolean = true
|
||||
): SelectionState<T> => {
|
||||
console.log(object, select);
|
||||
return {
|
||||
...selection,
|
||||
[object.ptr]: select ? object : null,
|
||||
};
|
||||
};
|
||||
|
||||
export const isSelected = <T: ObjectType>(
|
||||
selection: SelectionState<T>,
|
||||
object: T
|
||||
): boolean => !!selection[object.ptr];
|
||||
|
||||
export const hasSelection = <T: ObjectType>(
|
||||
selection: SelectionState<T>
|
||||
): boolean => {
|
||||
return !!values(selection).filter(value => !!value).length;
|
||||
};
|
1
newIDE/app/src/VariablesList/ClipboardKind.js
Normal file
1
newIDE/app/src/VariablesList/ClipboardKind.js
Normal file
@@ -0,0 +1 @@
|
||||
export const CLIPBOARD_KIND = 'Variables';
|
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { TreeTableRow, TreeTableCell } from '../UI/TreeTable';
|
||||
import DragHandle from '../UI/DragHandle';
|
||||
import SemiControlledTextField from '../UI/SemiControlledTextField';
|
||||
import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import Checkbox from 'material-ui/Checkbox';
|
||||
import AddCircle from 'material-ui/svg-icons/content/add-circle';
|
||||
import SubdirectoryArrowRight from 'material-ui/svg-icons/navigation/subdirectory-arrow-right';
|
||||
import TextField from 'material-ui/TextField';
|
||||
@@ -16,6 +17,25 @@ const Indent = ({ width }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const InlineCheckbox = props => <Checkbox {...props} style={{ width: 32 }} />;
|
||||
|
||||
type Props = {|
|
||||
name: string,
|
||||
variable: gdVariable,
|
||||
depth: number,
|
||||
errorText?: ?string,
|
||||
onBlur: () => void,
|
||||
onRemove: () => void,
|
||||
onAddChild: () => void,
|
||||
onChangeValue: string => void,
|
||||
children?: React.Node,
|
||||
muiTheme: Object,
|
||||
showHandle: boolean,
|
||||
showSelectionCheckbox: boolean,
|
||||
isSelected: boolean,
|
||||
onSelect: boolean => void,
|
||||
|};
|
||||
|
||||
const ThemableVariableRow = ({
|
||||
name,
|
||||
variable,
|
||||
@@ -27,7 +47,11 @@ const ThemableVariableRow = ({
|
||||
onChangeValue,
|
||||
children,
|
||||
muiTheme,
|
||||
}) => {
|
||||
showHandle,
|
||||
showSelectionCheckbox,
|
||||
isSelected,
|
||||
onSelect,
|
||||
}: Props) => {
|
||||
const isStructure = variable.isStructure();
|
||||
const key = '' + depth + name;
|
||||
|
||||
@@ -36,7 +60,13 @@ const ThemableVariableRow = ({
|
||||
{depth > 0 && (
|
||||
<Indent width={(depth + 1) * styles.tableChildIndentation} />
|
||||
)}
|
||||
{depth === 0 && <DragHandle />}
|
||||
{depth === 0 && showHandle && <DragHandle />}
|
||||
{showSelectionCheckbox && (
|
||||
<InlineCheckbox
|
||||
checked={isSelected}
|
||||
onCheck={(e, checked) => onSelect(checked)}
|
||||
/>
|
||||
)}
|
||||
<TextField
|
||||
fullWidth
|
||||
name={key + 'name'}
|
||||
@@ -64,9 +94,6 @@ const ThemableVariableRow = ({
|
||||
}
|
||||
columns.push(
|
||||
<TreeTableCell key="tools" style={styles.toolColumn}>
|
||||
<IconButton onClick={onRemove}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<IconButton onClick={onAddChild}>
|
||||
<AddCircle />
|
||||
</IconButton>
|
||||
|
@@ -1,11 +1,15 @@
|
||||
// @flow
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableHeaderColumn,
|
||||
TableRow,
|
||||
TableRowColumn,
|
||||
} from 'material-ui/Table';
|
||||
import IconButton from 'material-ui/IconButton';
|
||||
import ContentCopy from 'material-ui/svg-icons/content/content-copy';
|
||||
import ContentPaste from 'material-ui/svg-icons/content/content-paste';
|
||||
import Delete from 'material-ui/svg-icons/action/delete';
|
||||
import flatten from 'lodash/flatten';
|
||||
import { SortableContainer, SortableElement } from 'react-sortable-hoc';
|
||||
import { mapFor } from '../Utils/MapFor';
|
||||
@@ -14,12 +18,21 @@ import newNameGenerator from '../Utils/NewNameGenerator';
|
||||
import VariableRow from './VariableRow';
|
||||
import AddVariableRow from './AddVariableRow';
|
||||
import styles from './styles';
|
||||
import {
|
||||
getInitialSelection,
|
||||
hasSelection,
|
||||
addToSelection,
|
||||
getSelection,
|
||||
} from '../Utils/SelectionHandler';
|
||||
import { CLIPBOARD_KIND } from './ClipboardKind';
|
||||
import Clipboard from '../Utils/Clipboard';
|
||||
import { serializeToJSObject, unserializeFromJSObject } from '../Utils/Serializer';
|
||||
const gd = global.gd;
|
||||
|
||||
const SortableVariableRow = SortableElement(VariableRow);
|
||||
const SortableAddVariableRow = SortableElement(AddVariableRow);
|
||||
|
||||
class VariablesListBody extends Component {
|
||||
class VariablesListBody extends Component<*, *> {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
@@ -28,22 +41,97 @@ class VariablesListBody extends Component {
|
||||
const SortableVariablesListBody = SortableContainer(VariablesListBody);
|
||||
SortableVariablesListBody.muiName = 'TableBody';
|
||||
|
||||
export default class VariablesList extends Component {
|
||||
constructor() {
|
||||
super();
|
||||
type VariableAndName = {| name: string, ptr: number, variable: gdVariable |};
|
||||
|
||||
this.state = {
|
||||
nameErrors: {},
|
||||
};
|
||||
}
|
||||
type Props = {|
|
||||
variablesContainer: gdVariablesContainer,
|
||||
emptyExplanationMessage?: string,
|
||||
emptyExplanationSecondMessage?: string,
|
||||
|};
|
||||
type State = {|
|
||||
nameErrors: { [string]: string },
|
||||
selectedVariables: { [number]: ?VariableAndName },
|
||||
mode: 'select' | 'move',
|
||||
|};
|
||||
|
||||
_renderVariableChildren(name, parentVariable, depth) {
|
||||
const children = parentVariable.getAllChildren();
|
||||
const names = children.keys().toJSArray();
|
||||
export default class VariablesList extends Component<Props, State> {
|
||||
state = {
|
||||
nameErrors: {},
|
||||
selectedVariables: getInitialSelection(),
|
||||
mode: 'select',
|
||||
};
|
||||
|
||||
_selectVariable = (variableAndName: VariableAndName, select: boolean) => {
|
||||
this.setState({
|
||||
selectedVariables: addToSelection(
|
||||
this.state.selectedVariables,
|
||||
variableAndName,
|
||||
select
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
copySelection = () => {
|
||||
Clipboard.set(
|
||||
CLIPBOARD_KIND,
|
||||
getSelection(this.state.selectedVariables).map(({ name, variable }) => ({
|
||||
name,
|
||||
serializedVariable: serializeToJSObject(variable),
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
paste = () => {
|
||||
const { variablesContainer } = this.props;
|
||||
if (!Clipboard.has(CLIPBOARD_KIND)) return;
|
||||
|
||||
const variables = Clipboard.get(CLIPBOARD_KIND);
|
||||
variables.forEach(({ name, serializedVariable }) => {
|
||||
const newName = newNameGenerator(name, (name) => variablesContainer.has(name), 'CopyOf');
|
||||
const newVariable = new gd.Variable();
|
||||
unserializeFromJSObject(newVariable, serializedVariable);
|
||||
variablesContainer.insert(newName, newVariable, variablesContainer.count())
|
||||
newVariable.delete();
|
||||
});
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
deleteSelection = () => {
|
||||
const { variablesContainer } = this.props;
|
||||
const selection: Array<VariableAndName> = getSelection(
|
||||
this.state.selectedVariables
|
||||
);
|
||||
|
||||
// Only delete ancestor variables, as selection can be composed of variables
|
||||
// that are contained inside others.
|
||||
const ancestorOnlyVariables = selection.filter(({ variable }) => {
|
||||
return selection.filter(
|
||||
otherVariableAndName =>
|
||||
variable !== otherVariableAndName &&
|
||||
otherVariableAndName.variable.contains(variable)
|
||||
);
|
||||
});
|
||||
|
||||
// We don't want to ever manipulate/access to variables that have been deleted (by removeRecursively):
|
||||
// that's why it's important to only delete ancestor variables.
|
||||
ancestorOnlyVariables.forEach(({ variable }: VariableAndName) =>
|
||||
variablesContainer.removeRecursively(variable)
|
||||
);
|
||||
this.setState({
|
||||
selectedVariables: getInitialSelection(),
|
||||
});
|
||||
};
|
||||
|
||||
_renderVariableChildren(
|
||||
name: string,
|
||||
parentVariable: gdVariable,
|
||||
depth: number
|
||||
) {
|
||||
const names = parentVariable.getAllChildrenNames().toJSArray();
|
||||
|
||||
return flatten(
|
||||
names.map((name, index) => {
|
||||
const variable = children.get(name);
|
||||
const variable = parentVariable.getChild(name);
|
||||
return this._renderVariableAndChildrenRows(
|
||||
name,
|
||||
variable,
|
||||
@@ -55,7 +143,13 @@ export default class VariablesList extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_renderVariableAndChildrenRows(name, variable, depth, index, parentVariable) {
|
||||
_renderVariableAndChildrenRows(
|
||||
name: string,
|
||||
variable: gdVariable,
|
||||
depth: number,
|
||||
index: number,
|
||||
parentVariable: ?gdVariable
|
||||
) {
|
||||
const { variablesContainer } = this.props;
|
||||
const isStructure = variable.isStructure();
|
||||
|
||||
@@ -119,6 +213,11 @@ export default class VariablesList extends Component {
|
||||
? this._renderVariableChildren(name, variable, depth)
|
||||
: null
|
||||
}
|
||||
showHandle={this.state.mode === 'move'}
|
||||
showSelectionCheckbox={this.state.mode === 'select'}
|
||||
isSelected={!!this.state.selectedVariables[variable.ptr]}
|
||||
onSelect={select =>
|
||||
this._selectVariable({ name, ptr: variable.ptr, variable }, select)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -150,9 +249,8 @@ export default class VariablesList extends Component {
|
||||
0,
|
||||
variablesContainer.count(),
|
||||
index => {
|
||||
const nameAndVariable = variablesContainer.getAt(index);
|
||||
const variable = nameAndVariable.getVariable();
|
||||
const name = nameAndVariable.getName();
|
||||
const variable = variablesContainer.getAt(index);
|
||||
const name = variablesContainer.getNameAt(index);
|
||||
|
||||
return this._renderVariableAndChildrenRows(
|
||||
name,
|
||||
@@ -190,7 +288,26 @@ export default class VariablesList extends Component {
|
||||
<TableRow>
|
||||
<TableHeaderColumn>Name</TableHeaderColumn>
|
||||
<TableHeaderColumn>Value</TableHeaderColumn>
|
||||
<TableRowColumn />
|
||||
<TableHeaderColumn style={styles.toolColumnHeader}>
|
||||
<IconButton
|
||||
onClick={this.copySelection}
|
||||
disabled={!hasSelection(this.state.selectedVariables)}
|
||||
>
|
||||
<ContentCopy />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={this.paste}
|
||||
disabled={!Clipboard.has(CLIPBOARD_KIND)}
|
||||
>
|
||||
<ContentPaste />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={this.deleteSelection}
|
||||
disabled={!hasSelection(this.state.selectedVariables)}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</TableHeaderColumn>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
</Table>
|
||||
|
@@ -1,6 +1,10 @@
|
||||
export default {
|
||||
toolColumnHeader: {
|
||||
textAlign: 'right',
|
||||
paddingRight: 8,
|
||||
},
|
||||
toolColumn: {
|
||||
minWidth: 72,
|
||||
minWidth: 48,
|
||||
flex: 0,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
|
@@ -23,6 +23,7 @@ import TiledSpriteEditor from '../ObjectEditor/Editors/TiledSpriteEditor';
|
||||
import PanelSpriteEditor from '../ObjectEditor/Editors/PanelSpriteEditor';
|
||||
import SpriteEditor from '../ObjectEditor/Editors/SpriteEditor';
|
||||
import PointsEditor from '../ObjectEditor/Editors/SpriteEditor/PointsEditor';
|
||||
import CollisionMasksEditor from '../ObjectEditor/Editors/SpriteEditor/CollisionMasksEditor';
|
||||
import EmptyEditor from '../ObjectEditor/Editors/EmptyEditor';
|
||||
import ImageThumbnail from '../ObjectEditor/ImageThumbnail';
|
||||
import ShapePainterEditor from '../ObjectEditor/Editors/ShapePainterEditor';
|
||||
@@ -424,6 +425,15 @@ storiesOf('SpriteEditor and related editors', module)
|
||||
resourcesLoader={ResourcesLoader}
|
||||
/>
|
||||
</SerializedObjectDisplay>
|
||||
))
|
||||
.add('CollisionMasksEditor', () => (
|
||||
<SerializedObjectDisplay object={spriteObject}>
|
||||
<CollisionMasksEditor
|
||||
object={spriteObject}
|
||||
project={project}
|
||||
resourcesLoader={ResourcesLoader}
|
||||
/>
|
||||
</SerializedObjectDisplay>
|
||||
));
|
||||
|
||||
storiesOf('ShapePainterEditor', module)
|
||||
|
2
newIDE/electron-app/app/package-lock.json
generated
2
newIDE/electron-app/app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gdevelop",
|
||||
"version": "5.0.0-beta25",
|
||||
"version": "5.0.0-beta28",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "gdevelop",
|
||||
"productName": "GDevelop 5",
|
||||
"description": "GDevelop 5 IDE running on the Electron runtime",
|
||||
"version": "5.0.0-beta27",
|
||||
"version": "5.0.0-beta28",
|
||||
"author": "Florian Rival",
|
||||
"license": "MIT",
|
||||
"homepage": "http://gdevelop-app.com",
|
||||
|
@@ -23,8 +23,8 @@
|
||||
"7zip-bin-win" "^2.1.1"
|
||||
|
||||
"@types/node@^7.0.18":
|
||||
version "7.0.48"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.48.tgz#24bfdc0aa82e8f6dbd017159c58094a2e06d0abb"
|
||||
version "7.0.58"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.58.tgz#ae852120137f40a29731a559e48003bd2d5d19f7"
|
||||
|
||||
ajv-keywords@^2.1.1:
|
||||
version "2.1.1"
|
||||
|
Reference in New Issue
Block a user